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