indentation
[oweals/gnunet.git] / src / fs / fs_uri.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file fs/fs_uri.c
23  * @brief Parses and produces uri strings.
24  * @author Igor Wronsky, Christian Grothoff
25  *
26  * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
27  * The specific structure of "IDENTIFIER" depends on the module and
28  * maybe differenciated into additional subcategories if applicable.
29  * This module only deals with fs identifiers (MODULE = "fs").
30  * <p>
31  *
32  * This module only parses URIs for the AFS module.  The FS URIs fall
33  * into four categories, "chk", "sks", "ksk" and "loc".  The first three
34  * categories were named in analogy (!) to Freenet, but they do NOT
35  * work in exactly the same way.  They are very similar from the user's
36  * point of view (unique file identifier, subspace, keyword), but the
37  * implementation is rather different in pretty much every detail.
38  * The concrete URI formats are:
39  *
40  * <ul><li>
41  *
42  * First, there are URIs that identify a file.  They have the format
43  * "gnunet://fs/chk/HEX1.HEX2.SIZE".  These URIs can be used to
44  * download the file.  The description, filename, mime-type and other
45  * meta-data is NOT part of the file-URI since a URI uniquely
46  * identifies a resource (and the contents of the file would be the
47  * same even if it had a different description).
48  *
49  * </li><li>
50  *
51  * The second category identifies entries in a namespace.  The format
52  * is "gnunet://fs/sks/NAMESPACE/IDENTIFIER" where the namespace
53  * should be given in HEX.  Applications may allow using a nickname
54  * for the namespace if the nickname is not ambiguous.  The identifier
55  * can be either an ASCII sequence or a HEX-encoding.  If the
56  * identifier is in ASCII but the format is ambiguous and could denote
57  * a HEX-string a "/" is appended to indicate ASCII encoding.
58  *
59  * </li> <li>
60  *
61  * The third category identifies ordinary searches.  The format is
62  * "gnunet://fs/ksk/KEYWORD[+KEYWORD]*".  Using the "+" syntax
63  * it is possible to encode searches with the boolean "AND" operator.
64  * "+" is used since it indicates a commutative 'and' operation and
65  * is unlikely to be used in a keyword by itself.
66  *
67  * </li><li>
68  *
69  * The last category identifies a datum on a specific machine.  The
70  * format is "gnunet://fs/loc/HEX1.HEX2.SIZE.PEER.SIG.EXPTIME".  PEER is
71  * the BinName of the public key of the peer storing the datum.  The
72  * signature (SIG) certifies that this peer has this content.
73  * HEX1, HEX2 and SIZE correspond to a 'chk' URI.
74  *
75  * </li></ul>
76  *
77  * The encoding for hexadecimal values is defined in the hashing.c
78  * module in the gnunetutil library and discussed there.
79  * <p>
80  */
81 #include "platform.h"
82 #include "gnunet_fs_service.h"
83 #include "gnunet_signatures.h"
84 #include "fs.h"
85
86
87 /**
88  * Get a unique key from a URI.  This is for putting URIs
89  * into HashMaps.  The key may change between FS implementations.
90  *
91  * @param uri uri to convert to a unique key
92  * @param key wherer to store the unique key
93  */
94 void
95 GNUNET_FS_uri_to_key (const struct GNUNET_FS_Uri *uri, GNUNET_HashCode * key)
96 {
97   switch (uri->type)
98   {
99   case chk:
100     *key = uri->data.chk.chk.query;
101     return;
102   case sks:
103     GNUNET_CRYPTO_hash (uri->data.sks.identifier,
104                         strlen (uri->data.sks.identifier), key);
105     break;
106   case ksk:
107     if (uri->data.ksk.keywordCount > 0)
108       GNUNET_CRYPTO_hash (uri->data.ksk.keywords[0],
109                           strlen (uri->data.ksk.keywords[0]), key);
110     break;
111   case loc:
112     GNUNET_CRYPTO_hash (&uri->data.loc.fi,
113                         sizeof (struct FileIdentifier) +
114                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
115                         key);
116     break;
117   default:
118     memset (key, 0, sizeof (GNUNET_HashCode));
119     break;
120   }
121 }
122
123
124 /**
125  * Convert keyword URI to a human readable format
126  * (i.e. the search query that was used in the first place)
127  *
128  * @param uri ksk uri to convert to a string 
129  * @return string with the keywords
130  */
131 char *
132 GNUNET_FS_uri_ksk_to_string_fancy (const struct GNUNET_FS_Uri *uri)
133 {
134   size_t n;
135   char *ret;
136   unsigned int i;
137   const char *keyword;
138   char **keywords;
139   unsigned int keywordCount;
140
141   if ((uri == NULL) || (uri->type != ksk))
142   {
143     GNUNET_break (0);
144     return NULL;
145   }
146   keywords = uri->data.ksk.keywords;
147   keywordCount = uri->data.ksk.keywordCount;
148   n = keywordCount + 1;
149   for (i = 0; i < keywordCount; i++)
150   {
151     keyword = keywords[i];
152     n += strlen (keyword) - 1;
153     if (NULL != strstr (&keyword[1], " "))
154       n += 2;
155     if (keyword[0] == '+')
156       n++;
157   }
158   ret = GNUNET_malloc (n);
159   strcpy (ret, "");
160   for (i = 0; i < keywordCount; i++)
161   {
162     keyword = keywords[i];
163     if (NULL != strstr (&keyword[1], " "))
164     {
165       strcat (ret, "\"");
166       if (keyword[0] == '+')
167         strcat (ret, keyword);
168       else
169         strcat (ret, &keyword[1]);
170       strcat (ret, "\"");
171     }
172     else
173     {
174       if (keyword[0] == '+')
175         strcat (ret, keyword);
176       else
177         strcat (ret, &keyword[1]);
178     }
179     strcat (ret, " ");
180   }
181   return ret;
182 }
183
184
185 /**
186  * Given a keyword with %-encoding (and possibly quotes to protect
187  * spaces), return a copy of the keyword without %-encoding and
188  * without double-quotes (%22).  Also, add a space at the beginning
189  * if there is not a '+'.
190  * 
191  * @param in string with %-encoding
192  * @param emsg where to store the parser error message (if any)
193  * @return decodded string with leading space (or preserved plus)
194  */
195 static char *
196 percent_decode_keyword (const char *in, char **emsg)
197 {
198   char *out;
199   char *ret;
200   unsigned int rpos;
201   unsigned int wpos;
202   unsigned int hx;
203
204   out = GNUNET_strdup (in);
205   rpos = 0;
206   wpos = 0;
207   while (out[rpos] != '\0')
208   {
209     if (out[rpos] == '%')
210     {
211       if (1 != sscanf (&out[rpos + 1], "%2X", &hx))
212       {
213         GNUNET_free (out);
214         *emsg = GNUNET_strdup (_("`%' must be followed by HEX number"));
215         return NULL;
216       }
217       rpos += 3;
218       if (hx == '"')
219         continue;               /* skip double quote */
220       out[wpos++] = (char) hx;
221     }
222     else
223     {
224       out[wpos++] = out[rpos++];
225     }
226   }
227   out[wpos] = '\0';
228   if (out[0] == '+')
229   {
230     ret = GNUNET_strdup (out);
231   }
232   else
233   {
234     /* need to prefix with space */
235     ret = GNUNET_malloc (strlen (out) + 2);
236     strcpy (ret, " ");
237     strcat (ret, out);
238   }
239   GNUNET_free (out);
240   return ret;
241 }
242
243 #define GNUNET_FS_URI_KSK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_KSK_INFIX
244
245 /**
246  * Parse a KSK URI.
247  *
248  * @param s an uri string
249  * @param emsg where to store the parser error message (if any)
250  * @return NULL on error, otherwise the KSK URI
251  */
252 static struct GNUNET_FS_Uri *
253 uri_ksk_parse (const char *s, char **emsg)
254 {
255   struct GNUNET_FS_Uri *ret;
256   char **keywords;
257   unsigned int pos;
258   int max;
259   int iret;
260   int i;
261   size_t slen;
262   char *dup;
263   int saw_quote;
264
265   GNUNET_assert (s != NULL);
266   slen = strlen (s);
267   pos = strlen (GNUNET_FS_URI_KSK_PREFIX);
268   if ((slen <= pos) || (0 != strncmp (s, GNUNET_FS_URI_KSK_PREFIX, pos)))
269     return NULL;                /* not KSK URI */
270   if ((s[slen - 1] == '+') || (s[pos] == '+'))
271   {
272     *emsg =
273         GNUNET_strdup (_("Malformed KSK URI (must not begin or end with `+')"));
274     return NULL;
275   }
276   max = 1;
277   saw_quote = 0;
278   for (i = pos; i < slen; i++)
279   {
280     if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22")))
281     {
282       saw_quote = (saw_quote + 1) % 2;
283       i += 3;
284       continue;
285     }
286     if ((s[i] == '+') && (saw_quote == 0))
287     {
288       max++;
289       if (s[i - 1] == '+')
290       {
291         *emsg = GNUNET_strdup (_("`++' not allowed in KSK URI"));
292         return NULL;
293       }
294     }
295   }
296   if (saw_quote == 1)
297   {
298     *emsg = GNUNET_strdup (_("Quotes not balanced in KSK URI"));
299     return NULL;
300   }
301   iret = max;
302   dup = GNUNET_strdup (s);
303   keywords = GNUNET_malloc (max * sizeof (char *));
304   for (i = slen - 1; i >= pos; i--)
305   {
306     if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22")))
307     {
308       saw_quote = (saw_quote + 1) % 2;
309       i += 3;
310       continue;
311     }
312     if ((dup[i] == '+') && (saw_quote == 0))
313     {
314       keywords[--max] = percent_decode_keyword (&dup[i + 1], emsg);
315       if (NULL == keywords[max])
316         goto CLEANUP;
317       dup[i] = '\0';
318     }
319   }
320   keywords[--max] = percent_decode_keyword (&dup[pos], emsg);
321   if (NULL == keywords[max])
322     goto CLEANUP;
323   GNUNET_assert (max == 0);
324   GNUNET_free (dup);
325   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
326   ret->type = ksk;
327   ret->data.ksk.keywordCount = iret;
328   ret->data.ksk.keywords = keywords;
329   return ret;
330 CLEANUP:
331   for (i = 0; i < max; i++)
332     GNUNET_free_non_null (keywords[i]);
333   GNUNET_free (keywords);
334   GNUNET_free (dup);
335   return NULL;
336 }
337
338
339 #define GNUNET_FS_URI_SKS_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_SKS_INFIX
340
341 /**
342  * Parse an SKS URI.
343  *
344  * @param s an uri string
345  * @param emsg where to store the parser error message (if any)
346  * @return NULL on error, SKS URI otherwise
347  */
348 static struct GNUNET_FS_Uri *
349 uri_sks_parse (const char *s, char **emsg)
350 {
351   struct GNUNET_FS_Uri *ret;
352   GNUNET_HashCode namespace;
353   char *identifier;
354   unsigned int pos;
355   size_t slen;
356   char enc[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
357
358   GNUNET_assert (s != NULL);
359   slen = strlen (s);
360   pos = strlen (GNUNET_FS_URI_SKS_PREFIX);
361   if ((slen <= pos) || (0 != strncmp (s, GNUNET_FS_URI_SKS_PREFIX, pos)))
362     return NULL;                /* not an SKS URI */
363   if ((slen < pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) ||
364       (s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '/'))
365   {
366     *emsg = GNUNET_strdup (_("Malformed SKS URI"));
367     return NULL;
368   }
369   memcpy (enc, &s[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
370   enc[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
371   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (enc, &namespace))
372   {
373     *emsg = GNUNET_strdup (_("Malformed SKS URI"));
374     return NULL;
375   }
376   identifier =
377       GNUNET_strdup (&s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)]);
378   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
379   ret->type = sks;
380   ret->data.sks.namespace = namespace;
381   ret->data.sks.identifier = identifier;
382   return ret;
383 }
384
385 #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
386
387
388 /**
389  * Parse a CHK URI.
390  *
391  * @param s an uri string
392  * @param emsg where to store the parser error message (if any)
393  * @return NULL on error, CHK URI otherwise
394  */
395 static struct GNUNET_FS_Uri *
396 uri_chk_parse (const char *s, char **emsg)
397 {
398   struct GNUNET_FS_Uri *ret;
399   struct FileIdentifier fi;
400   unsigned int pos;
401   unsigned long long flen;
402   size_t slen;
403   char h1[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
404   char h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
405
406   if (NULL == s)
407     return NULL;
408   GNUNET_assert (s != NULL);
409   slen = strlen (s);
410   pos = strlen (GNUNET_FS_URI_CHK_PREFIX);
411   if ((slen < pos + 2 * sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) ||
412       (0 != strncmp (s, GNUNET_FS_URI_CHK_PREFIX, pos)))
413     return NULL;                /* not a CHK URI */
414   if ((s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') ||
415       (s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.'))
416   {
417     *emsg = GNUNET_strdup (_("Malformed CHK URI"));
418     return NULL;
419   }
420   memcpy (h1, &s[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
421   h1[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
422   memcpy (h2,
423           &s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)],
424           sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
425   h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
426
427   if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1,
428                                                     &fi.chk.key)) ||
429       (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2,
430                                                     &fi.chk.query)) ||
431       (1 !=
432        SSCANF (&s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
433                "%llu", &flen)))
434   {
435     *emsg = GNUNET_strdup (_("Malformed CHK URI"));
436     return NULL;
437   }
438   fi.file_length = GNUNET_htonll (flen);
439   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
440   ret->type = chk;
441   ret->data.chk = fi;
442   return ret;
443 }
444
445
446 /**
447  * Convert a character back to the binary value
448  * that it represents (given base64-encoding).
449  *
450  * @param a character to convert
451  * @return offset in the "tbl" array
452  */
453 static unsigned int
454 c2v (unsigned char a)
455 {
456   if ((a >= '0') && (a <= '9'))
457     return a - '0';
458   if ((a >= 'A') && (a <= 'Z'))
459     return (a - 'A' + 10);
460   if ((a >= 'a') && (a <= 'z'))
461     return (a - 'a' + 36);
462   if (a == '_')
463     return 62;
464   if (a == '=')
465     return 63;
466   return -1;
467 }
468
469
470 /**
471  * Convert string back to binary data.
472  *
473  * @param input '\\0'-terminated string
474  * @param data where to write binary data
475  * @param size how much data should be converted
476  * @return number of characters processed from input,
477  *        -1 on error
478  */
479 static int
480 enc2bin (const char *input, void *data, size_t size)
481 {
482   size_t len;
483   size_t pos;
484   unsigned int bits;
485   unsigned int hbits;
486
487   len = size * 8 / 6;
488   if (((size * 8) % 6) != 0)
489     len++;
490   if (strlen (input) < len)
491     return -1;                  /* error! */
492   bits = 0;
493   hbits = 0;
494   len = 0;
495   for (pos = 0; pos < size; pos++)
496   {
497     while (hbits < 8)
498     {
499       bits |= (c2v (input[len++]) << hbits);
500       hbits += 6;
501     }
502     (((unsigned char *) data)[pos]) = (unsigned char) bits;
503     bits >>= 8;
504     hbits -= 8;
505   }
506   return len;
507 }
508
509
510 /**
511  * Structure that defines how the
512  * contents of a location URI must be
513  * assembled in memory to create or
514  * verify the signature of a location
515  * URI.
516  */
517 struct LocUriAssembly
518 {
519   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
520
521   struct GNUNET_TIME_AbsoluteNBO exptime;
522
523   struct FileIdentifier fi;
524
525   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded peer;
526
527 };
528
529
530 #define GNUNET_FS_URI_LOC_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_LOC_INFIX
531
532 /**
533  * Parse a LOC URI.
534  * Also verifies validity of the location URI.
535  *
536  * @param s an uri string
537  * @param emsg where to store the parser error message (if any)
538  * @return NULL on error, valid LOC URI otherwise
539  */
540 static struct GNUNET_FS_Uri *
541 uri_loc_parse (const char *s, char **emsg)
542 {
543   struct GNUNET_FS_Uri *uri;
544   char h1[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
545   char h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
546   unsigned int pos;
547   unsigned int npos;
548   unsigned long long exptime;
549   unsigned long long flen;
550   struct GNUNET_TIME_Absolute et;
551   struct GNUNET_CRYPTO_RsaSignature sig;
552   struct LocUriAssembly ass;
553   int ret;
554   size_t slen;
555
556   GNUNET_assert (s != NULL);
557   slen = strlen (s);
558   pos = strlen (GNUNET_FS_URI_LOC_PREFIX);
559   if ((slen < pos + 2 * sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) ||
560       (0 != strncmp (s, GNUNET_FS_URI_LOC_PREFIX, pos)))
561     return NULL;                /* not an SKS URI */
562   if ((s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') ||
563       (s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.'))
564   {
565     *emsg = GNUNET_strdup (_("SKS URI malformed"));
566     return NULL;
567   }
568   memcpy (h1, &s[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
569   h1[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
570   memcpy (h2,
571           &s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)],
572           sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
573   h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
574
575   if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1,
576                                                     &ass.fi.chk.key)) ||
577       (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2,
578                                                     &ass.fi.chk.query)) ||
579       (1 !=
580        SSCANF (&s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
581                "%llu", &flen)))
582   {
583     *emsg = GNUNET_strdup (_("SKS URI malformed"));
584     return NULL;
585   }
586   ass.fi.file_length = GNUNET_htonll (flen);
587
588   npos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2;
589   while ((s[npos] != '\0') && (s[npos] != '.'))
590     npos++;
591   if (s[npos] == '\0')
592   {
593     *emsg = GNUNET_strdup (_("SKS URI malformed"));
594     goto ERR;
595   }
596   npos++;
597   ret = enc2bin (&s[npos],
598                  &ass.peer,
599                  sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
600   if (ret == -1)
601   {
602     *emsg =
603         GNUNET_strdup (_("SKS URI malformed (could not decode public key)"));
604     goto ERR;
605   }
606   npos += ret;
607   if (s[npos++] != '.')
608   {
609     *emsg = GNUNET_strdup (_("SKS URI malformed (could not find signature)"));
610     goto ERR;
611   }
612   ret = enc2bin (&s[npos], &sig, sizeof (struct GNUNET_CRYPTO_RsaSignature));
613   if (ret == -1)
614   {
615     *emsg = GNUNET_strdup (_("SKS URI malformed (could not decode signature)"));
616     goto ERR;
617   }
618   npos += ret;
619   if (s[npos++] != '.')
620   {
621     *emsg = GNUNET_strdup (_("SKS URI malformed"));
622     goto ERR;
623   }
624   if (1 != SSCANF (&s[npos], "%llu", &exptime))
625   {
626     *emsg =
627         GNUNET_strdup (_
628                        ("SKS URI malformed (could not parse expiration time)"));
629     goto ERR;
630   }
631   ass.purpose.size = htonl (sizeof (struct LocUriAssembly));
632   ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
633   et.abs_value = exptime;
634   ass.exptime = GNUNET_TIME_absolute_hton (et);
635   if (GNUNET_OK !=
636       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT,
637                                 &ass.purpose, &sig, &ass.peer))
638   {
639     *emsg =
640         GNUNET_strdup (_("SKS URI malformed (signature failed validation)"));
641     goto ERR;
642   }
643   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
644   uri->type = loc;
645   uri->data.loc.fi = ass.fi;
646   uri->data.loc.peer = ass.peer;
647   uri->data.loc.expirationTime = et;
648   uri->data.loc.contentSignature = sig;
649
650   return uri;
651 ERR:
652   return NULL;
653 }
654
655
656 /**
657  * Convert a UTF-8 String to a URI.
658  *
659  * @param uri string to parse
660  * @param emsg where to store the parser error message (if any)
661  * @return NULL on error
662  */
663 struct GNUNET_FS_Uri *
664 GNUNET_FS_uri_parse (const char *uri, char **emsg)
665 {
666   struct GNUNET_FS_Uri *ret;
667   char *msg;
668
669   if (NULL == emsg)
670     emsg = &msg;
671   *emsg = NULL;
672   if ((NULL != (ret = uri_chk_parse (uri, emsg))) ||
673       (NULL != (ret = uri_ksk_parse (uri, emsg))) ||
674       (NULL != (ret = uri_sks_parse (uri, emsg))) ||
675       (NULL != (ret = uri_loc_parse (uri, emsg))))
676     return ret;
677   if (NULL == *emsg)
678     *emsg = GNUNET_strdup (_("Unrecognized URI type"));
679   if (emsg == &msg)
680     GNUNET_free (msg);
681   return NULL;
682 }
683
684
685 /**
686  * Free URI.
687  *
688  * @param uri uri to free
689  */
690 void
691 GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri)
692 {
693   unsigned int i;
694
695   GNUNET_assert (uri != NULL);
696   switch (uri->type)
697   {
698   case ksk:
699     for (i = 0; i < uri->data.ksk.keywordCount; i++)
700       GNUNET_free (uri->data.ksk.keywords[i]);
701     GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, 0);
702     break;
703   case sks:
704     GNUNET_free (uri->data.sks.identifier);
705     break;
706   case loc:
707     break;
708   default:
709     /* do nothing */
710     break;
711   }
712   GNUNET_free (uri);
713 }
714
715 /**
716  * How many keywords are ANDed in this keyword URI?
717  *
718  * @param uri ksk uri to get the number of keywords from
719  * @return 0 if this is not a keyword URI
720  */
721 unsigned int
722 GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri)
723 {
724   if (uri->type != ksk)
725     return 0;
726   return uri->data.ksk.keywordCount;
727 }
728
729
730 /**
731  * Iterate over all keywords in this keyword URI.
732  *
733  * @param uri ksk uri to get the keywords from
734  * @param iterator function to call on each keyword
735  * @param iterator_cls closure for iterator
736  * @return -1 if this is not a keyword URI, otherwise number of
737  *   keywords iterated over until iterator aborted
738  */
739 int
740 GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri,
741                                 GNUNET_FS_KeywordIterator iterator,
742                                 void *iterator_cls)
743 {
744   unsigned int i;
745   char *keyword;
746
747   if (uri->type != ksk)
748     return -1;
749   if (iterator == NULL)
750     return uri->data.ksk.keywordCount;
751   for (i = 0; i < uri->data.ksk.keywordCount; i++)
752   {
753     keyword = uri->data.ksk.keywords[i];
754     /* first character of keyword indicates
755      * if it is mandatory or not */
756     if (GNUNET_OK != iterator (iterator_cls, &keyword[1], keyword[0] == '+'))
757       return i;
758   }
759   return i;
760 }
761
762
763 /**
764  * Add the given keyword to the set of keywords represented by the URI.
765  * Does nothing if the keyword is already present.
766  *
767  * @param uri ksk uri to modify
768  * @param keyword keyword to add
769  * @param is_mandatory is this keyword mandatory?
770  */
771 void
772 GNUNET_FS_uri_ksk_add_keyword (struct GNUNET_FS_Uri *uri,
773                                const char *keyword, int is_mandatory)
774 {
775   unsigned int i;
776   const char *old;
777   char *n;
778
779   GNUNET_assert (uri->type == ksk);
780   for (i = 0; i < uri->data.ksk.keywordCount; i++)
781   {
782     old = uri->data.ksk.keywords[i];
783     if (0 == strcmp (&old[1], keyword))
784       return;
785   }
786   GNUNET_asprintf (&n, is_mandatory ? "+%s" : " %s", keyword);
787   GNUNET_array_append (uri->data.ksk.keywords, uri->data.ksk.keywordCount, n);
788 }
789
790
791 /**
792  * Remove the given keyword from the set of keywords represented by the URI.
793  * Does nothing if the keyword is not present.
794  *
795  * @param uri ksk uri to modify
796  * @param keyword keyword to add
797  */
798 void
799 GNUNET_FS_uri_ksk_remove_keyword (struct GNUNET_FS_Uri *uri,
800                                   const char *keyword)
801 {
802   unsigned int i;
803   char *old;
804
805   GNUNET_assert (uri->type == ksk);
806   for (i = 0; i < uri->data.ksk.keywordCount; i++)
807   {
808     old = uri->data.ksk.keywords[i];
809     if (0 == strcmp (&old[1], keyword))
810     {
811       uri->data.ksk.keywords[i] =
812           uri->data.ksk.keywords[uri->data.ksk.keywordCount - 1];
813       GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount,
814                          uri->data.ksk.keywordCount - 1);
815       GNUNET_free (old);
816       return;
817     }
818   }
819 }
820
821
822 /**
823  * Obtain the identity of the peer offering the data
824  *
825  * @param uri the location URI to inspect
826  * @param peer where to store the identify of the peer (presumably) offering the content
827  * @return GNUNET_SYSERR if this is not a location URI, otherwise GNUNET_OK
828  */
829 int
830 GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri,
831                                      struct GNUNET_PeerIdentity *peer)
832 {
833   if (uri->type != loc)
834     return GNUNET_SYSERR;
835   GNUNET_CRYPTO_hash (&uri->data.loc.peer,
836                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
837                       &peer->hashPubKey);
838   return GNUNET_OK;
839 }
840
841
842 /**
843  * Obtain the expiration of the LOC URI.
844  *
845  * @param uri location URI to get the expiration from
846  * @return expiration time of the URI
847  */
848 struct GNUNET_TIME_Absolute
849 GNUNET_FS_uri_loc_get_expiration (const struct GNUNET_FS_Uri *uri)
850 {
851   GNUNET_assert (uri->type == loc);
852   return uri->data.loc.expirationTime;
853 }
854
855
856
857 /**
858  * Obtain the URI of the content itself.
859  *
860  * @param uri location URI to get the content URI from
861  * @return NULL if argument is not a location URI
862  */
863 struct GNUNET_FS_Uri *
864 GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri)
865 {
866   struct GNUNET_FS_Uri *ret;
867
868   if (uri->type != loc)
869     return NULL;
870   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
871   ret->type = chk;
872   ret->data.chk = uri->data.loc.fi;
873   return ret;
874 }
875
876
877 /**
878  * Construct a location URI (this peer will be used for the location).
879  *
880  * @param baseUri content offered by the sender
881  * @param cfg configuration information (used to find our hostkey)
882  * @param expiration_time how long will the content be offered?
883  * @return the location URI, NULL on error
884  */
885 struct GNUNET_FS_Uri *
886 GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *baseUri,
887                           const struct GNUNET_CONFIGURATION_Handle *cfg,
888                           struct GNUNET_TIME_Absolute expiration_time)
889 {
890   struct GNUNET_FS_Uri *uri;
891   struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
892   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
893   char *keyfile;
894   struct LocUriAssembly ass;
895
896   if (baseUri->type != chk)
897     return NULL;
898   if (GNUNET_OK !=
899       GNUNET_CONFIGURATION_get_value_filename (cfg,
900                                                "GNUNETD", "HOSTKEY", &keyfile))
901   {
902     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
903                 _("Lacking key configuration settings.\n"));
904     return NULL;
905   }
906   my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
907   if (my_private_key == NULL)
908   {
909     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
910                 _("Could not access hostkey file `%s'.\n"), keyfile);
911     GNUNET_free (keyfile);
912     return NULL;
913   }
914   GNUNET_free (keyfile);
915   GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
916   ass.purpose.size = htonl (sizeof (struct LocUriAssembly));
917   ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
918   ass.exptime = GNUNET_TIME_absolute_hton (expiration_time);
919   ass.fi = baseUri->data.chk;
920   ass.peer = my_public_key;
921   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
922   uri->type = loc;
923   uri->data.loc.fi = baseUri->data.chk;
924   uri->data.loc.expirationTime = expiration_time;
925   uri->data.loc.peer = my_public_key;
926   GNUNET_assert (GNUNET_OK ==
927                  GNUNET_CRYPTO_rsa_sign (my_private_key,
928                                          &ass.purpose,
929                                          &uri->data.loc.contentSignature));
930   GNUNET_CRYPTO_rsa_key_free (my_private_key);
931   return uri;
932 }
933
934
935 /**
936  * Create an SKS URI from a namespace and an identifier.
937  *
938  * @param ns namespace
939  * @param id identifier
940  * @param emsg where to store an error message
941  * @return an FS URI for the given namespace and identifier
942  */
943 struct GNUNET_FS_Uri *
944 GNUNET_FS_uri_sks_create (struct GNUNET_FS_Namespace *ns,
945                           const char *id, char **emsg)
946 {
947   struct GNUNET_FS_Uri *ns_uri;
948   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
949
950   ns_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
951   ns_uri->type = sks;
952   GNUNET_CRYPTO_rsa_key_get_public (ns->key, &pk);
953   GNUNET_CRYPTO_hash (&pk, sizeof (pk), &ns_uri->data.sks.namespace);
954   ns_uri->data.sks.identifier = GNUNET_strdup (id);
955   return ns_uri;
956 }
957
958
959 /**
960  * Create an SKS URI from a namespace ID and an identifier.
961  *
962  * @param nsid namespace ID
963  * @param id identifier
964  * @return an FS URI for the given namespace and identifier
965  */
966 struct GNUNET_FS_Uri *
967 GNUNET_FS_uri_sks_create_from_nsid (GNUNET_HashCode * nsid, const char *id)
968 {
969   struct GNUNET_FS_Uri *ns_uri;
970
971   ns_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
972   ns_uri->type = sks;
973   ns_uri->data.sks.namespace = *nsid;
974   ns_uri->data.sks.identifier = GNUNET_strdup (id);
975   return ns_uri;
976 }
977
978
979 /**
980  * Canonicalize a keyword.
981  * 
982  * @param in input string (the keyword)
983  * @return canonicalized keyword
984  */
985 static char *
986 canonicalize_keyword (const char *in)
987 {
988   char *ret;
989   char *wpos;
990   const char *rpos;
991
992   ret = GNUNET_strdup (in);
993   wpos = ret;
994   rpos = in;
995   while ('\0' != *rpos)
996   {
997     switch (tolower ((unsigned char) *rpos))
998     {
999     case 'a':
1000     case 'e':
1001     case 'i':
1002     case 'o':
1003     case 'u':
1004     case ' ':
1005     case '\t':
1006     case '\n':
1007     case '\r':
1008       /* skip characters listed above */
1009       break;
1010     case 'b':
1011     case 'c':
1012     case 'd':
1013     case 'f':
1014     case 'g':
1015     case 'h':
1016     case 'j':
1017     case 'k':
1018     case 'l':
1019     case 'm':
1020     case 'n':
1021     case 'p':
1022     case 'r':
1023     case 's':
1024     case 't':
1025     case 'v':
1026     case 'w':
1027     case 'x':
1028     case 'y':
1029     case 'z':
1030       /* convert characters listed above to lower case */
1031       *wpos = tolower ((unsigned char) *rpos);
1032       wpos++;
1033       break;
1034     case '!':
1035     case '.':
1036     case '?':
1037     case '-':
1038       /* keep characters listed above without changes */
1039       *wpos = *rpos;
1040       wpos++;
1041       break;
1042     default:
1043       /* replace characters listed above with '_' */
1044       *wpos = '_';
1045       wpos++;
1046       break;
1047     }
1048     rpos++;
1049   }
1050   return ret;
1051 }
1052
1053
1054 /**
1055  * Canonicalize keyword URI.  Performs operations such
1056  * as decapitalization and removal of certain characters.
1057  * (useful for search).
1058  *
1059  * @param uri the URI to canonicalize 
1060  * @return canonicalized version of the URI, NULL on error
1061  */
1062 struct GNUNET_FS_Uri *
1063 GNUNET_FS_uri_ksk_canonicalize (const struct GNUNET_FS_Uri *uri)
1064 {
1065   struct GNUNET_FS_Uri *ret;
1066   unsigned int kc;
1067   unsigned int i;
1068   char **kl;
1069
1070   kc = uri->data.ksk.keywordCount;
1071   kl = GNUNET_malloc (kc * sizeof (char *));
1072   for (i = 0; i < kc; i++)
1073     kl[i] = canonicalize_keyword (uri->data.ksk.keywords[i]);
1074   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1075   ret->type = ksk;
1076   ret->data.ksk.keywordCount = kc;
1077   ret->data.ksk.keywords = kl;
1078   return ret;
1079 }
1080
1081
1082 /**
1083  * Merge the sets of keywords from two KSK URIs.
1084  * (useful for merging the canonicalized keywords with
1085  * the original keywords for sharing).
1086  *
1087  * @param u1 first uri
1088  * @param u2 second uri
1089  * @return merged URI, NULL on error
1090  */
1091 struct GNUNET_FS_Uri *
1092 GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
1093                          const struct GNUNET_FS_Uri *u2)
1094 {
1095   struct GNUNET_FS_Uri *ret;
1096   unsigned int kc;
1097   unsigned int i;
1098   unsigned int j;
1099   int found;
1100   const char *kp;
1101   char **kl;
1102
1103   if ((u1 == NULL) && (u2 == NULL))
1104     return NULL;
1105   if (u1 == NULL)
1106     return GNUNET_FS_uri_dup (u2);
1107   if (u2 == NULL)
1108     return GNUNET_FS_uri_dup (u1);
1109   if ((u1->type != ksk) || (u2->type != ksk))
1110   {
1111     GNUNET_break (0);
1112     return NULL;
1113   }
1114   kc = u1->data.ksk.keywordCount;
1115   kl = GNUNET_malloc ((kc + u2->data.ksk.keywordCount) * sizeof (char *));
1116   for (i = 0; i < u1->data.ksk.keywordCount; i++)
1117     kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]);
1118   for (i = 0; i < u2->data.ksk.keywordCount; i++)
1119   {
1120     kp = u2->data.ksk.keywords[i];
1121     found = 0;
1122     for (j = 0; j < u1->data.ksk.keywordCount; j++)
1123       if (0 == strcmp (kp + 1, kl[j] + 1))
1124       {
1125         found = 1;
1126         if (kp[0] == '+')
1127           kl[j][0] = '+';
1128         break;
1129       }
1130     if (0 == found)
1131       kl[kc++] = GNUNET_strdup (kp);
1132   }
1133   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1134   ret->type = ksk;
1135   ret->data.ksk.keywordCount = kc;
1136   ret->data.ksk.keywords = kl;
1137   return ret;
1138 }
1139
1140
1141 /**
1142  * Duplicate URI.
1143  *
1144  * @param uri the URI to duplicate
1145  * @return copy of the URI
1146  */
1147 struct GNUNET_FS_Uri *
1148 GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
1149 {
1150   struct GNUNET_FS_Uri *ret;
1151   unsigned int i;
1152
1153   if (uri == NULL)
1154     return NULL;
1155   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1156   memcpy (ret, uri, sizeof (struct GNUNET_FS_Uri));
1157   switch (ret->type)
1158   {
1159   case ksk:
1160     if (ret->data.ksk.keywordCount >=
1161         GNUNET_MAX_MALLOC_CHECKED / sizeof (char *))
1162     {
1163       GNUNET_break (0);
1164       GNUNET_free (ret);
1165       return NULL;
1166     }
1167     if (ret->data.ksk.keywordCount > 0)
1168     {
1169       ret->data.ksk.keywords
1170           = GNUNET_malloc (ret->data.ksk.keywordCount * sizeof (char *));
1171       for (i = 0; i < ret->data.ksk.keywordCount; i++)
1172         ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]);
1173     }
1174     else
1175       ret->data.ksk.keywords = NULL;    /* just to be sure */
1176     break;
1177   case sks:
1178     ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
1179     break;
1180   case loc:
1181     break;
1182   default:
1183     break;
1184   }
1185   return ret;
1186 }
1187
1188
1189 /**
1190  * Create an FS URI from a single user-supplied string of keywords.
1191  * The string is broken up at spaces into individual keywords.
1192  * Keywords that start with "+" are mandatory.  Double-quotes can
1193  * be used to prevent breaking up strings at spaces (and also
1194  * to specify non-mandatory keywords starting with "+").
1195  *
1196  * Keywords must contain a balanced number of double quotes and
1197  * double quotes can not be used in the actual keywords (for
1198  * example, the string '""foo bar""' will be turned into two
1199  * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
1200  *
1201  * @param keywords the keyword string
1202  * @param emsg where to store an error message
1203  * @return an FS URI for the given keywords, NULL
1204  *  if keywords is not legal (i.e. empty).
1205  */
1206 struct GNUNET_FS_Uri *
1207 GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg)
1208 {
1209   char **keywordarr;
1210   unsigned int num_Words;
1211   int inWord;
1212   char *pos;
1213   struct GNUNET_FS_Uri *uri;
1214   char *searchString;
1215   int saw_quote;
1216
1217   if (keywords == NULL)
1218   {
1219     *emsg = GNUNET_strdup (_("No keywords specified!\n"));
1220     GNUNET_break (0);
1221     return NULL;
1222   }
1223   searchString = GNUNET_strdup (keywords);
1224   num_Words = 0;
1225   inWord = 0;
1226   saw_quote = 0;
1227   pos = searchString;
1228   while ('\0' != *pos)
1229   {
1230     if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1231     {
1232       inWord = 0;
1233     }
1234     else if (0 == inWord)
1235     {
1236       inWord = 1;
1237       ++num_Words;
1238     }
1239     if ('"' == *pos)
1240       saw_quote = (saw_quote + 1) % 2;
1241     pos++;
1242   }
1243   if (num_Words == 0)
1244   {
1245     GNUNET_free (searchString);
1246     *emsg = GNUNET_strdup (_("No keywords specified!\n"));
1247     return NULL;
1248   }
1249   if (saw_quote != 0)
1250   {
1251     GNUNET_free (searchString);
1252     *emsg = GNUNET_strdup (_("Number of double-quotes not balanced!\n"));
1253     return NULL;
1254   }
1255   keywordarr = GNUNET_malloc (num_Words * sizeof (char *));
1256   num_Words = 0;
1257   inWord = 0;
1258   pos = searchString;
1259   while ('\0' != *pos)
1260   {
1261     if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1262     {
1263       inWord = 0;
1264       *pos = '\0';
1265     }
1266     else if (0 == inWord)
1267     {
1268       keywordarr[num_Words] = pos;
1269       inWord = 1;
1270       ++num_Words;
1271     }
1272     if ('"' == *pos)
1273       saw_quote = (saw_quote + 1) % 2;
1274     pos++;
1275   }
1276   uri =
1277       GNUNET_FS_uri_ksk_create_from_args (num_Words,
1278                                           (const char **) keywordarr);
1279   GNUNET_free (keywordarr);
1280   GNUNET_free (searchString);
1281   return uri;
1282 }
1283
1284
1285 /**
1286  * Create an FS URI from a user-supplied command line of keywords.
1287  * Arguments should start with "+" to indicate mandatory
1288  * keywords.
1289  *
1290  * @param argc number of keywords
1291  * @param argv keywords (double quotes are not required for
1292  *             keywords containing spaces; however, double
1293  *             quotes are required for keywords starting with
1294  *             "+"); there is no mechanism for having double
1295  *             quotes in the actual keywords (if the user
1296  *             did specifically specify double quotes, the
1297  *             caller should convert each double quote
1298  *             into two single quotes).
1299  * @return an FS URI for the given keywords, NULL
1300  *  if keywords is not legal (i.e. empty).
1301  */
1302 struct GNUNET_FS_Uri *
1303 GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv)
1304 {
1305   unsigned int i;
1306   struct GNUNET_FS_Uri *uri;
1307   const char *keyword;
1308   char *val;
1309   const char *r;
1310   char *w;
1311   char *emsg;
1312
1313   if (argc == 0)
1314     return NULL;
1315   /* allow URI to be given as one and only keyword and
1316    * handle accordingly */
1317   emsg = NULL;
1318   if ((argc == 1) &&
1319       (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) &&
1320       (0 ==
1321        strncmp (argv[0], GNUNET_FS_URI_PREFIX, strlen (GNUNET_FS_URI_PREFIX)))
1322       && (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg))))
1323     return uri;
1324   GNUNET_free_non_null (emsg);
1325   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1326   uri->type = ksk;
1327   uri->data.ksk.keywordCount = argc;
1328   uri->data.ksk.keywords = GNUNET_malloc (argc * sizeof (char *));
1329   for (i = 0; i < argc; i++)
1330   {
1331     keyword = argv[i];
1332     if (keyword[0] == '+')
1333       val = GNUNET_strdup (keyword);
1334     else
1335       GNUNET_asprintf (&val, " %s", keyword);
1336     r = val;
1337     w = val;
1338     while ('\0' != *r)
1339     {
1340       if ('"' == *r)
1341         r++;
1342       else
1343         *(w++) = *(r++);
1344     }
1345     *w = '\0';
1346     uri->data.ksk.keywords[i] = val;
1347   }
1348   return uri;
1349 }
1350
1351
1352 /**
1353  * Test if two URIs are equal.
1354  *
1355  * @param u1 one of the URIs
1356  * @param u2 the other URI
1357  * @return GNUNET_YES if the URIs are equal
1358  */
1359 int
1360 GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
1361                           const struct GNUNET_FS_Uri *u2)
1362 {
1363   int ret;
1364   unsigned int i;
1365   unsigned int j;
1366
1367   GNUNET_assert (u1 != NULL);
1368   GNUNET_assert (u2 != NULL);
1369   if (u1->type != u2->type)
1370     return GNUNET_NO;
1371   switch (u1->type)
1372   {
1373   case chk:
1374     if (0 == memcmp (&u1->data.chk,
1375                      &u2->data.chk, sizeof (struct FileIdentifier)))
1376       return GNUNET_YES;
1377     return GNUNET_NO;
1378   case sks:
1379     if ((0 == memcmp (&u1->data.sks.namespace,
1380                       &u2->data.sks.namespace,
1381                       sizeof (GNUNET_HashCode))) &&
1382         (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier)))
1383
1384       return GNUNET_YES;
1385     return GNUNET_NO;
1386   case ksk:
1387     if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount)
1388       return GNUNET_NO;
1389     for (i = 0; i < u1->data.ksk.keywordCount; i++)
1390     {
1391       ret = GNUNET_NO;
1392       for (j = 0; j < u2->data.ksk.keywordCount; j++)
1393       {
1394         if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j]))
1395         {
1396           ret = GNUNET_YES;
1397           break;
1398         }
1399       }
1400       if (ret == GNUNET_NO)
1401         return GNUNET_NO;
1402     }
1403     return GNUNET_YES;
1404   case loc:
1405     if (memcmp (&u1->data.loc,
1406                 &u2->data.loc,
1407                 sizeof (struct FileIdentifier) +
1408                 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1409                 sizeof (struct GNUNET_TIME_Absolute) +
1410                 sizeof (unsigned short) + sizeof (unsigned short)) != 0)
1411       return GNUNET_NO;
1412     return GNUNET_YES;
1413   default:
1414     return GNUNET_NO;
1415   }
1416 }
1417
1418
1419 /**
1420  * Is this a namespace URI?
1421  *
1422  * @param uri the uri to check
1423  * @return GNUNET_YES if this is an SKS uri
1424  */
1425 int
1426 GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
1427 {
1428   return uri->type == sks;
1429 }
1430
1431
1432 /**
1433  * Get the ID of a namespace from the given
1434  * namespace URI.
1435  *
1436  * @param uri the uri to get the namespace ID from
1437  * @param nsid where to store the ID of the namespace
1438  * @return GNUNET_OK on success
1439  */
1440 int
1441 GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
1442                                  GNUNET_HashCode * nsid)
1443 {
1444   if (!GNUNET_FS_uri_test_sks (uri))
1445   {
1446     GNUNET_break (0);
1447     return GNUNET_SYSERR;
1448   }
1449   *nsid = uri->data.sks.namespace;
1450   return GNUNET_OK;
1451 }
1452
1453
1454 /**
1455  * Get the content identifier of an SKS URI.
1456  *
1457  * @param uri the sks uri
1458  * @return NULL on error (not a valid SKS URI)
1459  */
1460 char *
1461 GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
1462 {
1463   if (!GNUNET_FS_uri_test_sks (uri))
1464   {
1465     GNUNET_break (0);
1466     return NULL;
1467   }
1468   return GNUNET_strdup (uri->data.sks.identifier);
1469 }
1470
1471
1472 /**
1473  * Convert namespace URI to a human readable format
1474  * (using the namespace description, if available).
1475  *
1476  * @param cfg configuration to use
1477  * @param uri SKS uri to convert
1478  * @return NULL on error (not an SKS URI)
1479  */
1480 char *
1481 GNUNET_FS_uri_sks_to_string_fancy (struct GNUNET_CONFIGURATION_Handle *cfg,
1482                                    const struct GNUNET_FS_Uri *uri)
1483 {
1484   char *ret;
1485   char *name;
1486
1487   if (uri->type != sks)
1488     return NULL;
1489   name = GNUNET_PSEUDONYM_id_to_name (cfg, &uri->data.sks.namespace);
1490   if (name == NULL)
1491     return GNUNET_FS_uri_to_string (uri);
1492   GNUNET_asprintf (&ret, "%s: %s", name, uri->data.sks.identifier);
1493   GNUNET_free (name);
1494   return ret;
1495 }
1496
1497
1498 /**
1499  * Is this a keyword URI?
1500  *
1501  * @param uri the uri
1502  * @return GNUNET_YES if this is a KSK uri
1503  */
1504 int
1505 GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
1506 {
1507 #if EXTRA_CHECKS
1508   unsigned int i;
1509
1510   if (uri->type == ksk)
1511   {
1512     for (i = uri->data.ksk.keywordCount - 1; i >= 0; i--)
1513       GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
1514   }
1515 #endif
1516   return uri->type == ksk;
1517 }
1518
1519
1520 /**
1521  * Is this a file (or directory) URI?
1522  *
1523  * @param uri the uri to check
1524  * @return GNUNET_YES if this is a CHK uri
1525  */
1526 int
1527 GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
1528 {
1529   return uri->type == chk;
1530 }
1531
1532
1533 /**
1534  * What is the size of the file that this URI
1535  * refers to?
1536  *
1537  * @param uri the CHK URI to inspect
1538  * @return size of the file as specified in the CHK URI
1539  */
1540 uint64_t
1541 GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri * uri)
1542 {
1543   switch (uri->type)
1544   {
1545   case chk:
1546     return GNUNET_ntohll (uri->data.chk.file_length);
1547   case loc:
1548     return GNUNET_ntohll (uri->data.loc.fi.file_length);
1549   default:
1550     GNUNET_assert (0);
1551   }
1552   return 0;                     /* unreachable */
1553 }
1554
1555
1556 /**
1557  * Is this a location URI?
1558  *
1559  * @param uri the uri to check
1560  * @return GNUNET_YES if this is a LOC uri
1561  */
1562 int
1563 GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1564 {
1565   return uri->type == loc;
1566 }
1567
1568
1569 /**
1570  * Function called on each value in the meta data.
1571  * Adds it to the URI.
1572  *
1573  * @param cls URI to update
1574  * @param plugin_name name of the plugin that produced this value;
1575  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
1576  *        used in the main libextractor library and yielding
1577  *        meta data).
1578  * @param type libextractor-type describing the meta data
1579  * @param format basic format information about data 
1580  * @param data_mime_type mime-type of data (not of the original file);
1581  *        can be NULL (if mime-type is not known)
1582  * @param data actual meta-data found
1583  * @param data_len number of bytes in data
1584  * @return 0 (always)
1585  */
1586 static int
1587 gather_uri_data (void *cls,
1588                  const char *plugin_name,
1589                  enum EXTRACTOR_MetaType type,
1590                  enum EXTRACTOR_MetaFormat format,
1591                  const char *data_mime_type, const char *data, size_t data_len)
1592 {
1593   struct GNUNET_FS_Uri *uri = cls;
1594   char *nkword;
1595   int j;
1596
1597   if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1598       (format != EXTRACTOR_METAFORMAT_C_STRING))
1599     return 0;
1600   for (j = uri->data.ksk.keywordCount - 1; j >= 0; j--)
1601     if (0 == strcmp (&uri->data.ksk.keywords[j][1], data))
1602       return GNUNET_OK;
1603   GNUNET_asprintf (&nkword, " %s",      /* space to mark as 'non mandatory' */
1604                    data);
1605   uri->data.ksk.keywords[uri->data.ksk.keywordCount++] = nkword;
1606   return 0;
1607 }
1608
1609
1610 /**
1611  * Construct a keyword-URI from meta-data (take all entries
1612  * in the meta-data and construct one large keyword URI
1613  * that lists all keywords that can be found in the meta-data).
1614  *
1615  * @param md metadata to use
1616  * @return NULL on error, otherwise a KSK URI
1617  */
1618 struct GNUNET_FS_Uri *
1619 GNUNET_FS_uri_ksk_create_from_meta_data (const struct GNUNET_CONTAINER_MetaData
1620                                          *md)
1621 {
1622   struct GNUNET_FS_Uri *ret;
1623   int ent;
1624
1625   if (md == NULL)
1626     return NULL;
1627   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1628   ret->type = ksk;
1629   ent = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
1630   if (ent > 0)
1631   {
1632     ret->data.ksk.keywords = GNUNET_malloc (sizeof (char *) * ent);
1633     GNUNET_CONTAINER_meta_data_iterate (md, &gather_uri_data, ret);
1634   }
1635   return ret;
1636 }
1637
1638
1639 /**
1640  * In URI-encoding, does the given character
1641  * need to be encoded using %-encoding?
1642  */
1643 static int
1644 needs_percent (char c)
1645 {
1646   return (!((isalnum ((unsigned char) c)) ||
1647             (c == '-') || (c == '_') || (c == '.') || (c == '~')));
1648 }
1649
1650
1651 /**
1652  * Convert a KSK URI to a string.
1653  *
1654  * @param uri the URI to convert
1655  * @return NULL on error (i.e. keywordCount == 0)
1656  */
1657 static char *
1658 uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1659 {
1660   char **keywords;
1661   unsigned int keywordCount;
1662   size_t n;
1663   char *ret;
1664   unsigned int i;
1665   unsigned int j;
1666   unsigned int wpos;
1667   size_t slen;
1668   const char *keyword;
1669
1670   if (uri->type != ksk)
1671     return NULL;
1672   keywords = uri->data.ksk.keywords;
1673   keywordCount = uri->data.ksk.keywordCount;
1674   n = keywordCount + strlen (GNUNET_FS_URI_PREFIX) +
1675       strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1676   for (i = 0; i < keywordCount; i++)
1677   {
1678     keyword = keywords[i];
1679     slen = strlen (keyword);
1680     n += slen;
1681     for (j = 0; j < slen; j++)
1682     {
1683       if ((j == 0) && (keyword[j] == ' '))
1684       {
1685         n--;
1686         continue;               /* skip leading space */
1687       }
1688       if (needs_percent (keyword[j]))
1689         n += 2;                 /* will use %-encoding */
1690     }
1691   }
1692   ret = GNUNET_malloc (n);
1693   strcpy (ret, GNUNET_FS_URI_PREFIX);
1694   strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1695   wpos = strlen (ret);
1696   for (i = 0; i < keywordCount; i++)
1697   {
1698     keyword = keywords[i];
1699     slen = strlen (keyword);
1700     for (j = 0; j < slen; j++)
1701     {
1702       if ((j == 0) && (keyword[j] == ' '))
1703         continue;               /* skip leading space */
1704       if (needs_percent (keyword[j]))
1705       {
1706         sprintf (&ret[wpos], "%%%02X", keyword[j]);
1707         wpos += 3;
1708       }
1709       else
1710       {
1711         ret[wpos++] = keyword[j];
1712       }
1713     }
1714     if (i != keywordCount - 1)
1715       ret[wpos++] = '+';
1716   }
1717   return ret;
1718 }
1719
1720
1721 /**
1722  * Convert SKS URI to a string.
1723  *
1724  * @param uri sks uri to convert
1725  * @return NULL on error
1726  */
1727 static char *
1728 uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1729 {
1730   const GNUNET_HashCode *namespace;
1731   const char *identifier;
1732   char *ret;
1733   struct GNUNET_CRYPTO_HashAsciiEncoded ns;
1734
1735   if (uri->type != sks)
1736     return NULL;
1737   namespace = &uri->data.sks.namespace;
1738   identifier = uri->data.sks.identifier;
1739   GNUNET_CRYPTO_hash_to_enc (namespace, &ns);
1740   GNUNET_asprintf (&ret,
1741                    "%s%s%s/%s",
1742                    GNUNET_FS_URI_PREFIX,
1743                    GNUNET_FS_URI_SKS_INFIX, (const char *) &ns, identifier);
1744   return ret;
1745 }
1746
1747
1748 /**
1749  * Convert a CHK URI to a string.
1750  *
1751  * @param uri chk uri to convert
1752  * @return NULL on error
1753  */
1754 static char *
1755 uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1756 {
1757   const struct FileIdentifier *fi;
1758   char *ret;
1759   struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1760   struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1761
1762   if (uri->type != chk)
1763     return NULL;
1764   fi = &uri->data.chk;
1765   GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1766   GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1767
1768   GNUNET_asprintf (&ret,
1769                    "%s%s%s.%s.%llu",
1770                    GNUNET_FS_URI_PREFIX,
1771                    GNUNET_FS_URI_CHK_INFIX,
1772                    (const char *) &keyhash,
1773                    (const char *) &queryhash, GNUNET_ntohll (fi->file_length));
1774   return ret;
1775 }
1776
1777 /**
1778  * Convert binary data to a string.
1779  *
1780  * @param data binary data to convert
1781  * @param size number of bytes in data
1782  * @return converted data
1783  */
1784 static char *
1785 bin2enc (const void *data, size_t size)
1786 {
1787   /**
1788    * 64 characters for encoding, 6 bits per character
1789    */
1790   static char *tbl =
1791       "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=";
1792
1793   size_t len;
1794   size_t pos;
1795   unsigned int bits;
1796   unsigned int hbits;
1797   char *ret;
1798
1799   GNUNET_assert (strlen (tbl) == 64);
1800   len = size * 8 / 6;
1801   if (((size * 8) % 6) != 0)
1802     len++;
1803   ret = GNUNET_malloc (len + 1);
1804   ret[len] = '\0';
1805   len = 0;
1806   bits = 0;
1807   hbits = 0;
1808   for (pos = 0; pos < size; pos++)
1809   {
1810     bits |= ((((const unsigned char *) data)[pos]) << hbits);
1811     hbits += 8;
1812     while (hbits >= 6)
1813     {
1814       ret[len++] = tbl[bits & 63];
1815       bits >>= 6;
1816       hbits -= 6;
1817     }
1818   }
1819   if (hbits > 0)
1820     ret[len] = tbl[bits & 63];
1821   return ret;
1822 }
1823
1824
1825 /**
1826  * Convert a LOC URI to a string.
1827  *
1828  * @param uri loc uri to convert
1829  * @return NULL on error
1830  */
1831 static char *
1832 uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1833 {
1834   char *ret;
1835   struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1836   struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1837   char *peerId;
1838   char *peerSig;
1839
1840   GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
1841   GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
1842   peerId = bin2enc (&uri->data.loc.peer,
1843                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
1844   peerSig = bin2enc (&uri->data.loc.contentSignature,
1845                      sizeof (struct GNUNET_CRYPTO_RsaSignature));
1846   GNUNET_asprintf (&ret,
1847                    "%s%s%s.%s.%llu.%s.%s.%llu",
1848                    GNUNET_FS_URI_PREFIX,
1849                    GNUNET_FS_URI_LOC_INFIX,
1850                    (const char *) &keyhash,
1851                    (const char *) &queryhash,
1852                    (unsigned long long) GNUNET_ntohll (uri->data.loc.
1853                                                        fi.file_length), peerId,
1854                    peerSig,
1855                    (unsigned long long) uri->data.loc.expirationTime.abs_value);
1856   GNUNET_free (peerSig);
1857   GNUNET_free (peerId);
1858   return ret;
1859 }
1860
1861
1862 /**
1863  * Convert a URI to a UTF-8 String.
1864  *
1865  * @param uri uri to convert to a string
1866  * @return the UTF-8 string
1867  */
1868 char *
1869 GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
1870 {
1871   if (uri == NULL)
1872   {
1873     GNUNET_break (0);
1874     return NULL;
1875   }
1876   switch (uri->type)
1877   {
1878   case ksk:
1879     return uri_ksk_to_string (uri);
1880   case sks:
1881     return uri_sks_to_string (uri);
1882   case chk:
1883     return uri_chk_to_string (uri);
1884   case loc:
1885     return uri_loc_to_string (uri);
1886   default:
1887     GNUNET_break (0);
1888     return NULL;
1889   }
1890 }
1891
1892 /* end of fs_uri.c */