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