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