206a8adca82f5e22de6aa196beff094969fa6c9b
[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   return ret;
1044 }
1045
1046
1047 /**
1048  * Canonicalize keyword URI.  Performs operations such
1049  * as decapitalization and removal of certain characters.
1050  * (useful for search).
1051  *
1052  * @param uri the URI to canonicalize 
1053  * @return canonicalized version of the URI, NULL on error
1054  */
1055 struct GNUNET_FS_Uri *
1056 GNUNET_FS_uri_ksk_canonicalize (const struct GNUNET_FS_Uri *uri)
1057 {
1058   struct GNUNET_FS_Uri *ret;
1059   unsigned int kc;
1060   unsigned int i;
1061   char **kl;
1062
1063   kc = uri->data.ksk.keywordCount;
1064   kl = GNUNET_malloc (kc * sizeof (char *));
1065   for (i = 0; i < kc; i++)
1066     kl[i] = canonicalize_keyword (uri->data.ksk.keywords[i]);
1067   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1068   ret->type = ksk;
1069   ret->data.ksk.keywordCount = kc;
1070   ret->data.ksk.keywords = kl;
1071   return ret;
1072 }
1073
1074
1075 /**
1076  * Merge the sets of keywords from two KSK URIs.
1077  * (useful for merging the canonicalized keywords with
1078  * the original keywords for sharing).
1079  *
1080  * @param u1 first uri
1081  * @param u2 second uri
1082  * @return merged URI, NULL on error
1083  */
1084 struct GNUNET_FS_Uri *
1085 GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
1086                          const struct GNUNET_FS_Uri *u2)
1087 {
1088   struct GNUNET_FS_Uri *ret;
1089   unsigned int kc;
1090   unsigned int i;
1091   unsigned int j;
1092   int found;
1093   const char *kp;
1094   char **kl;
1095
1096   if ((u1 == NULL) && (u2 == NULL))
1097     return NULL;
1098   if (u1 == NULL)
1099     return GNUNET_FS_uri_dup (u2);
1100   if (u2 == NULL)
1101     return GNUNET_FS_uri_dup (u1);
1102   if ((u1->type != ksk) || (u2->type != ksk))
1103   {
1104     GNUNET_break (0);
1105     return NULL;
1106   }
1107   kc = u1->data.ksk.keywordCount;
1108   kl = GNUNET_malloc ((kc + u2->data.ksk.keywordCount) * sizeof (char *));
1109   for (i = 0; i < u1->data.ksk.keywordCount; i++)
1110     kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]);
1111   for (i = 0; i < u2->data.ksk.keywordCount; i++)
1112   {
1113     kp = u2->data.ksk.keywords[i];
1114     found = 0;
1115     for (j = 0; j < u1->data.ksk.keywordCount; j++)
1116       if (0 == strcmp (kp + 1, kl[j] + 1))
1117       {
1118         found = 1;
1119         if (kp[0] == '+')
1120           kl[j][0] = '+';
1121         break;
1122       }
1123     if (0 == found)
1124       kl[kc++] = GNUNET_strdup (kp);
1125   }
1126   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1127   ret->type = ksk;
1128   ret->data.ksk.keywordCount = kc;
1129   ret->data.ksk.keywords = kl;
1130   return ret;
1131 }
1132
1133
1134 /**
1135  * Duplicate URI.
1136  *
1137  * @param uri the URI to duplicate
1138  * @return copy of the URI
1139  */
1140 struct GNUNET_FS_Uri *
1141 GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
1142 {
1143   struct GNUNET_FS_Uri *ret;
1144   unsigned int i;
1145
1146   if (uri == NULL)
1147     return NULL;
1148   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1149   memcpy (ret, uri, sizeof (struct GNUNET_FS_Uri));
1150   switch (ret->type)
1151   {
1152   case ksk:
1153     if (ret->data.ksk.keywordCount >=
1154         GNUNET_MAX_MALLOC_CHECKED / sizeof (char *))
1155     {
1156       GNUNET_break (0);
1157       GNUNET_free (ret);
1158       return NULL;
1159     }
1160     if (ret->data.ksk.keywordCount > 0)
1161     {
1162       ret->data.ksk.keywords =
1163           GNUNET_malloc (ret->data.ksk.keywordCount * sizeof (char *));
1164       for (i = 0; i < ret->data.ksk.keywordCount; i++)
1165         ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]);
1166     }
1167     else
1168       ret->data.ksk.keywords = NULL;    /* just to be sure */
1169     break;
1170   case sks:
1171     ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
1172     break;
1173   case loc:
1174     break;
1175   default:
1176     break;
1177   }
1178   return ret;
1179 }
1180
1181
1182 /**
1183  * Create an FS URI from a single user-supplied string of keywords.
1184  * The string is broken up at spaces into individual keywords.
1185  * Keywords that start with "+" are mandatory.  Double-quotes can
1186  * be used to prevent breaking up strings at spaces (and also
1187  * to specify non-mandatory keywords starting with "+").
1188  *
1189  * Keywords must contain a balanced number of double quotes and
1190  * double quotes can not be used in the actual keywords (for
1191  * example, the string '""foo bar""' will be turned into two
1192  * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
1193  *
1194  * @param keywords the keyword string
1195  * @param emsg where to store an error message
1196  * @return an FS URI for the given keywords, NULL
1197  *  if keywords is not legal (i.e. empty).
1198  */
1199 struct GNUNET_FS_Uri *
1200 GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg)
1201 {
1202   char **keywordarr;
1203   unsigned int num_Words;
1204   int inWord;
1205   char *pos;
1206   struct GNUNET_FS_Uri *uri;
1207   char *searchString;
1208   int saw_quote;
1209
1210   if (keywords == NULL)
1211   {
1212     *emsg = GNUNET_strdup (_("No keywords specified!\n"));
1213     GNUNET_break (0);
1214     return NULL;
1215   }
1216   searchString = GNUNET_strdup (keywords);
1217   num_Words = 0;
1218   inWord = 0;
1219   saw_quote = 0;
1220   pos = searchString;
1221   while ('\0' != *pos)
1222   {
1223     if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1224     {
1225       inWord = 0;
1226     }
1227     else if (0 == inWord)
1228     {
1229       inWord = 1;
1230       ++num_Words;
1231     }
1232     if ('"' == *pos)
1233       saw_quote = (saw_quote + 1) % 2;
1234     pos++;
1235   }
1236   if (num_Words == 0)
1237   {
1238     GNUNET_free (searchString);
1239     *emsg = GNUNET_strdup (_("No keywords specified!\n"));
1240     return NULL;
1241   }
1242   if (saw_quote != 0)
1243   {
1244     GNUNET_free (searchString);
1245     *emsg = GNUNET_strdup (_("Number of double-quotes not balanced!\n"));
1246     return NULL;
1247   }
1248   keywordarr = GNUNET_malloc (num_Words * sizeof (char *));
1249   num_Words = 0;
1250   inWord = 0;
1251   pos = searchString;
1252   while ('\0' != *pos)
1253   {
1254     if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1255     {
1256       inWord = 0;
1257       *pos = '\0';
1258     }
1259     else if (0 == inWord)
1260     {
1261       keywordarr[num_Words] = pos;
1262       inWord = 1;
1263       ++num_Words;
1264     }
1265     if ('"' == *pos)
1266       saw_quote = (saw_quote + 1) % 2;
1267     pos++;
1268   }
1269   uri =
1270       GNUNET_FS_uri_ksk_create_from_args (num_Words,
1271                                           (const char **) keywordarr);
1272   GNUNET_free (keywordarr);
1273   GNUNET_free (searchString);
1274   return uri;
1275 }
1276
1277
1278 /**
1279  * Create an FS URI from a user-supplied command line of keywords.
1280  * Arguments should start with "+" to indicate mandatory
1281  * keywords.
1282  *
1283  * @param argc number of keywords
1284  * @param argv keywords (double quotes are not required for
1285  *             keywords containing spaces; however, double
1286  *             quotes are required for keywords starting with
1287  *             "+"); there is no mechanism for having double
1288  *             quotes in the actual keywords (if the user
1289  *             did specifically specify double quotes, the
1290  *             caller should convert each double quote
1291  *             into two single quotes).
1292  * @return an FS URI for the given keywords, NULL
1293  *  if keywords is not legal (i.e. empty).
1294  */
1295 struct GNUNET_FS_Uri *
1296 GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv)
1297 {
1298   unsigned int i;
1299   struct GNUNET_FS_Uri *uri;
1300   const char *keyword;
1301   char *val;
1302   const char *r;
1303   char *w;
1304   char *emsg;
1305
1306   if (argc == 0)
1307     return NULL;
1308   /* allow URI to be given as one and only keyword and
1309    * handle accordingly */
1310   emsg = NULL;
1311   if ((argc == 1) && (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) &&
1312       (0 ==
1313        strncmp (argv[0], GNUNET_FS_URI_PREFIX, strlen (GNUNET_FS_URI_PREFIX)))
1314       && (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg))))
1315     return uri;
1316   GNUNET_free_non_null (emsg);
1317   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1318   uri->type = ksk;
1319   uri->data.ksk.keywordCount = argc;
1320   uri->data.ksk.keywords = GNUNET_malloc (argc * sizeof (char *));
1321   for (i = 0; i < argc; i++)
1322   {
1323     keyword = argv[i];
1324     if (keyword[0] == '+')
1325       val = GNUNET_strdup (keyword);
1326     else
1327       GNUNET_asprintf (&val, " %s", keyword);
1328     r = val;
1329     w = val;
1330     while ('\0' != *r)
1331     {
1332       if ('"' == *r)
1333         r++;
1334       else
1335         *(w++) = *(r++);
1336     }
1337     *w = '\0';
1338     uri->data.ksk.keywords[i] = val;
1339   }
1340   return uri;
1341 }
1342
1343
1344 /**
1345  * Test if two URIs are equal.
1346  *
1347  * @param u1 one of the URIs
1348  * @param u2 the other URI
1349  * @return GNUNET_YES if the URIs are equal
1350  */
1351 int
1352 GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
1353                           const struct GNUNET_FS_Uri *u2)
1354 {
1355   int ret;
1356   unsigned int i;
1357   unsigned int j;
1358
1359   GNUNET_assert (u1 != NULL);
1360   GNUNET_assert (u2 != NULL);
1361   if (u1->type != u2->type)
1362     return GNUNET_NO;
1363   switch (u1->type)
1364   {
1365   case chk:
1366     if (0 ==
1367         memcmp (&u1->data.chk, &u2->data.chk, sizeof (struct FileIdentifier)))
1368       return GNUNET_YES;
1369     return GNUNET_NO;
1370   case sks:
1371     if ((0 ==
1372          memcmp (&u1->data.sks.namespace, &u2->data.sks.namespace,
1373                  sizeof (GNUNET_HashCode))) &&
1374         (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier)))
1375
1376       return GNUNET_YES;
1377     return GNUNET_NO;
1378   case ksk:
1379     if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount)
1380       return GNUNET_NO;
1381     for (i = 0; i < u1->data.ksk.keywordCount; i++)
1382     {
1383       ret = GNUNET_NO;
1384       for (j = 0; j < u2->data.ksk.keywordCount; j++)
1385       {
1386         if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j]))
1387         {
1388           ret = GNUNET_YES;
1389           break;
1390         }
1391       }
1392       if (ret == GNUNET_NO)
1393         return GNUNET_NO;
1394     }
1395     return GNUNET_YES;
1396   case loc:
1397     if (memcmp
1398         (&u1->data.loc, &u2->data.loc,
1399          sizeof (struct FileIdentifier) +
1400          sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1401          sizeof (struct GNUNET_TIME_Absolute) + sizeof (unsigned short) +
1402          sizeof (unsigned short)) != 0)
1403       return GNUNET_NO;
1404     return GNUNET_YES;
1405   default:
1406     return GNUNET_NO;
1407   }
1408 }
1409
1410
1411 /**
1412  * Is this a namespace URI?
1413  *
1414  * @param uri the uri to check
1415  * @return GNUNET_YES if this is an SKS uri
1416  */
1417 int
1418 GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
1419 {
1420   return uri->type == sks;
1421 }
1422
1423
1424 /**
1425  * Get the ID of a namespace from the given
1426  * namespace URI.
1427  *
1428  * @param uri the uri to get the namespace ID from
1429  * @param nsid where to store the ID of the namespace
1430  * @return GNUNET_OK on success
1431  */
1432 int
1433 GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
1434                                  GNUNET_HashCode * nsid)
1435 {
1436   if (!GNUNET_FS_uri_test_sks (uri))
1437   {
1438     GNUNET_break (0);
1439     return GNUNET_SYSERR;
1440   }
1441   *nsid = uri->data.sks.namespace;
1442   return GNUNET_OK;
1443 }
1444
1445
1446 /**
1447  * Get the content identifier of an SKS URI.
1448  *
1449  * @param uri the sks uri
1450  * @return NULL on error (not a valid SKS URI)
1451  */
1452 char *
1453 GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
1454 {
1455   if (!GNUNET_FS_uri_test_sks (uri))
1456   {
1457     GNUNET_break (0);
1458     return NULL;
1459   }
1460   return GNUNET_strdup (uri->data.sks.identifier);
1461 }
1462
1463
1464 /**
1465  * Convert namespace URI to a human readable format
1466  * (using the namespace description, if available).
1467  *
1468  * @param cfg configuration to use
1469  * @param uri SKS uri to convert
1470  * @return NULL on error (not an SKS URI)
1471  */
1472 char *
1473 GNUNET_FS_uri_sks_to_string_fancy (struct GNUNET_CONFIGURATION_Handle *cfg,
1474                                    const struct GNUNET_FS_Uri *uri)
1475 {
1476   char *ret;
1477   char *name;
1478
1479   if (uri->type != sks)
1480     return NULL;
1481   name = GNUNET_PSEUDONYM_id_to_name (cfg, &uri->data.sks.namespace);
1482   if (name == NULL)
1483     return GNUNET_FS_uri_to_string (uri);
1484   GNUNET_asprintf (&ret, "%s: %s", name, uri->data.sks.identifier);
1485   GNUNET_free (name);
1486   return ret;
1487 }
1488
1489
1490 /**
1491  * Is this a keyword URI?
1492  *
1493  * @param uri the uri
1494  * @return GNUNET_YES if this is a KSK uri
1495  */
1496 int
1497 GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
1498 {
1499 #if EXTRA_CHECKS
1500   unsigned int i;
1501
1502   if (uri->type == ksk)
1503   {
1504     for (i = uri->data.ksk.keywordCount - 1; i >= 0; i--)
1505       GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
1506   }
1507 #endif
1508   return uri->type == ksk;
1509 }
1510
1511
1512 /**
1513  * Is this a file (or directory) URI?
1514  *
1515  * @param uri the uri to check
1516  * @return GNUNET_YES if this is a CHK uri
1517  */
1518 int
1519 GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
1520 {
1521   return uri->type == chk;
1522 }
1523
1524
1525 /**
1526  * What is the size of the file that this URI
1527  * refers to?
1528  *
1529  * @param uri the CHK URI to inspect
1530  * @return size of the file as specified in the CHK URI
1531  */
1532 uint64_t
1533 GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri * uri)
1534 {
1535   switch (uri->type)
1536   {
1537   case chk:
1538     return GNUNET_ntohll (uri->data.chk.file_length);
1539   case loc:
1540     return GNUNET_ntohll (uri->data.loc.fi.file_length);
1541   default:
1542     GNUNET_assert (0);
1543   }
1544   return 0;                     /* unreachable */
1545 }
1546
1547
1548 /**
1549  * Is this a location URI?
1550  *
1551  * @param uri the uri to check
1552  * @return GNUNET_YES if this is a LOC uri
1553  */
1554 int
1555 GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1556 {
1557   return uri->type == loc;
1558 }
1559
1560
1561 /**
1562  * Function called on each value in the meta data.
1563  * Adds it to the URI.
1564  *
1565  * @param cls URI to update
1566  * @param plugin_name name of the plugin that produced this value;
1567  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
1568  *        used in the main libextractor library and yielding
1569  *        meta data).
1570  * @param type libextractor-type describing the meta data
1571  * @param format basic format information about data 
1572  * @param data_mime_type mime-type of data (not of the original file);
1573  *        can be NULL (if mime-type is not known)
1574  * @param data actual meta-data found
1575  * @param data_len number of bytes in data
1576  * @return 0 (always)
1577  */
1578 static int
1579 gather_uri_data (void *cls, const char *plugin_name,
1580                  enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format,
1581                  const char *data_mime_type, const char *data, size_t data_len)
1582 {
1583   struct GNUNET_FS_Uri *uri = cls;
1584   char *nkword;
1585   int j;
1586
1587   if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1588       (format != EXTRACTOR_METAFORMAT_C_STRING))
1589     return 0;
1590   for (j = uri->data.ksk.keywordCount - 1; j >= 0; j--)
1591     if (0 == strcmp (&uri->data.ksk.keywords[j][1], data))
1592       return GNUNET_OK;
1593   GNUNET_asprintf (&nkword, " %s",      /* space to mark as 'non mandatory' */
1594                    data);
1595   uri->data.ksk.keywords[uri->data.ksk.keywordCount++] = nkword;
1596   return 0;
1597 }
1598
1599
1600 /**
1601  * Construct a keyword-URI from meta-data (take all entries
1602  * in the meta-data and construct one large keyword URI
1603  * that lists all keywords that can be found in the meta-data).
1604  *
1605  * @param md metadata to use
1606  * @return NULL on error, otherwise a KSK URI
1607  */
1608 struct GNUNET_FS_Uri *
1609 GNUNET_FS_uri_ksk_create_from_meta_data (const struct GNUNET_CONTAINER_MetaData
1610                                          *md)
1611 {
1612   struct GNUNET_FS_Uri *ret;
1613   int ent;
1614
1615   if (md == NULL)
1616     return NULL;
1617   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1618   ret->type = ksk;
1619   ent = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
1620   if (ent > 0)
1621   {
1622     ret->data.ksk.keywords = GNUNET_malloc (sizeof (char *) * ent);
1623     GNUNET_CONTAINER_meta_data_iterate (md, &gather_uri_data, ret);
1624   }
1625   return ret;
1626 }
1627
1628
1629 /**
1630  * In URI-encoding, does the given character
1631  * need to be encoded using %-encoding?
1632  */
1633 static int
1634 needs_percent (char c)
1635 {
1636   return (!
1637           ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') ||
1638            (c == '.') || (c == '~')));
1639 }
1640
1641
1642 /**
1643  * Convert a KSK URI to a string.
1644  *
1645  * @param uri the URI to convert
1646  * @return NULL on error (i.e. keywordCount == 0)
1647  */
1648 static char *
1649 uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1650 {
1651   char **keywords;
1652   unsigned int keywordCount;
1653   size_t n;
1654   char *ret;
1655   unsigned int i;
1656   unsigned int j;
1657   unsigned int wpos;
1658   size_t slen;
1659   const char *keyword;
1660
1661   if (uri->type != ksk)
1662     return NULL;
1663   keywords = uri->data.ksk.keywords;
1664   keywordCount = uri->data.ksk.keywordCount;
1665   n = keywordCount + strlen (GNUNET_FS_URI_PREFIX) +
1666       strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1667   for (i = 0; i < keywordCount; i++)
1668   {
1669     keyword = keywords[i];
1670     slen = strlen (keyword);
1671     n += slen;
1672     for (j = 0; j < slen; j++)
1673     {
1674       if ((j == 0) && (keyword[j] == ' '))
1675       {
1676         n--;
1677         continue;               /* skip leading space */
1678       }
1679       if (needs_percent (keyword[j]))
1680         n += 2;                 /* will use %-encoding */
1681     }
1682   }
1683   ret = GNUNET_malloc (n);
1684   strcpy (ret, GNUNET_FS_URI_PREFIX);
1685   strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1686   wpos = strlen (ret);
1687   for (i = 0; i < keywordCount; i++)
1688   {
1689     keyword = keywords[i];
1690     slen = strlen (keyword);
1691     for (j = 0; j < slen; j++)
1692     {
1693       if ((j == 0) && (keyword[j] == ' '))
1694         continue;               /* skip leading space */
1695       if (needs_percent (keyword[j]))
1696       {
1697         sprintf (&ret[wpos], "%%%02X", keyword[j]);
1698         wpos += 3;
1699       }
1700       else
1701       {
1702         ret[wpos++] = keyword[j];
1703       }
1704     }
1705     if (i != keywordCount - 1)
1706       ret[wpos++] = '+';
1707   }
1708   return ret;
1709 }
1710
1711
1712 /**
1713  * Convert SKS URI to a string.
1714  *
1715  * @param uri sks uri to convert
1716  * @return NULL on error
1717  */
1718 static char *
1719 uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1720 {
1721   const GNUNET_HashCode *namespace;
1722   const char *identifier;
1723   char *ret;
1724   struct GNUNET_CRYPTO_HashAsciiEncoded ns;
1725
1726   if (uri->type != sks)
1727     return NULL;
1728   namespace = &uri->data.sks.namespace;
1729   identifier = uri->data.sks.identifier;
1730   GNUNET_CRYPTO_hash_to_enc (namespace, &ns);
1731   GNUNET_asprintf (&ret, "%s%s%s/%s", GNUNET_FS_URI_PREFIX,
1732                    GNUNET_FS_URI_SKS_INFIX, (const char *) &ns, identifier);
1733   return ret;
1734 }
1735
1736
1737 /**
1738  * Convert a CHK URI to a string.
1739  *
1740  * @param uri chk uri to convert
1741  * @return NULL on error
1742  */
1743 static char *
1744 uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1745 {
1746   const struct FileIdentifier *fi;
1747   char *ret;
1748   struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1749   struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1750
1751   if (uri->type != chk)
1752     return NULL;
1753   fi = &uri->data.chk;
1754   GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1755   GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1756
1757   GNUNET_asprintf (&ret, "%s%s%s.%s.%llu", GNUNET_FS_URI_PREFIX,
1758                    GNUNET_FS_URI_CHK_INFIX, (const char *) &keyhash,
1759                    (const char *) &queryhash, GNUNET_ntohll (fi->file_length));
1760   return ret;
1761 }
1762
1763 /**
1764  * Convert binary data to a string.
1765  *
1766  * @param data binary data to convert
1767  * @param size number of bytes in data
1768  * @return converted data
1769  */
1770 static char *
1771 bin2enc (const void *data, size_t size)
1772 {
1773   /**
1774    * 64 characters for encoding, 6 bits per character
1775    */
1776   static char *tbl =
1777       "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=";
1778
1779   size_t len;
1780   size_t pos;
1781   unsigned int bits;
1782   unsigned int hbits;
1783   char *ret;
1784
1785   GNUNET_assert (strlen (tbl) == 64);
1786   len = size * 8 / 6;
1787   if (((size * 8) % 6) != 0)
1788     len++;
1789   ret = GNUNET_malloc (len + 1);
1790   ret[len] = '\0';
1791   len = 0;
1792   bits = 0;
1793   hbits = 0;
1794   for (pos = 0; pos < size; pos++)
1795   {
1796     bits |= ((((const unsigned char *) data)[pos]) << hbits);
1797     hbits += 8;
1798     while (hbits >= 6)
1799     {
1800       ret[len++] = tbl[bits & 63];
1801       bits >>= 6;
1802       hbits -= 6;
1803     }
1804   }
1805   if (hbits > 0)
1806     ret[len] = tbl[bits & 63];
1807   return ret;
1808 }
1809
1810
1811 /**
1812  * Convert a LOC URI to a string.
1813  *
1814  * @param uri loc uri to convert
1815  * @return NULL on error
1816  */
1817 static char *
1818 uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1819 {
1820   char *ret;
1821   struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1822   struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1823   char *peerId;
1824   char *peerSig;
1825
1826   GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
1827   GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
1828   peerId =
1829       bin2enc (&uri->data.loc.peer,
1830                sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
1831   peerSig =
1832       bin2enc (&uri->data.loc.contentSignature,
1833                sizeof (struct GNUNET_CRYPTO_RsaSignature));
1834   GNUNET_asprintf (&ret, "%s%s%s.%s.%llu.%s.%s.%llu", GNUNET_FS_URI_PREFIX,
1835                    GNUNET_FS_URI_LOC_INFIX, (const char *) &keyhash,
1836                    (const char *) &queryhash,
1837                    (unsigned long long) GNUNET_ntohll (uri->data.loc.
1838                                                        fi.file_length), peerId,
1839                    peerSig,
1840                    (unsigned long long) uri->data.loc.expirationTime.abs_value);
1841   GNUNET_free (peerSig);
1842   GNUNET_free (peerId);
1843   return ret;
1844 }
1845
1846
1847 /**
1848  * Convert a URI to a UTF-8 String.
1849  *
1850  * @param uri uri to convert to a string
1851  * @return the UTF-8 string
1852  */
1853 char *
1854 GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
1855 {
1856   if (uri == NULL)
1857   {
1858     GNUNET_break (0);
1859     return NULL;
1860   }
1861   switch (uri->type)
1862   {
1863   case ksk:
1864     return uri_ksk_to_string (uri);
1865   case sks:
1866     return uri_sks_to_string (uri);
1867   case chk:
1868     return uri_chk_to_string (uri);
1869   case loc:
1870     return uri_loc_to_string (uri);
1871   default:
1872     GNUNET_break (0);
1873     return NULL;
1874   }
1875 }
1876
1877 /* end of fs_uri.c */