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