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