eedf72e047c3ded30b1c7e6f9188e3c9f96bfc29
[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, &s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)],
423           sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
424   h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
425
426   if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1, &fi.chk.key)) ||
427       (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2, &fi.chk.query)) ||
428       (1 !=
429        SSCANF (&s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
430                "%llu", &flen)))
431   {
432     *emsg = GNUNET_strdup (_("Malformed CHK URI"));
433     return NULL;
434   }
435   fi.file_length = GNUNET_htonll (flen);
436   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
437   ret->type = chk;
438   ret->data.chk = fi;
439   return ret;
440 }
441
442
443 /**
444  * Convert a character back to the binary value
445  * that it represents (given base64-encoding).
446  *
447  * @param a character to convert
448  * @return offset in the "tbl" array
449  */
450 static unsigned int
451 c2v (unsigned char a)
452 {
453   if ((a >= '0') && (a <= '9'))
454     return a - '0';
455   if ((a >= 'A') && (a <= 'Z'))
456     return (a - 'A' + 10);
457   if ((a >= 'a') && (a <= 'z'))
458     return (a - 'a' + 36);
459   if (a == '_')
460     return 62;
461   if (a == '=')
462     return 63;
463   return -1;
464 }
465
466
467 /**
468  * Convert string back to binary data.
469  *
470  * @param input '\\0'-terminated string
471  * @param data where to write binary data
472  * @param size how much data should be converted
473  * @return number of characters processed from input,
474  *        -1 on error
475  */
476 static int
477 enc2bin (const char *input, void *data, size_t size)
478 {
479   size_t len;
480   size_t pos;
481   unsigned int bits;
482   unsigned int hbits;
483
484   len = size * 8 / 6;
485   if (((size * 8) % 6) != 0)
486     len++;
487   if (strlen (input) < len)
488     return -1;                  /* error! */
489   bits = 0;
490   hbits = 0;
491   len = 0;
492   for (pos = 0; pos < size; pos++)
493   {
494     while (hbits < 8)
495     {
496       bits |= (c2v (input[len++]) << hbits);
497       hbits += 6;
498     }
499     (((unsigned char *) data)[pos]) = (unsigned char) bits;
500     bits >>= 8;
501     hbits -= 8;
502   }
503   return len;
504 }
505
506
507 /**
508  * Structure that defines how the
509  * contents of a location URI must be
510  * assembled in memory to create or
511  * verify the signature of a location
512  * URI.
513  */
514 struct LocUriAssembly
515 {
516   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
517
518   struct GNUNET_TIME_AbsoluteNBO exptime;
519
520   struct FileIdentifier fi;
521
522   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded peer;
523
524 };
525
526
527 #define GNUNET_FS_URI_LOC_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_LOC_INFIX
528
529 /**
530  * Parse a LOC URI.
531  * Also verifies validity of the location URI.
532  *
533  * @param s an uri string
534  * @param emsg where to store the parser error message (if any)
535  * @return NULL on error, valid LOC URI otherwise
536  */
537 static struct GNUNET_FS_Uri *
538 uri_loc_parse (const char *s, char **emsg)
539 {
540   struct GNUNET_FS_Uri *uri;
541   char h1[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
542   char h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
543   unsigned int pos;
544   unsigned int npos;
545   unsigned long long exptime;
546   unsigned long long flen;
547   struct GNUNET_TIME_Absolute et;
548   struct GNUNET_CRYPTO_RsaSignature sig;
549   struct LocUriAssembly ass;
550   int ret;
551   size_t slen;
552
553   GNUNET_assert (s != NULL);
554   slen = strlen (s);
555   pos = strlen (GNUNET_FS_URI_LOC_PREFIX);
556   if ((slen < pos + 2 * sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1) ||
557       (0 != strncmp (s, GNUNET_FS_URI_LOC_PREFIX, pos)))
558     return NULL;                /* not an SKS URI */
559   if ((s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] != '.') ||
560       (s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2 - 1] != '.'))
561   {
562     *emsg = GNUNET_strdup (_("SKS URI malformed"));
563     return NULL;
564   }
565   memcpy (h1, &s[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
566   h1[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
567   memcpy (h2, &s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)],
568           sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
569   h2[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
570
571   if ((GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h1, &ass.fi.chk.key)) ||
572       (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (h2, &ass.fi.chk.query)) ||
573       (1 !=
574        SSCANF (&s[pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
575                "%llu", &flen)))
576   {
577     *emsg = GNUNET_strdup (_("SKS URI malformed"));
578     return NULL;
579   }
580   ass.fi.file_length = GNUNET_htonll (flen);
581
582   npos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) * 2;
583   while ((s[npos] != '\0') && (s[npos] != '.'))
584     npos++;
585   if (s[npos] == '\0')
586   {
587     *emsg = GNUNET_strdup (_("SKS URI malformed"));
588     goto ERR;
589   }
590   npos++;
591   ret =
592       enc2bin (&s[npos], &ass.peer,
593                sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
594   if (ret == -1)
595   {
596     *emsg =
597         GNUNET_strdup (_("SKS URI malformed (could not decode public key)"));
598     goto ERR;
599   }
600   npos += ret;
601   if (s[npos++] != '.')
602   {
603     *emsg = GNUNET_strdup (_("SKS URI malformed (could not find signature)"));
604     goto ERR;
605   }
606   ret = enc2bin (&s[npos], &sig, sizeof (struct GNUNET_CRYPTO_RsaSignature));
607   if (ret == -1)
608   {
609     *emsg = GNUNET_strdup (_("SKS URI malformed (could not decode signature)"));
610     goto ERR;
611   }
612   npos += ret;
613   if (s[npos++] != '.')
614   {
615     *emsg = GNUNET_strdup (_("SKS URI malformed"));
616     goto ERR;
617   }
618   if (1 != SSCANF (&s[npos], "%llu", &exptime))
619   {
620     *emsg =
621         GNUNET_strdup (_
622                        ("SKS URI malformed (could not parse expiration time)"));
623     goto ERR;
624   }
625   ass.purpose.size = htonl (sizeof (struct LocUriAssembly));
626   ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
627   et.abs_value = exptime;
628   ass.exptime = GNUNET_TIME_absolute_hton (et);
629   if (GNUNET_OK !=
630       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT,
631                                 &ass.purpose, &sig, &ass.peer))
632   {
633     *emsg =
634         GNUNET_strdup (_("SKS URI malformed (signature failed validation)"));
635     goto ERR;
636   }
637   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
638   uri->type = loc;
639   uri->data.loc.fi = ass.fi;
640   uri->data.loc.peer = ass.peer;
641   uri->data.loc.expirationTime = et;
642   uri->data.loc.contentSignature = sig;
643
644   return uri;
645 ERR:
646   return NULL;
647 }
648
649
650 /**
651  * Convert a UTF-8 String to a URI.
652  *
653  * @param uri string to parse
654  * @param emsg where to store the parser error message (if any)
655  * @return NULL on error
656  */
657 struct GNUNET_FS_Uri *
658 GNUNET_FS_uri_parse (const char *uri, char **emsg)
659 {
660   struct GNUNET_FS_Uri *ret;
661   char *msg;
662
663   if (NULL == emsg)
664     emsg = &msg;
665   *emsg = NULL;
666   if ((NULL != (ret = uri_chk_parse (uri, emsg))) ||
667       (NULL != (ret = uri_ksk_parse (uri, emsg))) ||
668       (NULL != (ret = uri_sks_parse (uri, emsg))) ||
669       (NULL != (ret = uri_loc_parse (uri, emsg))))
670     return ret;
671   if (NULL == *emsg)
672     *emsg = GNUNET_strdup (_("Unrecognized URI type"));
673   if (emsg == &msg)
674     GNUNET_free (msg);
675   return NULL;
676 }
677
678
679 /**
680  * Free URI.
681  *
682  * @param uri uri to free
683  */
684 void
685 GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri)
686 {
687   unsigned int i;
688
689   GNUNET_assert (uri != NULL);
690   switch (uri->type)
691   {
692   case ksk:
693     for (i = 0; i < uri->data.ksk.keywordCount; i++)
694       GNUNET_free (uri->data.ksk.keywords[i]);
695     GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, 0);
696     break;
697   case sks:
698     GNUNET_free (uri->data.sks.identifier);
699     break;
700   case loc:
701     break;
702   default:
703     /* do nothing */
704     break;
705   }
706   GNUNET_free (uri);
707 }
708
709 /**
710  * How many keywords are ANDed in this keyword URI?
711  *
712  * @param uri ksk uri to get the number of keywords from
713  * @return 0 if this is not a keyword URI
714  */
715 unsigned int
716 GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri)
717 {
718   if (uri->type != ksk)
719     return 0;
720   return uri->data.ksk.keywordCount;
721 }
722
723
724 /**
725  * Iterate over all keywords in this keyword URI.
726  *
727  * @param uri ksk uri to get the keywords from
728  * @param iterator function to call on each keyword
729  * @param iterator_cls closure for iterator
730  * @return -1 if this is not a keyword URI, otherwise number of
731  *   keywords iterated over until iterator aborted
732  */
733 int
734 GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri,
735                                 GNUNET_FS_KeywordIterator iterator,
736                                 void *iterator_cls)
737 {
738   unsigned int i;
739   char *keyword;
740
741   if (uri->type != ksk)
742     return -1;
743   if (iterator == NULL)
744     return uri->data.ksk.keywordCount;
745   for (i = 0; i < uri->data.ksk.keywordCount; i++)
746   {
747     keyword = uri->data.ksk.keywords[i];
748     /* first character of keyword indicates
749      * if it is mandatory or not */
750     if (GNUNET_OK != iterator (iterator_cls, &keyword[1], keyword[0] == '+'))
751       return i;
752   }
753   return i;
754 }
755
756
757 /**
758  * Add the given keyword to the set of keywords represented by the URI.
759  * Does nothing if the keyword is already present.
760  *
761  * @param uri ksk uri to modify
762  * @param keyword keyword to add
763  * @param is_mandatory is this keyword mandatory?
764  */
765 void
766 GNUNET_FS_uri_ksk_add_keyword (struct GNUNET_FS_Uri *uri, const char *keyword,
767                                int is_mandatory)
768 {
769   unsigned int i;
770   const char *old;
771   char *n;
772
773   GNUNET_assert (uri->type == ksk);
774   for (i = 0; i < uri->data.ksk.keywordCount; i++)
775   {
776     old = uri->data.ksk.keywords[i];
777     if (0 == strcmp (&old[1], keyword))
778       return;
779   }
780   GNUNET_asprintf (&n, is_mandatory ? "+%s" : " %s", keyword);
781   GNUNET_array_append (uri->data.ksk.keywords, uri->data.ksk.keywordCount, n);
782 }
783
784
785 /**
786  * Remove the given keyword from the set of keywords represented by the URI.
787  * Does nothing if the keyword is not present.
788  *
789  * @param uri ksk uri to modify
790  * @param keyword keyword to add
791  */
792 void
793 GNUNET_FS_uri_ksk_remove_keyword (struct GNUNET_FS_Uri *uri,
794                                   const char *keyword)
795 {
796   unsigned int i;
797   char *old;
798
799   GNUNET_assert (uri->type == ksk);
800   for (i = 0; i < uri->data.ksk.keywordCount; i++)
801   {
802     old = uri->data.ksk.keywords[i];
803     if (0 == strcmp (&old[1], keyword))
804     {
805       uri->data.ksk.keywords[i] =
806           uri->data.ksk.keywords[uri->data.ksk.keywordCount - 1];
807       GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount,
808                          uri->data.ksk.keywordCount - 1);
809       GNUNET_free (old);
810       return;
811     }
812   }
813 }
814
815
816 /**
817  * Obtain the identity of the peer offering the data
818  *
819  * @param uri the location URI to inspect
820  * @param peer where to store the identify of the peer (presumably) offering the content
821  * @return GNUNET_SYSERR if this is not a location URI, otherwise GNUNET_OK
822  */
823 int
824 GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri,
825                                      struct GNUNET_PeerIdentity *peer)
826 {
827   if (uri->type != loc)
828     return GNUNET_SYSERR;
829   GNUNET_CRYPTO_hash (&uri->data.loc.peer,
830                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
831                       &peer->hashPubKey);
832   return GNUNET_OK;
833 }
834
835
836 /**
837  * Obtain the expiration of the LOC URI.
838  *
839  * @param uri location URI to get the expiration from
840  * @return expiration time of the URI
841  */
842 struct GNUNET_TIME_Absolute
843 GNUNET_FS_uri_loc_get_expiration (const struct GNUNET_FS_Uri *uri)
844 {
845   GNUNET_assert (uri->type == loc);
846   return uri->data.loc.expirationTime;
847 }
848
849
850
851 /**
852  * Obtain the URI of the content itself.
853  *
854  * @param uri location URI to get the content URI from
855  * @return NULL if argument is not a location URI
856  */
857 struct GNUNET_FS_Uri *
858 GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri)
859 {
860   struct GNUNET_FS_Uri *ret;
861
862   if (uri->type != loc)
863     return NULL;
864   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
865   ret->type = chk;
866   ret->data.chk = uri->data.loc.fi;
867   return ret;
868 }
869
870
871 /**
872  * Construct a location URI (this peer will be used for the location).
873  *
874  * @param baseUri content offered by the sender
875  * @param cfg configuration information (used to find our hostkey)
876  * @param expiration_time how long will the content be offered?
877  * @return the location URI, NULL on error
878  */
879 struct GNUNET_FS_Uri *
880 GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *baseUri,
881                           const struct GNUNET_CONFIGURATION_Handle *cfg,
882                           struct GNUNET_TIME_Absolute expiration_time)
883 {
884   struct GNUNET_FS_Uri *uri;
885   struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
886   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
887   char *keyfile;
888   struct LocUriAssembly ass;
889
890   if (baseUri->type != chk)
891     return NULL;
892   if (GNUNET_OK !=
893       GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
894                                                &keyfile))
895   {
896     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
897                 _("Lacking key configuration settings.\n"));
898     return NULL;
899   }
900   my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
901   if (my_private_key == NULL)
902   {
903     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
904                 _("Could not access hostkey file `%s'.\n"), keyfile);
905     GNUNET_free (keyfile);
906     return NULL;
907   }
908   GNUNET_free (keyfile);
909   GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
910   ass.purpose.size = htonl (sizeof (struct LocUriAssembly));
911   ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
912   ass.exptime = GNUNET_TIME_absolute_hton (expiration_time);
913   ass.fi = baseUri->data.chk;
914   ass.peer = my_public_key;
915   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
916   uri->type = loc;
917   uri->data.loc.fi = baseUri->data.chk;
918   uri->data.loc.expirationTime = expiration_time;
919   uri->data.loc.peer = my_public_key;
920   GNUNET_assert (GNUNET_OK ==
921                  GNUNET_CRYPTO_rsa_sign (my_private_key, &ass.purpose,
922                                          &uri->data.loc.contentSignature));
923   GNUNET_CRYPTO_rsa_key_free (my_private_key);
924   return uri;
925 }
926
927
928 /**
929  * Create an SKS URI from a namespace and an identifier.
930  *
931  * @param ns namespace
932  * @param id identifier
933  * @param emsg where to store an error message
934  * @return an FS URI for the given namespace and identifier
935  */
936 struct GNUNET_FS_Uri *
937 GNUNET_FS_uri_sks_create (struct GNUNET_FS_Namespace *ns, const char *id,
938                           char **emsg)
939 {
940   struct GNUNET_FS_Uri *ns_uri;
941   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
942
943   ns_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
944   ns_uri->type = sks;
945   GNUNET_CRYPTO_rsa_key_get_public (ns->key, &pk);
946   GNUNET_CRYPTO_hash (&pk, sizeof (pk), &ns_uri->data.sks.namespace);
947   ns_uri->data.sks.identifier = GNUNET_strdup (id);
948   return ns_uri;
949 }
950
951
952 /**
953  * Create an SKS URI from a namespace ID and an identifier.
954  *
955  * @param nsid namespace ID
956  * @param id identifier
957  * @return an FS URI for the given namespace and identifier
958  */
959 struct GNUNET_FS_Uri *
960 GNUNET_FS_uri_sks_create_from_nsid (GNUNET_HashCode * nsid, const char *id)
961 {
962   struct GNUNET_FS_Uri *ns_uri;
963
964   ns_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
965   ns_uri->type = sks;
966   ns_uri->data.sks.namespace = *nsid;
967   ns_uri->data.sks.identifier = GNUNET_strdup (id);
968   return ns_uri;
969 }
970
971
972 /**
973  * Canonicalize a keyword.
974  *
975  * @param in input string (the keyword)
976  * @return canonicalized keyword
977  */
978 static char *
979 canonicalize_keyword (const char *in)
980 {
981   char *ret;
982   char *wpos;
983   const char *rpos;
984
985   ret = GNUNET_strdup (in);
986   wpos = ret;
987   rpos = in;
988   while ('\0' != *rpos)
989   {
990     switch (tolower ((unsigned char) *rpos))
991     {
992     case 'a':
993     case 'e':
994     case 'i':
995     case 'o':
996     case 'u':
997     case ' ':
998     case '\t':
999     case '\n':
1000     case '\r':
1001       /* skip characters listed above */
1002       break;
1003     case 'b':
1004     case 'c':
1005     case 'd':
1006     case 'f':
1007     case 'g':
1008     case 'h':
1009     case 'j':
1010     case 'k':
1011     case 'l':
1012     case 'm':
1013     case 'n':
1014     case 'p':
1015     case 'r':
1016     case 's':
1017     case 't':
1018     case 'v':
1019     case 'w':
1020     case 'x':
1021     case 'y':
1022     case 'z':
1023       /* convert characters listed above to lower case */
1024       *wpos = tolower ((unsigned char) *rpos);
1025       wpos++;
1026       break;
1027     case '!':
1028     case '.':
1029     case '?':
1030     case '-':
1031       /* keep characters listed above without changes */
1032       *wpos = *rpos;
1033       wpos++;
1034       break;
1035     default:
1036       /* replace characters listed above with '_' */
1037       *wpos = '_';
1038       wpos++;
1039       break;
1040     }
1041     rpos++;
1042   }
1043   *wpos = '\0';
1044   return ret;
1045 }
1046
1047
1048 /**
1049  * Canonicalize keyword URI.  Performs operations such
1050  * as decapitalization and removal of certain characters.
1051  * (useful for search).
1052  *
1053  * @param uri the URI to canonicalize
1054  * @return canonicalized version of the URI, NULL on error
1055  */
1056 struct GNUNET_FS_Uri *
1057 GNUNET_FS_uri_ksk_canonicalize (const struct GNUNET_FS_Uri *uri)
1058 {
1059   struct GNUNET_FS_Uri *ret;
1060   unsigned int kc;
1061   unsigned int i;
1062   char **kl;
1063
1064   kc = uri->data.ksk.keywordCount;
1065   kl = GNUNET_malloc (kc * sizeof (char *));
1066   for (i = 0; i < kc; i++)
1067     kl[i] = canonicalize_keyword (uri->data.ksk.keywords[i]);
1068   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1069   ret->type = ksk;
1070   ret->data.ksk.keywordCount = kc;
1071   ret->data.ksk.keywords = kl;
1072   return ret;
1073 }
1074
1075
1076 /**
1077  * Merge the sets of keywords from two KSK URIs.
1078  * (useful for merging the canonicalized keywords with
1079  * the original keywords for sharing).
1080  *
1081  * @param u1 first uri
1082  * @param u2 second uri
1083  * @return merged URI, NULL on error
1084  */
1085 struct GNUNET_FS_Uri *
1086 GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
1087                          const struct GNUNET_FS_Uri *u2)
1088 {
1089   struct GNUNET_FS_Uri *ret;
1090   unsigned int kc;
1091   unsigned int i;
1092   unsigned int j;
1093   int found;
1094   const char *kp;
1095   char **kl;
1096
1097   if ((u1 == NULL) && (u2 == NULL))
1098     return NULL;
1099   if (u1 == NULL)
1100     return GNUNET_FS_uri_dup (u2);
1101   if (u2 == NULL)
1102     return GNUNET_FS_uri_dup (u1);
1103   if ((u1->type != ksk) || (u2->type != ksk))
1104   {
1105     GNUNET_break (0);
1106     return NULL;
1107   }
1108   kc = u1->data.ksk.keywordCount;
1109   kl = GNUNET_malloc ((kc + u2->data.ksk.keywordCount) * sizeof (char *));
1110   for (i = 0; i < u1->data.ksk.keywordCount; i++)
1111     kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]);
1112   for (i = 0; i < u2->data.ksk.keywordCount; i++)
1113   {
1114     kp = u2->data.ksk.keywords[i];
1115     found = 0;
1116     for (j = 0; j < u1->data.ksk.keywordCount; j++)
1117       if (0 == strcmp (kp + 1, kl[j] + 1))
1118       {
1119         found = 1;
1120         if (kp[0] == '+')
1121           kl[j][0] = '+';
1122         break;
1123       }
1124     if (0 == found)
1125       kl[kc++] = GNUNET_strdup (kp);
1126   }
1127   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1128   ret->type = ksk;
1129   ret->data.ksk.keywordCount = kc;
1130   ret->data.ksk.keywords = kl;
1131   return ret;
1132 }
1133
1134
1135 /**
1136  * Duplicate URI.
1137  *
1138  * @param uri the URI to duplicate
1139  * @return copy of the URI
1140  */
1141 struct GNUNET_FS_Uri *
1142 GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
1143 {
1144   struct GNUNET_FS_Uri *ret;
1145   unsigned int i;
1146
1147   if (uri == NULL)
1148     return NULL;
1149   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1150   memcpy (ret, uri, sizeof (struct GNUNET_FS_Uri));
1151   switch (ret->type)
1152   {
1153   case ksk:
1154     if (ret->data.ksk.keywordCount >=
1155         GNUNET_MAX_MALLOC_CHECKED / sizeof (char *))
1156     {
1157       GNUNET_break (0);
1158       GNUNET_free (ret);
1159       return NULL;
1160     }
1161     if (ret->data.ksk.keywordCount > 0)
1162     {
1163       ret->data.ksk.keywords =
1164           GNUNET_malloc (ret->data.ksk.keywordCount * sizeof (char *));
1165       for (i = 0; i < ret->data.ksk.keywordCount; i++)
1166         ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]);
1167     }
1168     else
1169       ret->data.ksk.keywords = NULL;    /* just to be sure */
1170     break;
1171   case sks:
1172     ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
1173     break;
1174   case loc:
1175     break;
1176   default:
1177     break;
1178   }
1179   return ret;
1180 }
1181
1182
1183 /**
1184  * Create an FS URI from a single user-supplied string of keywords.
1185  * The string is broken up at spaces into individual keywords.
1186  * Keywords that start with "+" are mandatory.  Double-quotes can
1187  * be used to prevent breaking up strings at spaces (and also
1188  * to specify non-mandatory keywords starting with "+").
1189  *
1190  * Keywords must contain a balanced number of double quotes and
1191  * double quotes can not be used in the actual keywords (for
1192  * example, the string '""foo bar""' will be turned into two
1193  * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
1194  *
1195  * @param keywords the keyword string
1196  * @param emsg where to store an error message
1197  * @return an FS URI for the given keywords, NULL
1198  *  if keywords is not legal (i.e. empty).
1199  */
1200 struct GNUNET_FS_Uri *
1201 GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg)
1202 {
1203   char **keywordarr;
1204   unsigned int num_Words;
1205   int inWord;
1206   char *pos;
1207   struct GNUNET_FS_Uri *uri;
1208   char *searchString;
1209   int saw_quote;
1210
1211   if (keywords == NULL)
1212   {
1213     *emsg = GNUNET_strdup (_("No keywords specified!\n"));
1214     GNUNET_break (0);
1215     return NULL;
1216   }
1217   searchString = GNUNET_strdup (keywords);
1218   num_Words = 0;
1219   inWord = 0;
1220   saw_quote = 0;
1221   pos = searchString;
1222   while ('\0' != *pos)
1223   {
1224     if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1225     {
1226       inWord = 0;
1227     }
1228     else if (0 == inWord)
1229     {
1230       inWord = 1;
1231       ++num_Words;
1232     }
1233     if ('"' == *pos)
1234       saw_quote = (saw_quote + 1) % 2;
1235     pos++;
1236   }
1237   if (num_Words == 0)
1238   {
1239     GNUNET_free (searchString);
1240     *emsg = GNUNET_strdup (_("No keywords specified!\n"));
1241     return NULL;
1242   }
1243   if (saw_quote != 0)
1244   {
1245     GNUNET_free (searchString);
1246     *emsg = GNUNET_strdup (_("Number of double-quotes not balanced!\n"));
1247     return NULL;
1248   }
1249   keywordarr = GNUNET_malloc (num_Words * sizeof (char *));
1250   num_Words = 0;
1251   inWord = 0;
1252   pos = searchString;
1253   while ('\0' != *pos)
1254   {
1255     if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1256     {
1257       inWord = 0;
1258       *pos = '\0';
1259     }
1260     else if (0 == inWord)
1261     {
1262       keywordarr[num_Words] = pos;
1263       inWord = 1;
1264       ++num_Words;
1265     }
1266     if ('"' == *pos)
1267       saw_quote = (saw_quote + 1) % 2;
1268     pos++;
1269   }
1270   uri =
1271       GNUNET_FS_uri_ksk_create_from_args (num_Words,
1272                                           (const char **) keywordarr);
1273   GNUNET_free (keywordarr);
1274   GNUNET_free (searchString);
1275   return uri;
1276 }
1277
1278
1279 /**
1280  * Create an FS URI from a user-supplied command line of keywords.
1281  * Arguments should start with "+" to indicate mandatory
1282  * keywords.
1283  *
1284  * @param argc number of keywords
1285  * @param argv keywords (double quotes are not required for
1286  *             keywords containing spaces; however, double
1287  *             quotes are required for keywords starting with
1288  *             "+"); there is no mechanism for having double
1289  *             quotes in the actual keywords (if the user
1290  *             did specifically specify double quotes, the
1291  *             caller should convert each double quote
1292  *             into two single quotes).
1293  * @return an FS URI for the given keywords, NULL
1294  *  if keywords is not legal (i.e. empty).
1295  */
1296 struct GNUNET_FS_Uri *
1297 GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv)
1298 {
1299   unsigned int i;
1300   struct GNUNET_FS_Uri *uri;
1301   const char *keyword;
1302   char *val;
1303   const char *r;
1304   char *w;
1305   char *emsg;
1306
1307   if (argc == 0)
1308     return NULL;
1309   /* allow URI to be given as one and only keyword and
1310    * handle accordingly */
1311   emsg = NULL;
1312   if ((argc == 1) && (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) &&
1313       (0 ==
1314        strncmp (argv[0], GNUNET_FS_URI_PREFIX, strlen (GNUNET_FS_URI_PREFIX)))
1315       && (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg))))
1316     return uri;
1317   GNUNET_free_non_null (emsg);
1318   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1319   uri->type = ksk;
1320   uri->data.ksk.keywordCount = argc;
1321   uri->data.ksk.keywords = GNUNET_malloc (argc * sizeof (char *));
1322   for (i = 0; i < argc; i++)
1323   {
1324     keyword = argv[i];
1325     if (keyword[0] == '+')
1326       val = GNUNET_strdup (keyword);
1327     else
1328       GNUNET_asprintf (&val, " %s", keyword);
1329     r = val;
1330     w = val;
1331     while ('\0' != *r)
1332     {
1333       if ('"' == *r)
1334         r++;
1335       else
1336         *(w++) = *(r++);
1337     }
1338     *w = '\0';
1339     uri->data.ksk.keywords[i] = val;
1340   }
1341   return uri;
1342 }
1343
1344
1345 /**
1346  * Test if two URIs are equal.
1347  *
1348  * @param u1 one of the URIs
1349  * @param u2 the other URI
1350  * @return GNUNET_YES if the URIs are equal
1351  */
1352 int
1353 GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
1354                           const struct GNUNET_FS_Uri *u2)
1355 {
1356   int ret;
1357   unsigned int i;
1358   unsigned int j;
1359
1360   GNUNET_assert (u1 != NULL);
1361   GNUNET_assert (u2 != NULL);
1362   if (u1->type != u2->type)
1363     return GNUNET_NO;
1364   switch (u1->type)
1365   {
1366   case chk:
1367     if (0 ==
1368         memcmp (&u1->data.chk, &u2->data.chk, sizeof (struct FileIdentifier)))
1369       return GNUNET_YES;
1370     return GNUNET_NO;
1371   case sks:
1372     if ((0 ==
1373          memcmp (&u1->data.sks.namespace, &u2->data.sks.namespace,
1374                  sizeof (GNUNET_HashCode))) &&
1375         (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier)))
1376
1377       return GNUNET_YES;
1378     return GNUNET_NO;
1379   case ksk:
1380     if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount)
1381       return GNUNET_NO;
1382     for (i = 0; i < u1->data.ksk.keywordCount; i++)
1383     {
1384       ret = GNUNET_NO;
1385       for (j = 0; j < u2->data.ksk.keywordCount; j++)
1386       {
1387         if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j]))
1388         {
1389           ret = GNUNET_YES;
1390           break;
1391         }
1392       }
1393       if (ret == GNUNET_NO)
1394         return GNUNET_NO;
1395     }
1396     return GNUNET_YES;
1397   case loc:
1398     if (memcmp
1399         (&u1->data.loc, &u2->data.loc,
1400          sizeof (struct FileIdentifier) +
1401          sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1402          sizeof (struct GNUNET_TIME_Absolute) + sizeof (unsigned short) +
1403          sizeof (unsigned short)) != 0)
1404       return GNUNET_NO;
1405     return GNUNET_YES;
1406   default:
1407     return GNUNET_NO;
1408   }
1409 }
1410
1411
1412 /**
1413  * Is this a namespace URI?
1414  *
1415  * @param uri the uri to check
1416  * @return GNUNET_YES if this is an SKS uri
1417  */
1418 int
1419 GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
1420 {
1421   return uri->type == sks;
1422 }
1423
1424
1425 /**
1426  * Get the ID of a namespace from the given
1427  * namespace URI.
1428  *
1429  * @param uri the uri to get the namespace ID from
1430  * @param nsid where to store the ID of the namespace
1431  * @return GNUNET_OK on success
1432  */
1433 int
1434 GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
1435                                  GNUNET_HashCode * nsid)
1436 {
1437   if (!GNUNET_FS_uri_test_sks (uri))
1438   {
1439     GNUNET_break (0);
1440     return GNUNET_SYSERR;
1441   }
1442   *nsid = uri->data.sks.namespace;
1443   return GNUNET_OK;
1444 }
1445
1446
1447 /**
1448  * Get the content identifier of an SKS URI.
1449  *
1450  * @param uri the sks uri
1451  * @return NULL on error (not a valid SKS URI)
1452  */
1453 char *
1454 GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
1455 {
1456   if (!GNUNET_FS_uri_test_sks (uri))
1457   {
1458     GNUNET_break (0);
1459     return NULL;
1460   }
1461   return GNUNET_strdup (uri->data.sks.identifier);
1462 }
1463
1464
1465 /**
1466  * Convert namespace URI to a human readable format
1467  * (using the namespace description, if available).
1468  *
1469  * @param cfg configuration to use
1470  * @param uri SKS uri to convert
1471  * @return NULL on error (not an SKS URI)
1472  */
1473 char *
1474 GNUNET_FS_uri_sks_to_string_fancy (struct GNUNET_CONFIGURATION_Handle *cfg,
1475                                    const struct GNUNET_FS_Uri *uri)
1476 {
1477   char *ret;
1478   char *name;
1479
1480   if (uri->type != sks)
1481     return NULL;
1482   name = GNUNET_PSEUDONYM_id_to_name (cfg, &uri->data.sks.namespace);
1483   if (name == NULL)
1484     return GNUNET_FS_uri_to_string (uri);
1485   GNUNET_asprintf (&ret, "%s: %s", name, uri->data.sks.identifier);
1486   GNUNET_free (name);
1487   return ret;
1488 }
1489
1490
1491 /**
1492  * Is this a keyword URI?
1493  *
1494  * @param uri the uri
1495  * @return GNUNET_YES if this is a KSK uri
1496  */
1497 int
1498 GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
1499 {
1500 #if EXTRA_CHECKS
1501   unsigned int i;
1502
1503   if (uri->type == ksk)
1504   {
1505     for (i = uri->data.ksk.keywordCount - 1; i >= 0; i--)
1506       GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
1507   }
1508 #endif
1509   return uri->type == ksk;
1510 }
1511
1512
1513 /**
1514  * Is this a file (or directory) URI?
1515  *
1516  * @param uri the uri to check
1517  * @return GNUNET_YES if this is a CHK uri
1518  */
1519 int
1520 GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
1521 {
1522   return uri->type == chk;
1523 }
1524
1525
1526 /**
1527  * What is the size of the file that this URI
1528  * refers to?
1529  *
1530  * @param uri the CHK URI to inspect
1531  * @return size of the file as specified in the CHK URI
1532  */
1533 uint64_t
1534 GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri * uri)
1535 {
1536   switch (uri->type)
1537   {
1538   case chk:
1539     return GNUNET_ntohll (uri->data.chk.file_length);
1540   case loc:
1541     return GNUNET_ntohll (uri->data.loc.fi.file_length);
1542   default:
1543     GNUNET_assert (0);
1544   }
1545   return 0;                     /* unreachable */
1546 }
1547
1548
1549 /**
1550  * Is this a location URI?
1551  *
1552  * @param uri the uri to check
1553  * @return GNUNET_YES if this is a LOC uri
1554  */
1555 int
1556 GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1557 {
1558   return uri->type == loc;
1559 }
1560
1561
1562 /**
1563  * Function called on each value in the meta data.
1564  * Adds it to the URI.
1565  *
1566  * @param cls URI to update
1567  * @param plugin_name name of the plugin that produced this value;
1568  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
1569  *        used in the main libextractor library and yielding
1570  *        meta data).
1571  * @param type libextractor-type describing the meta data
1572  * @param format basic format information about data
1573  * @param data_mime_type mime-type of data (not of the original file);
1574  *        can be NULL (if mime-type is not known)
1575  * @param data actual meta-data found
1576  * @param data_len number of bytes in data
1577  * @return 0 (always)
1578  */
1579 static int
1580 gather_uri_data (void *cls, const char *plugin_name,
1581                  enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format,
1582                  const char *data_mime_type, const char *data, size_t data_len)
1583 {
1584   struct GNUNET_FS_Uri *uri = cls;
1585   char *nkword;
1586   int j;
1587
1588   if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1589       (format != EXTRACTOR_METAFORMAT_C_STRING))
1590     return 0;
1591   for (j = uri->data.ksk.keywordCount - 1; j >= 0; j--)
1592     if (0 == strcmp (&uri->data.ksk.keywords[j][1], data))
1593       return GNUNET_OK;
1594   GNUNET_asprintf (&nkword, " %s",      /* space to mark as 'non mandatory' */
1595                    data);
1596   uri->data.ksk.keywords[uri->data.ksk.keywordCount++] = nkword;
1597   return 0;
1598 }
1599
1600
1601 /**
1602  * Construct a keyword-URI from meta-data (take all entries
1603  * in the meta-data and construct one large keyword URI
1604  * that lists all keywords that can be found in the meta-data).
1605  *
1606  * @param md metadata to use
1607  * @return NULL on error, otherwise a KSK URI
1608  */
1609 struct GNUNET_FS_Uri *
1610 GNUNET_FS_uri_ksk_create_from_meta_data (const struct GNUNET_CONTAINER_MetaData
1611                                          *md)
1612 {
1613   struct GNUNET_FS_Uri *ret;
1614   int ent;
1615
1616   if (md == NULL)
1617     return NULL;
1618   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1619   ret->type = ksk;
1620   ent = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
1621   if (ent > 0)
1622   {
1623     ret->data.ksk.keywords = GNUNET_malloc (sizeof (char *) * ent);
1624     GNUNET_CONTAINER_meta_data_iterate (md, &gather_uri_data, ret);
1625   }
1626   return ret;
1627 }
1628
1629
1630 /**
1631  * In URI-encoding, does the given character
1632  * need to be encoded using %-encoding?
1633  */
1634 static int
1635 needs_percent (char c)
1636 {
1637   return (!
1638           ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') ||
1639            (c == '.') || (c == '~')));
1640 }
1641
1642
1643 /**
1644  * Convert a KSK URI to a string.
1645  *
1646  * @param uri the URI to convert
1647  * @return NULL on error (i.e. keywordCount == 0)
1648  */
1649 static char *
1650 uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1651 {
1652   char **keywords;
1653   unsigned int keywordCount;
1654   size_t n;
1655   char *ret;
1656   unsigned int i;
1657   unsigned int j;
1658   unsigned int wpos;
1659   size_t slen;
1660   const char *keyword;
1661
1662   if (uri->type != ksk)
1663     return NULL;
1664   keywords = uri->data.ksk.keywords;
1665   keywordCount = uri->data.ksk.keywordCount;
1666   n = keywordCount + strlen (GNUNET_FS_URI_PREFIX) +
1667       strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1668   for (i = 0; i < keywordCount; i++)
1669   {
1670     keyword = keywords[i];
1671     slen = strlen (keyword);
1672     n += slen;
1673     for (j = 0; j < slen; j++)
1674     {
1675       if ((j == 0) && (keyword[j] == ' '))
1676       {
1677         n--;
1678         continue;               /* skip leading space */
1679       }
1680       if (needs_percent (keyword[j]))
1681         n += 2;                 /* will use %-encoding */
1682     }
1683   }
1684   ret = GNUNET_malloc (n);
1685   strcpy (ret, GNUNET_FS_URI_PREFIX);
1686   strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1687   wpos = strlen (ret);
1688   for (i = 0; i < keywordCount; i++)
1689   {
1690     keyword = keywords[i];
1691     slen = strlen (keyword);
1692     for (j = 0; j < slen; j++)
1693     {
1694       if ((j == 0) && (keyword[j] == ' '))
1695         continue;               /* skip leading space */
1696       if (needs_percent (keyword[j]))
1697       {
1698         sprintf (&ret[wpos], "%%%02X", keyword[j]);
1699         wpos += 3;
1700       }
1701       else
1702       {
1703         ret[wpos++] = keyword[j];
1704       }
1705     }
1706     if (i != keywordCount - 1)
1707       ret[wpos++] = '+';
1708   }
1709   return ret;
1710 }
1711
1712
1713 /**
1714  * Convert SKS URI to a string.
1715  *
1716  * @param uri sks uri to convert
1717  * @return NULL on error
1718  */
1719 static char *
1720 uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1721 {
1722   const GNUNET_HashCode *namespace;
1723   const char *identifier;
1724   char *ret;
1725   struct GNUNET_CRYPTO_HashAsciiEncoded ns;
1726
1727   if (uri->type != sks)
1728     return NULL;
1729   namespace = &uri->data.sks.namespace;
1730   identifier = uri->data.sks.identifier;
1731   GNUNET_CRYPTO_hash_to_enc (namespace, &ns);
1732   GNUNET_asprintf (&ret, "%s%s%s/%s", GNUNET_FS_URI_PREFIX,
1733                    GNUNET_FS_URI_SKS_INFIX, (const char *) &ns, identifier);
1734   return ret;
1735 }
1736
1737
1738 /**
1739  * Convert a CHK URI to a string.
1740  *
1741  * @param uri chk uri to convert
1742  * @return NULL on error
1743  */
1744 static char *
1745 uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1746 {
1747   const struct FileIdentifier *fi;
1748   char *ret;
1749   struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1750   struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1751
1752   if (uri->type != chk)
1753     return NULL;
1754   fi = &uri->data.chk;
1755   GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1756   GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1757
1758   GNUNET_asprintf (&ret, "%s%s%s.%s.%llu", GNUNET_FS_URI_PREFIX,
1759                    GNUNET_FS_URI_CHK_INFIX, (const char *) &keyhash,
1760                    (const char *) &queryhash, GNUNET_ntohll (fi->file_length));
1761   return ret;
1762 }
1763
1764 /**
1765  * Convert binary data to a string.
1766  *
1767  * @param data binary data to convert
1768  * @param size number of bytes in data
1769  * @return converted data
1770  */
1771 static char *
1772 bin2enc (const void *data, size_t size)
1773 {
1774   /**
1775    * 64 characters for encoding, 6 bits per character
1776    */
1777   static char *tbl =
1778       "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=";
1779
1780   size_t len;
1781   size_t pos;
1782   unsigned int bits;
1783   unsigned int hbits;
1784   char *ret;
1785
1786   GNUNET_assert (strlen (tbl) == 64);
1787   len = size * 8 / 6;
1788   if (((size * 8) % 6) != 0)
1789     len++;
1790   ret = GNUNET_malloc (len + 1);
1791   ret[len] = '\0';
1792   len = 0;
1793   bits = 0;
1794   hbits = 0;
1795   for (pos = 0; pos < size; pos++)
1796   {
1797     bits |= ((((const unsigned char *) data)[pos]) << hbits);
1798     hbits += 8;
1799     while (hbits >= 6)
1800     {
1801       ret[len++] = tbl[bits & 63];
1802       bits >>= 6;
1803       hbits -= 6;
1804     }
1805   }
1806   if (hbits > 0)
1807     ret[len] = tbl[bits & 63];
1808   return ret;
1809 }
1810
1811
1812 /**
1813  * Convert a LOC URI to a string.
1814  *
1815  * @param uri loc uri to convert
1816  * @return NULL on error
1817  */
1818 static char *
1819 uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1820 {
1821   char *ret;
1822   struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1823   struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1824   char *peerId;
1825   char *peerSig;
1826
1827   GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
1828   GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
1829   peerId =
1830       bin2enc (&uri->data.loc.peer,
1831                sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
1832   peerSig =
1833       bin2enc (&uri->data.loc.contentSignature,
1834                sizeof (struct GNUNET_CRYPTO_RsaSignature));
1835   GNUNET_asprintf (&ret, "%s%s%s.%s.%llu.%s.%s.%llu", GNUNET_FS_URI_PREFIX,
1836                    GNUNET_FS_URI_LOC_INFIX, (const char *) &keyhash,
1837                    (const char *) &queryhash,
1838                    (unsigned long long) GNUNET_ntohll (uri->data.loc.
1839                                                        fi.file_length), peerId,
1840                    peerSig,
1841                    (unsigned long long) uri->data.loc.expirationTime.abs_value);
1842   GNUNET_free (peerSig);
1843   GNUNET_free (peerId);
1844   return ret;
1845 }
1846
1847
1848 /**
1849  * Convert a URI to a UTF-8 String.
1850  *
1851  * @param uri uri to convert to a string
1852  * @return the UTF-8 string
1853  */
1854 char *
1855 GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
1856 {
1857   if (uri == NULL)
1858   {
1859     GNUNET_break (0);
1860     return NULL;
1861   }
1862   switch (uri->type)
1863   {
1864   case ksk:
1865     return uri_ksk_to_string (uri);
1866   case sks:
1867     return uri_sks_to_string (uri);
1868   case chk:
1869     return uri_chk_to_string (uri);
1870   case loc:
1871     return uri_loc_to_string (uri);
1872   default:
1873     GNUNET_break (0);
1874     return NULL;
1875   }
1876 }
1877
1878 /* end of fs_uri.c */