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