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