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.c2061
1 files changed, 0 insertions, 2061 deletions
diff --git a/src/fs/fs_uri.c b/src/fs/fs_uri.c
deleted file mode 100644
index 2d5566b54..000000000
--- a/src/fs/fs_uri.c
+++ /dev/null
@@ -1,2061 +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 /**
657 * FIXME: Do we want to log this?
658 */
659 msg = NULL;
660 if (NULL != (ret = uri_chk_parse (uri, &msg)))
661 return ret;
662 GNUNET_free (msg);
663 if (NULL != (ret = uri_ksk_parse (uri, &msg)))
664 return ret;
665 GNUNET_free (msg);
666 if (NULL != (ret = uri_sks_parse (uri, &msg)))
667 return ret;
668 GNUNET_free (msg);
669 if (NULL != (ret = uri_loc_parse (uri, &msg)))
670 return ret;
671 GNUNET_free (msg);
672 if (NULL != emsg)
673 *emsg = GNUNET_strdup (_ ("Unrecognized URI type"));
674 return NULL;
675}
676
677
678/**
679 * Free URI.
680 *
681 * @param uri uri to free
682 */
683void
684GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri)
685{
686 unsigned int i;
687
688 switch (uri->type)
689 {
690 case GNUNET_FS_URI_KSK:
691 for (i = 0; i < uri->data.ksk.keywordCount; i++)
692 GNUNET_free (uri->data.ksk.keywords[i]);
693 GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, 0);
694 break;
695
696 case GNUNET_FS_URI_SKS:
697 GNUNET_free (uri->data.sks.identifier);
698 break;
699
700 case GNUNET_FS_URI_LOC:
701 break;
702
703 default:
704 /* do nothing */
705 break;
706 }
707 GNUNET_free (uri);
708}
709
710
711/**
712 * How many keywords are ANDed in this keyword URI?
713 *
714 * @param uri ksk uri to get the number of keywords from
715 * @return 0 if this is not a keyword URI
716 */
717unsigned int
718GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri)
719{
720 if (uri->type != GNUNET_FS_URI_KSK)
721 return 0;
722 return uri->data.ksk.keywordCount;
723}
724
725
726/**
727 * Iterate over all keywords in this keyword URI.
728 *
729 * @param uri ksk uri to get the keywords from
730 * @param iterator function to call on each keyword
731 * @param iterator_cls closure for iterator
732 * @return -1 if this is not a keyword URI, otherwise number of
733 * keywords iterated over until iterator aborted
734 */
735int
736GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri,
737 GNUNET_FS_KeywordIterator iterator,
738 void *iterator_cls)
739{
740 unsigned int i;
741 char *keyword;
742
743 if (uri->type != GNUNET_FS_URI_KSK)
744 return -1;
745 if (NULL == iterator)
746 return uri->data.ksk.keywordCount;
747 for (i = 0; i < uri->data.ksk.keywordCount; i++)
748 {
749 keyword = uri->data.ksk.keywords[i];
750 /* first character of keyword indicates
751 * if it is mandatory or not */
752 if (GNUNET_OK != iterator (iterator_cls, &keyword[1],(keyword[0] == '+') ))
753 return i;
754 }
755 return i;
756}
757
758
759/**
760 * Add the given keyword to the set of keywords represented by the URI.
761 * Does nothing if the keyword is already present.
762 *
763 * @param uri ksk uri to modify
764 * @param keyword keyword to add
765 * @param is_mandatory is this keyword mandatory?
766 */
767void
768GNUNET_FS_uri_ksk_add_keyword (struct GNUNET_FS_Uri *uri,
769 const char *keyword,
770 int is_mandatory)
771{
772 unsigned int i;
773 const char *old;
774 char *n;
775
776 GNUNET_assert (uri->type == GNUNET_FS_URI_KSK);
777 for (i = 0; i < uri->data.ksk.keywordCount; i++)
778 {
779 old = uri->data.ksk.keywords[i];
780 if (0 == strcmp (&old[1], keyword))
781 return;
782 }
783 GNUNET_asprintf (&n, is_mandatory ? "+%s" : " %s", keyword);
784 GNUNET_array_append (uri->data.ksk.keywords, uri->data.ksk.keywordCount, n);
785}
786
787
788/**
789 * Remove the given keyword from the set of keywords represented by the URI.
790 * Does nothing if the keyword is not present.
791 *
792 * @param uri ksk uri to modify
793 * @param keyword keyword to add
794 */
795void
796GNUNET_FS_uri_ksk_remove_keyword (struct GNUNET_FS_Uri *uri,
797 const char *keyword)
798{
799 unsigned int i;
800 char *old;
801
802 GNUNET_assert (uri->type == GNUNET_FS_URI_KSK);
803 for (i = 0; i < uri->data.ksk.keywordCount; i++)
804 {
805 old = uri->data.ksk.keywords[i];
806 if (0 == strcmp (&old[1], keyword))
807 {
808 uri->data.ksk.keywords[i] =
809 uri->data.ksk.keywords[uri->data.ksk.keywordCount - 1];
810 GNUNET_array_grow (uri->data.ksk.keywords,
811 uri->data.ksk.keywordCount,
812 uri->data.ksk.keywordCount - 1);
813 GNUNET_free (old);
814 return;
815 }
816 }
817}
818
819
820/**
821 * Obtain the identity of the peer offering the data
822 *
823 * @param uri the location URI to inspect
824 * @param peer where to store the identify of the peer (presumably) offering the content
825 * @return #GNUNET_SYSERR if this is not a location URI, otherwise #GNUNET_OK
826 */
827int
828GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri,
829 struct GNUNET_PeerIdentity *peer)
830{
831 if (uri->type != GNUNET_FS_URI_LOC)
832 return GNUNET_SYSERR;
833 *peer = uri->data.loc.peer;
834 return GNUNET_OK;
835}
836
837
838/**
839 * Obtain the expiration of the LOC URI.
840 *
841 * @param uri location URI to get the expiration from
842 * @return expiration time of the URI
843 */
844struct GNUNET_TIME_Absolute
845GNUNET_FS_uri_loc_get_expiration (const struct GNUNET_FS_Uri *uri)
846{
847 GNUNET_assert (uri->type == GNUNET_FS_URI_LOC);
848 return uri->data.loc.expirationTime;
849}
850
851
852/**
853 * Obtain the URI of the content itself.
854 *
855 * @param uri location URI to get the content URI from
856 * @return NULL if argument is not a location URI
857 */
858struct GNUNET_FS_Uri *
859GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri)
860{
861 struct GNUNET_FS_Uri *ret;
862
863 if (uri->type != GNUNET_FS_URI_LOC)
864 return NULL;
865 ret = GNUNET_new (struct GNUNET_FS_Uri);
866 ret->type = GNUNET_FS_URI_CHK;
867 ret->data.chk = uri->data.loc.fi;
868 return ret;
869}
870
871
872/**
873 * Construct a location URI (this peer will be used for the location).
874 * This function should only be called from within gnunet-service-fs,
875 * as it requires the peer's private key which is generally unavailable
876 * to processes directly under the user's control. However, for
877 * testing and as it logically fits under URIs, it is in this API.
878 *
879 * @param base_uri content offered by the sender
880 * @param sign_key private key of the peer
881 * @param expiration_time how long will the content be offered?
882 * @return the location URI, NULL on error
883 */
884struct GNUNET_FS_Uri *
885GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *base_uri,
886 const struct GNUNET_CRYPTO_EddsaPrivateKey *sign_key,
887 struct GNUNET_TIME_Absolute expiration_time)
888{
889 struct GNUNET_FS_Uri *uri;
890 struct GNUNET_CRYPTO_EddsaPublicKey my_public_key;
891 struct LocUriAssembly ass;
892 struct GNUNET_TIME_Absolute et;
893
894 if (GNUNET_FS_URI_CHK != base_uri->type)
895 return NULL;
896 /* we round expiration time to full seconds for SKS URIs */
897 et.abs_value_us = (expiration_time.abs_value_us / 1000000LL) * 1000000LL;
898 GNUNET_CRYPTO_eddsa_key_get_public (sign_key, &my_public_key);
899 ass.purpose.size = htonl (sizeof(struct LocUriAssembly));
900 ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
901 ass.exptime = GNUNET_TIME_absolute_hton (et);
902 ass.fi = base_uri->data.chk;
903 ass.peer.public_key = my_public_key;
904 uri = GNUNET_new (struct GNUNET_FS_Uri);
905 uri->type = GNUNET_FS_URI_LOC;
906 uri->data.loc.fi = base_uri->data.chk;
907 uri->data.loc.expirationTime = et;
908 uri->data.loc.peer.public_key = my_public_key;
909 GNUNET_CRYPTO_eddsa_sign (sign_key,
910 &ass,
911 &uri->data.loc.contentSignature);
912 return uri;
913}
914
915
916/**
917 * Create an SKS URI from a namespace ID and an identifier.
918 *
919 * @param ns namespace ID
920 * @param id identifier
921 * @return an FS URI for the given namespace and identifier
922 */
923struct GNUNET_FS_Uri *
924GNUNET_FS_uri_sks_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
925 const char *id)
926{
927 struct GNUNET_FS_Uri *ns_uri;
928
929 ns_uri = GNUNET_new (struct GNUNET_FS_Uri);
930 ns_uri->type = GNUNET_FS_URI_SKS;
931 ns_uri->data.sks.ns = *ns;
932 ns_uri->data.sks.identifier = GNUNET_strdup (id);
933 return ns_uri;
934}
935
936
937/**
938 * Merge the sets of keywords from two KSK URIs.
939 * (useful for merging the canonicalized keywords with
940 * the original keywords for sharing).
941 *
942 * @param u1 first uri
943 * @param u2 second uri
944 * @return merged URI, NULL on error
945 */
946struct GNUNET_FS_Uri *
947GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
948 const struct GNUNET_FS_Uri *u2)
949{
950 struct GNUNET_FS_Uri *ret;
951 unsigned int kc;
952 unsigned int i;
953 unsigned int j;
954 int found;
955 const char *kp;
956 char **kl;
957
958 if ((u1 == NULL) && (u2 == NULL))
959 return NULL;
960 if (u1 == NULL)
961 return GNUNET_FS_uri_dup (u2);
962 if (u2 == NULL)
963 return GNUNET_FS_uri_dup (u1);
964 if ((u1->type != GNUNET_FS_URI_KSK) || (u2->type != GNUNET_FS_URI_KSK))
965 {
966 GNUNET_break (0);
967 return NULL;
968 }
969 kc = u1->data.ksk.keywordCount;
970 kl = GNUNET_new_array (kc + u2->data.ksk.keywordCount, char *);
971 for (i = 0; i < u1->data.ksk.keywordCount; i++)
972 kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]);
973 for (i = 0; i < u2->data.ksk.keywordCount; i++)
974 {
975 kp = u2->data.ksk.keywords[i];
976 found = 0;
977 for (j = 0; j < u1->data.ksk.keywordCount; j++)
978 if (0 == strcmp (kp + 1, kl[j] + 1))
979 {
980 found = 1;
981 if (kp[0] == '+')
982 kl[j][0] = '+';
983 break;
984 }
985 if (0 == found)
986 kl[kc++] = GNUNET_strdup (kp);
987 }
988 ret = GNUNET_new (struct GNUNET_FS_Uri);
989 ret->type = GNUNET_FS_URI_KSK;
990 ret->data.ksk.keywordCount = kc;
991 ret->data.ksk.keywords = kl;
992 return ret;
993}
994
995
996/**
997 * Duplicate URI.
998 *
999 * @param uri the URI to duplicate
1000 * @return copy of the URI
1001 */
1002struct GNUNET_FS_Uri *
1003GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
1004{
1005 struct GNUNET_FS_Uri *ret;
1006 unsigned int i;
1007
1008 if (uri == NULL)
1009 return NULL;
1010 ret = GNUNET_new (struct GNUNET_FS_Uri);
1011 GNUNET_memcpy (ret, uri, sizeof(struct GNUNET_FS_Uri));
1012 switch (ret->type)
1013 {
1014 case GNUNET_FS_URI_KSK:
1015 if (ret->data.ksk.keywordCount >=
1016 GNUNET_MAX_MALLOC_CHECKED / sizeof(char *))
1017 {
1018 GNUNET_break (0);
1019 GNUNET_free (ret);
1020 return NULL;
1021 }
1022 if (ret->data.ksk.keywordCount > 0)
1023 {
1024 ret->data.ksk.keywords =
1025 GNUNET_new_array (ret->data.ksk.keywordCount, char *);
1026 for (i = 0; i < ret->data.ksk.keywordCount; i++)
1027 ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]);
1028 }
1029 else
1030 ret->data.ksk.keywords = NULL; /* just to be sure */
1031 break;
1032
1033 case GNUNET_FS_URI_SKS:
1034 ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
1035 break;
1036
1037 case GNUNET_FS_URI_LOC:
1038 break;
1039
1040 default:
1041 break;
1042 }
1043 return ret;
1044}
1045
1046
1047/**
1048 * Create an FS URI from a single user-supplied string of keywords.
1049 * The string is broken up at spaces into individual keywords.
1050 * Keywords that start with "+" are mandatory. Double-quotes can
1051 * be used to prevent breaking up strings at spaces (and also
1052 * to specify non-mandatory keywords starting with "+").
1053 *
1054 * Keywords must contain a balanced number of double quotes and
1055 * double quotes can not be used in the actual keywords (for
1056 * example, the string '""foo bar""' will be turned into two
1057 * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
1058 *
1059 * @param keywords the keyword string
1060 * @param emsg where to store an error message
1061 * @return an FS URI for the given keywords, NULL
1062 * if keywords is not legal (i.e. empty).
1063 */
1064struct GNUNET_FS_Uri *
1065GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg)
1066{
1067 char **keywordarr;
1068 unsigned int num_Words;
1069 int inWord;
1070 char *pos;
1071 struct GNUNET_FS_Uri *uri;
1072 char *searchString;
1073 int saw_quote;
1074
1075 if (keywords == NULL)
1076 {
1077 *emsg = GNUNET_strdup (_ ("No keywords specified!\n"));
1078 GNUNET_break (0);
1079 return NULL;
1080 }
1081 searchString = GNUNET_strdup (keywords);
1082 num_Words = 0;
1083 inWord = 0;
1084 saw_quote = 0;
1085 pos = searchString;
1086 while ('\0' != *pos)
1087 {
1088 if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1089 {
1090 inWord = 0;
1091 }
1092 else if (0 == inWord)
1093 {
1094 inWord = 1;
1095 ++num_Words;
1096 }
1097 if ('"' == *pos)
1098 saw_quote = (saw_quote + 1) % 2;
1099 pos++;
1100 }
1101 if (num_Words == 0)
1102 {
1103 GNUNET_free (searchString);
1104 *emsg = GNUNET_strdup (_ ("No keywords specified!\n"));
1105 return NULL;
1106 }
1107 if (saw_quote != 0)
1108 {
1109 GNUNET_free (searchString);
1110 *emsg = GNUNET_strdup (_ ("Number of double-quotes not balanced!\n"));
1111 return NULL;
1112 }
1113 keywordarr = GNUNET_new_array (num_Words, char *);
1114 num_Words = 0;
1115 inWord = 0;
1116 pos = searchString;
1117 while ('\0' != *pos)
1118 {
1119 if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1120 {
1121 inWord = 0;
1122 *pos = '\0';
1123 }
1124 else if (0 == inWord)
1125 {
1126 keywordarr[num_Words] = pos;
1127 inWord = 1;
1128 ++num_Words;
1129 }
1130 if ('"' == *pos)
1131 saw_quote = (saw_quote + 1) % 2;
1132 pos++;
1133 }
1134 uri =
1135 GNUNET_FS_uri_ksk_create_from_args (num_Words, (const char **) keywordarr);
1136 GNUNET_free (keywordarr);
1137 GNUNET_free (searchString);
1138 return uri;
1139}
1140
1141
1142/**
1143 * Create an FS URI from a user-supplied command line of keywords.
1144 * Arguments should start with "+" to indicate mandatory
1145 * keywords.
1146 *
1147 * @param argc number of keywords
1148 * @param argv keywords (double quotes are not required for
1149 * keywords containing spaces; however, double
1150 * quotes are required for keywords starting with
1151 * "+"); there is no mechanism for having double
1152 * quotes in the actual keywords (if the user
1153 * did specifically specify double quotes, the
1154 * caller should convert each double quote
1155 * into two single quotes).
1156 * @return an FS URI for the given keywords, NULL
1157 * if keywords is not legal (i.e. empty).
1158 */
1159struct GNUNET_FS_Uri *
1160GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv)
1161{
1162 unsigned int i;
1163 struct GNUNET_FS_Uri *uri;
1164 const char *keyword;
1165 char *val;
1166 const char *r;
1167 char *w;
1168 char *emsg;
1169
1170 if (argc == 0)
1171 return NULL;
1172 /* allow URI to be given as one and only keyword and
1173 * handle accordingly */
1174 emsg = NULL;
1175 if ((argc == 1) && (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) &&
1176 (0 == strncmp (argv[0],
1177 GNUNET_FS_URI_PREFIX,
1178 strlen (GNUNET_FS_URI_PREFIX))) &&
1179 (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg))))
1180 return uri;
1181 GNUNET_free (emsg);
1182 uri = GNUNET_new (struct GNUNET_FS_Uri);
1183 uri->type = GNUNET_FS_URI_KSK;
1184 uri->data.ksk.keywordCount = argc;
1185 uri->data.ksk.keywords = GNUNET_new_array (argc, char *);
1186 for (i = 0; i < argc; i++)
1187 {
1188 keyword = argv[i];
1189 if (keyword[0] == '+')
1190 val = GNUNET_strdup (keyword);
1191 else
1192 GNUNET_asprintf (&val, " %s", keyword);
1193 r = val;
1194 w = val;
1195 while ('\0' != *r)
1196 {
1197 if ('"' == *r)
1198 r++;
1199 else
1200 *(w++) = *(r++);
1201 }
1202 *w = '\0';
1203 uri->data.ksk.keywords[i] = val;
1204 }
1205 return uri;
1206}
1207
1208
1209/**
1210 * Test if two URIs are equal.
1211 *
1212 * @param u1 one of the URIs
1213 * @param u2 the other URI
1214 * @return #GNUNET_YES if the URIs are equal
1215 */
1216int
1217GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
1218 const struct GNUNET_FS_Uri *u2)
1219{
1220 int ret;
1221 unsigned int i;
1222 unsigned int j;
1223
1224 GNUNET_assert (u1 != NULL);
1225 GNUNET_assert (u2 != NULL);
1226 if (u1->type != u2->type)
1227 return GNUNET_NO;
1228 switch (u1->type)
1229 {
1230 case GNUNET_FS_URI_CHK:
1231 if (0 ==
1232 memcmp (&u1->data.chk, &u2->data.chk, sizeof(struct FileIdentifier)))
1233 return GNUNET_YES;
1234 return GNUNET_NO;
1235
1236 case GNUNET_FS_URI_SKS:
1237 if ((0 == memcmp (&u1->data.sks.ns,
1238 &u2->data.sks.ns,
1239 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey))) &&
1240 (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier)))
1241
1242 return GNUNET_YES;
1243 return GNUNET_NO;
1244
1245 case GNUNET_FS_URI_KSK:
1246 if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount)
1247 return GNUNET_NO;
1248 for (i = 0; i < u1->data.ksk.keywordCount; i++)
1249 {
1250 ret = GNUNET_NO;
1251 for (j = 0; j < u2->data.ksk.keywordCount; j++)
1252 {
1253 if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j]))
1254 {
1255 ret = GNUNET_YES;
1256 break;
1257 }
1258 }
1259 if (ret == GNUNET_NO)
1260 return GNUNET_NO;
1261 }
1262 return GNUNET_YES;
1263
1264 case GNUNET_FS_URI_LOC:
1265 if (memcmp (&u1->data.loc,
1266 &u2->data.loc,
1267 sizeof(struct FileIdentifier)
1268 + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
1269 + sizeof(struct GNUNET_TIME_Absolute)
1270 + sizeof(unsigned short) + sizeof(unsigned short)) != 0)
1271 return GNUNET_NO;
1272 return GNUNET_YES;
1273
1274 default:
1275 return GNUNET_NO;
1276 }
1277}
1278
1279
1280/**
1281 * Is this a namespace URI?
1282 *
1283 * @param uri the uri to check
1284 * @return #GNUNET_YES if this is an SKS uri
1285 */
1286int
1287GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
1288{
1289 return uri->type == GNUNET_FS_URI_SKS;
1290}
1291
1292
1293/**
1294 * Get the ID of a namespace from the given
1295 * namespace URI.
1296 *
1297 * @param uri the uri to get the namespace ID from
1298 * @param pseudonym where to store the ID of the namespace
1299 * @return #GNUNET_OK on success
1300 */
1301int
1302GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
1303 struct GNUNET_CRYPTO_EcdsaPublicKey *pseudonym)
1304{
1305 if (! GNUNET_FS_uri_test_sks (uri))
1306 {
1307 GNUNET_break (0);
1308 return GNUNET_SYSERR;
1309 }
1310 *pseudonym = uri->data.sks.ns;
1311 return GNUNET_OK;
1312}
1313
1314
1315/**
1316 * Get the content identifier of an SKS URI.
1317 *
1318 * @param uri the sks uri
1319 * @return NULL on error (not a valid SKS URI)
1320 */
1321char *
1322GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
1323{
1324 if (! GNUNET_FS_uri_test_sks (uri))
1325 {
1326 GNUNET_break (0);
1327 return NULL;
1328 }
1329 return GNUNET_strdup (uri->data.sks.identifier);
1330}
1331
1332
1333/**
1334 * Is this a keyword URI?
1335 *
1336 * @param uri the uri
1337 * @return #GNUNET_YES if this is a KSK uri
1338 */
1339int
1340GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
1341{
1342#if EXTRA_CHECKS
1343 unsigned int i;
1344
1345 if (uri->type == GNUNET_FS_URI_KSK)
1346 {
1347 for (i = 0; i < uri->data.ksk.keywordCount; i++)
1348 GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
1349 }
1350#endif
1351 return uri->type == GNUNET_FS_URI_KSK;
1352}
1353
1354
1355/**
1356 * Is this a file (or directory) URI?
1357 *
1358 * @param uri the uri to check
1359 * @return #GNUNET_YES if this is a CHK uri
1360 */
1361int
1362GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
1363{
1364 return uri->type == GNUNET_FS_URI_CHK;
1365}
1366
1367
1368/**
1369 * What is the size of the file that this URI
1370 * refers to?
1371 *
1372 * @param uri the CHK URI to inspect
1373 * @return size of the file as specified in the CHK URI
1374 */
1375uint64_t
1376GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri *uri)
1377{
1378 switch (uri->type)
1379 {
1380 case GNUNET_FS_URI_CHK:
1381 return GNUNET_ntohll (uri->data.chk.file_length);
1382
1383 case GNUNET_FS_URI_LOC:
1384 return GNUNET_ntohll (uri->data.loc.fi.file_length);
1385
1386 default:
1387 GNUNET_assert (0);
1388 }
1389 return 0; /* unreachable */
1390}
1391
1392
1393/**
1394 * Is this a location URI?
1395 *
1396 * @param uri the uri to check
1397 * @return #GNUNET_YES if this is a LOC uri
1398 */
1399int
1400GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1401{
1402 return uri->type == GNUNET_FS_URI_LOC;
1403}
1404
1405
1406/**
1407 * Add a keyword as non-mandatory (with ' '-prefix) to the
1408 * given keyword list at offset 'index'. The array is
1409 * guaranteed to be long enough.
1410 *
1411 * @param s keyword to add
1412 * @param array array to add the keyword to
1413 * @param index offset where to add the keyword
1414 */
1415static void
1416insert_non_mandatory_keyword (const char *s, char **array, int index)
1417{
1418 char *nkword;
1419
1420 GNUNET_asprintf (&nkword,
1421 " %s", /* space to mark as 'non mandatory' */
1422 s);
1423 array[index] = nkword;
1424}
1425
1426
1427/**
1428 * Test if the given keyword @a s is already present in the
1429 * given array, ignoring the '+'-mandatory prefix in the array.
1430 *
1431 * @param s keyword to test
1432 * @param array keywords to test against, with ' ' or '+' prefix to ignore
1433 * @param array_length length of the @a array
1434 * @return #GNUNET_YES if the keyword exists, #GNUNET_NO if not
1435 */
1436static int
1437find_duplicate (const char *s, const char **array, int array_length)
1438{
1439 int j;
1440
1441 for (j = array_length - 1; j >= 0; j--)
1442 if (0 == strcmp (&array[j][1], s))
1443 return GNUNET_YES;
1444 return GNUNET_NO;
1445}
1446
1447
1448/**
1449 * FIXME: comment
1450 */
1451static char *
1452normalize_metadata (enum EXTRACTOR_MetaFormat format,
1453 const char *data,
1454 size_t data_len)
1455{
1456 uint8_t *free_str = NULL;
1457 uint8_t *str_to_normalize = (uint8_t *) data;
1458 uint8_t *normalized;
1459 size_t r_len;
1460
1461 if (str_to_normalize == NULL)
1462 return NULL;
1463 /* Don't trust libextractor */
1464 if (format == EXTRACTOR_METAFORMAT_UTF8)
1465 {
1466 free_str = (uint8_t *) u8_check ((const uint8_t *) data, data_len);
1467 if (free_str == NULL)
1468 free_str = NULL;
1469 else
1470 format = EXTRACTOR_METAFORMAT_C_STRING;
1471 }
1472 if (format == EXTRACTOR_METAFORMAT_C_STRING)
1473 {
1474 free_str = u8_strconv_from_encoding (data,
1475 locale_charset (),
1476 iconveh_escape_sequence);
1477 if (free_str == NULL)
1478 return NULL;
1479 }
1480
1481 normalized = u8_tolower (str_to_normalize,
1482 strlen ((char *) str_to_normalize),
1483 NULL,
1484 UNINORM_NFD,
1485 NULL,
1486 &r_len);
1487 /* free_str is allocated by libunistring internally, use free() */
1488 if (free_str != NULL)
1489 free (free_str);
1490 if (normalized != NULL)
1491 {
1492 /* u8_tolower allocates a non-NULL-terminated string! */
1493 free_str = GNUNET_malloc (r_len + 1);
1494 GNUNET_memcpy (free_str, normalized, r_len);
1495 free_str[r_len] = '\0';
1496 free (normalized);
1497 normalized = free_str;
1498 }
1499 return (char *) normalized;
1500}
1501
1502
1503/**
1504 * Counts the number of UTF-8 characters (not bytes) in the string,
1505 * returns that count.
1506 */
1507static size_t
1508u8_strcount (const uint8_t *s)
1509{
1510 size_t count;
1511 ucs4_t c;
1512
1513 GNUNET_assert (s != NULL);
1514 if (s[0] == 0)
1515 return 0;
1516 for (count = 0; s != NULL; count++)
1517 s = u8_next (&c, s);
1518 return count - 1;
1519}
1520
1521
1522/**
1523 * Break the filename up by matching [], () and {} pairs to make
1524 * keywords. In case of nesting parentheses only the inner pair counts.
1525 * You can't escape parentheses to scan something like "[blah\{foo]" to
1526 * make a "blah{foo" keyword, this function is only a heuristic!
1527 *
1528 * @param s string to break down.
1529 * @param array array to fill with enclosed tokens. If NULL, then tokens
1530 * are only counted.
1531 * @param index index at which to start filling the array (entries prior
1532 * to it are used to check for duplicates). ignored if @a array == NULL.
1533 * @return number of tokens counted (including duplicates), or number of
1534 * tokens extracted (excluding duplicates). 0 if there are no
1535 * matching parens in the string (when counting), or when all tokens
1536 * were duplicates (when extracting).
1537 */
1538static int
1539get_keywords_from_parens (const char *s, char **array, int index)
1540{
1541 int count = 0;
1542 char *open_paren;
1543 char *close_paren;
1544 char *ss;
1545 char tmp;
1546
1547 if (NULL == s)
1548 return 0;
1549 ss = GNUNET_strdup (s);
1550 open_paren = ss - 1;
1551 while (NULL != (open_paren = strpbrk (open_paren + 1, "[{(")))
1552 {
1553 int match = 0;
1554
1555 close_paren = strpbrk (open_paren + 1, "]})");
1556 if (NULL == close_paren)
1557 continue;
1558 switch (open_paren[0])
1559 {
1560 case '[':
1561 if (']' == close_paren[0])
1562 match = 1;
1563 break;
1564
1565 case '{':
1566 if ('}' == close_paren[0])
1567 match = 1;
1568 break;
1569
1570 case '(':
1571 if (')' == close_paren[0])
1572 match = 1;
1573 break;
1574
1575 default:
1576 break;
1577 }
1578 if (match && (close_paren - open_paren > 1))
1579 {
1580 tmp = close_paren[0];
1581 close_paren[0] = '\0';
1582 /* Keywords must be at least 3 characters long */
1583 if (u8_strcount ((const uint8_t *) &open_paren[1]) <= 2)
1584 {
1585 close_paren[0] = tmp;
1586 continue;
1587 }
1588 if (NULL != array)
1589 {
1590 char *normalized;
1591 if (GNUNET_NO == find_duplicate ((const char *) &open_paren[1],
1592 (const char **) array,
1593 index + count))
1594 {
1595 insert_non_mandatory_keyword ((const char *) &open_paren[1],
1596 array,
1597 index + count);
1598 count++;
1599 }
1600 normalized = normalize_metadata (EXTRACTOR_METAFORMAT_UTF8,
1601 &open_paren[1],
1602 close_paren - &open_paren[1]);
1603 if (normalized != NULL)
1604 {
1605 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1606 (const char **) array,
1607 index + count))
1608 {
1609 insert_non_mandatory_keyword ((const char *) normalized,
1610 array,
1611 index + count);
1612 count++;
1613 }
1614 GNUNET_free (normalized);
1615 }
1616 }
1617 else
1618 count++;
1619 close_paren[0] = tmp;
1620 }
1621 }
1622 GNUNET_free (ss);
1623 return count;
1624}
1625
1626
1627/**
1628 * Where to break up keywords
1629 */
1630#define TOKENS "_. /-!?#&+@\"\'\\;:,()[]{}$<>|"
1631
1632/**
1633 * Break the filename up by TOKENS to make
1634 * keywords.
1635 *
1636 * @param s string to break down.
1637 * @param array array to fill with tokens. If NULL, then tokens are only
1638 * counted.
1639 * @param index index at which to start filling the array (entries prior
1640 * to it are used to check for duplicates). ignored if @a array == NULL.
1641 * @return number of tokens (>1) counted (including duplicates), or number of
1642 * tokens extracted (excluding duplicates). 0 if there are no
1643 * separators in the string (when counting), or when all tokens were
1644 * duplicates (when extracting).
1645 */
1646static int
1647get_keywords_from_tokens (const char *s, char **array, int index)
1648{
1649 char *p;
1650 char *ss;
1651 int seps = 0;
1652
1653 ss = GNUNET_strdup (s);
1654 for (p = strtok (ss, TOKENS); p != NULL; p = strtok (NULL, TOKENS))
1655 {
1656 /* Keywords must be at least 3 characters long */
1657 if (u8_strcount ((const uint8_t *) p) <= 2)
1658 continue;
1659 if (NULL != array)
1660 {
1661 char *normalized;
1662 if (GNUNET_NO == find_duplicate (p, (const char **) array, index + seps))
1663 {
1664 insert_non_mandatory_keyword (p, array, index + seps);
1665 seps++;
1666 }
1667 normalized =
1668 normalize_metadata (EXTRACTOR_METAFORMAT_UTF8, p, strlen (p));
1669 if (normalized != NULL)
1670 {
1671 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1672 (const char **) array,
1673 index + seps))
1674 {
1675 insert_non_mandatory_keyword ((const char *) normalized,
1676 array,
1677 index + seps);
1678 seps++;
1679 }
1680 GNUNET_free (normalized);
1681 }
1682 }
1683 else
1684 seps++;
1685 }
1686 GNUNET_free (ss);
1687 return seps;
1688}
1689
1690
1691#undef TOKENS
1692
1693
1694/**
1695 * Function called on each value in the meta data.
1696 * Adds it to the URI.
1697 *
1698 * @param cls URI to update
1699 * @param plugin_name name of the plugin that produced this value;
1700 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
1701 * used in the main libextractor library and yielding
1702 * meta data).
1703 * @param type libextractor-type describing the meta data
1704 * @param format basic format information about data
1705 * @param data_mime_type mime-type of data (not of the original file);
1706 * can be NULL (if mime-type is not known)
1707 * @param data actual meta-data found
1708 * @param data_len number of bytes in @a data
1709 * @return 0 (always)
1710 */
1711static int
1712gather_uri_data (void *cls,
1713 const char *plugin_name,
1714 enum EXTRACTOR_MetaType type,
1715 enum EXTRACTOR_MetaFormat format,
1716 const char *data_mime_type,
1717 const char *data,
1718 size_t data_len)
1719{
1720 struct GNUNET_FS_Uri *uri = cls;
1721 char *normalized_data;
1722 const char *sep;
1723
1724 if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1725 (format != EXTRACTOR_METAFORMAT_C_STRING))
1726 return 0;
1727 /* Keywords must be at least 3 characters long
1728 * If given non-utf8 string it will, most likely, find it to be invalid,
1729 * and will return the length of its valid part, skipping the keyword.
1730 * If it does - fix the extractor, not this check!
1731 */if (u8_strcount ((const uint8_t *) data) <= 2)
1732 return 0;
1733 if ((EXTRACTOR_METATYPE_MIMETYPE == type) &&
1734 (NULL != (sep = memchr (data, '/', data_len))) && (sep != data))
1735 {
1736 char *xtra;
1737
1738 GNUNET_asprintf (&xtra, "mimetype:%.*s", (int) (sep - data), data);
1739 if (! find_duplicate (xtra,
1740 (const char **) uri->data.ksk.keywords,
1741 uri->data.ksk.keywordCount))
1742 {
1743 insert_non_mandatory_keyword (xtra,
1744 uri->data.ksk.keywords,
1745 uri->data.ksk.keywordCount);
1746 uri->data.ksk.keywordCount++;
1747 }
1748 GNUNET_free (xtra);
1749 }
1750
1751 normalized_data = normalize_metadata (format, data, data_len);
1752 if (! find_duplicate (data,
1753 (const char **) uri->data.ksk.keywords,
1754 uri->data.ksk.keywordCount))
1755 {
1756 insert_non_mandatory_keyword (data,
1757 uri->data.ksk.keywords,
1758 uri->data.ksk.keywordCount);
1759 uri->data.ksk.keywordCount++;
1760 }
1761 if (NULL != normalized_data)
1762 {
1763 if (! find_duplicate (normalized_data,
1764 (const char **) uri->data.ksk.keywords,
1765 uri->data.ksk.keywordCount))
1766 {
1767 insert_non_mandatory_keyword (normalized_data,
1768 uri->data.ksk.keywords,
1769 uri->data.ksk.keywordCount);
1770 uri->data.ksk.keywordCount++;
1771 }
1772 GNUNET_free (normalized_data);
1773 }
1774 return 0;
1775}
1776
1777
1778/**
1779 * Construct a keyword-URI from meta-data (take all entries
1780 * in the meta-data and construct one large keyword URI
1781 * that lists all keywords that can be found in the meta-data).
1782 *
1783 * @param md metadata to use
1784 * @return NULL on error, otherwise a KSK URI
1785 */
1786struct GNUNET_FS_Uri *
1787GNUNET_FS_uri_ksk_create_from_meta_data (
1788 const struct GNUNET_CONTAINER_MetaData *md)
1789{
1790 struct GNUNET_FS_Uri *ret;
1791 char *filename;
1792 char *full_name = NULL;
1793 char *ss;
1794 int ent;
1795 int tok_keywords = 0;
1796 int paren_keywords = 0;
1797
1798 if (NULL == md)
1799 return NULL;
1800 ret = GNUNET_new (struct GNUNET_FS_Uri);
1801 ret->type = GNUNET_FS_URI_KSK;
1802 ent = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
1803 if (ent > 0)
1804 {
1805 full_name = GNUNET_CONTAINER_meta_data_get_first_by_types (
1806 md,
1807 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
1808 -1);
1809 if (NULL != full_name)
1810 {
1811 filename = full_name;
1812 while (NULL != (ss = strstr (filename, DIR_SEPARATOR_STR)))
1813 filename = ss + 1;
1814 tok_keywords = get_keywords_from_tokens (filename, NULL, 0);
1815 paren_keywords = get_keywords_from_parens (filename, NULL, 0);
1816 }
1817 /* x3 because there might be a normalized variant of every keyword,
1818 plus theoretically one more for mime... */
1819 ret->data.ksk.keywords =
1820 GNUNET_new_array ((ent + tok_keywords + paren_keywords) * 3, char *);
1821 GNUNET_CONTAINER_meta_data_iterate (md, &gather_uri_data, ret);
1822 }
1823 if (tok_keywords > 0)
1824 ret->data.ksk.keywordCount +=
1825 get_keywords_from_tokens (filename,
1826 ret->data.ksk.keywords,
1827 ret->data.ksk.keywordCount);
1828 if (paren_keywords > 0)
1829 ret->data.ksk.keywordCount +=
1830 get_keywords_from_parens (filename,
1831 ret->data.ksk.keywords,
1832 ret->data.ksk.keywordCount);
1833 if (ent > 0)
1834 GNUNET_free (full_name);
1835 return ret;
1836}
1837
1838
1839/**
1840 * In URI-encoding, does the given character
1841 * need to be encoded using %-encoding?
1842 */
1843static int
1844needs_percent (char c)
1845{
1846 return(! ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') ||
1847 (c == '.') || (c == '~')));
1848}
1849
1850
1851/**
1852 * Convert a KSK URI to a string.
1853 *
1854 * @param uri the URI to convert
1855 * @return NULL on error (i.e. keywordCount == 0)
1856 */
1857static char *
1858uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1859{
1860 char **keywords;
1861 unsigned int keywordCount;
1862 size_t n;
1863 char *ret;
1864 unsigned int i;
1865 unsigned int j;
1866 unsigned int wpos;
1867 size_t slen;
1868 const char *keyword;
1869
1870 if (uri->type != GNUNET_FS_URI_KSK)
1871 return NULL;
1872 keywords = uri->data.ksk.keywords;
1873 keywordCount = uri->data.ksk.keywordCount;
1874 n = keywordCount + strlen (GNUNET_FS_URI_PREFIX)
1875 + strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1876 for (i = 0; i < keywordCount; i++)
1877 {
1878 keyword = keywords[i];
1879 slen = strlen (keyword);
1880 n += slen;
1881 for (j = 0; j < slen; j++)
1882 {
1883 if ((j == 0) && (keyword[j] == ' '))
1884 {
1885 n--;
1886 continue; /* skip leading space */
1887 }
1888 if (needs_percent (keyword[j]))
1889 n += 2; /* will use %-encoding */
1890 }
1891 }
1892 ret = GNUNET_malloc (n);
1893 strcpy (ret, GNUNET_FS_URI_PREFIX);
1894 strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1895 wpos = strlen (ret);
1896 for (i = 0; i < keywordCount; i++)
1897 {
1898 keyword = keywords[i];
1899 slen = strlen (keyword);
1900 for (j = 0; j < slen; j++)
1901 {
1902 if ((j == 0) && (keyword[j] == ' '))
1903 continue; /* skip leading space */
1904 if (needs_percent (keyword[j]))
1905 {
1906 sprintf (&ret[wpos], "%%%02X", (unsigned char) keyword[j]);
1907 wpos += 3;
1908 }
1909 else
1910 {
1911 ret[wpos++] = keyword[j];
1912 }
1913 }
1914 if (i != keywordCount - 1)
1915 ret[wpos++] = '+';
1916 }
1917 return ret;
1918}
1919
1920
1921/**
1922 * Convert SKS URI to a string.
1923 *
1924 * @param uri sks uri to convert
1925 * @return NULL on error
1926 */
1927static char *
1928uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1929{
1930 char *ret;
1931 char buf[1024];
1932
1933 if (GNUNET_FS_URI_SKS != uri->type)
1934 return NULL;
1935 ret =
1936 GNUNET_STRINGS_data_to_string (&uri->data.sks.ns,
1937 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
1938 buf,
1939 sizeof(buf));
1940 GNUNET_assert (NULL != ret);
1941 ret[0] = '\0';
1942 GNUNET_asprintf (&ret,
1943 "%s%s%s/%s",
1944 GNUNET_FS_URI_PREFIX,
1945 GNUNET_FS_URI_SKS_INFIX,
1946 buf,
1947 uri->data.sks.identifier);
1948 return ret;
1949}
1950
1951
1952/**
1953 * Convert a CHK URI to a string.
1954 *
1955 * @param uri chk uri to convert
1956 * @return NULL on error
1957 */
1958static char *
1959uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1960{
1961 const struct FileIdentifier *fi;
1962 char *ret;
1963 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1964 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1965
1966 if (uri->type != GNUNET_FS_URI_CHK)
1967 return NULL;
1968 fi = &uri->data.chk;
1969 GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1970 GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1971
1972 GNUNET_asprintf (&ret,
1973 "%s%s%s.%s.%llu",
1974 GNUNET_FS_URI_PREFIX,
1975 GNUNET_FS_URI_CHK_INFIX,
1976 (const char *) &keyhash,
1977 (const char *) &queryhash,
1978 (unsigned long long) GNUNET_ntohll (fi->file_length));
1979 return ret;
1980}
1981
1982
1983/**
1984 * Convert a LOC URI to a string.
1985 *
1986 * @param uri loc uri to convert
1987 * @return NULL on error
1988 */
1989static char *
1990uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1991{
1992 char *ret;
1993 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1994 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1995 char *peer_id;
1996 char peer_sig[SIGNATURE_ASCII_LENGTH + 1];
1997
1998 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
1999 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
2000 peer_id =
2001 GNUNET_CRYPTO_eddsa_public_key_to_string (&uri->data.loc.peer.public_key);
2002 GNUNET_assert (
2003 NULL !=
2004 GNUNET_STRINGS_data_to_string (&uri->data.loc.contentSignature,
2005 sizeof(struct GNUNET_CRYPTO_EddsaSignature),
2006 peer_sig,
2007 sizeof(peer_sig)));
2008 GNUNET_asprintf (&ret,
2009 "%s%s%s.%s.%llu.%s.%s.%llu",
2010 GNUNET_FS_URI_PREFIX,
2011 GNUNET_FS_URI_LOC_INFIX,
2012 (const char *) &keyhash,
2013 (const char *) &queryhash,
2014 (unsigned long long) GNUNET_ntohll (
2015 uri->data.loc.fi.file_length),
2016 peer_id,
2017 peer_sig,
2018 (unsigned long long)
2019 uri->data.loc.expirationTime.abs_value_us
2020 / 1000000LL);
2021 GNUNET_free (peer_id);
2022 return ret;
2023}
2024
2025
2026/**
2027 * Convert a URI to a UTF-8 String.
2028 *
2029 * @param uri uri to convert to a string
2030 * @return the UTF-8 string
2031 */
2032char *
2033GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
2034{
2035 if (uri == NULL)
2036 {
2037 GNUNET_break (0);
2038 return NULL;
2039 }
2040 switch (uri->type)
2041 {
2042 case GNUNET_FS_URI_KSK:
2043 return uri_ksk_to_string (uri);
2044
2045 case GNUNET_FS_URI_SKS:
2046 return uri_sks_to_string (uri);
2047
2048 case GNUNET_FS_URI_CHK:
2049 return uri_chk_to_string (uri);
2050
2051 case GNUNET_FS_URI_LOC:
2052 return uri_loc_to_string (uri);
2053
2054 default:
2055 GNUNET_break (0);
2056 return NULL;
2057 }
2058}
2059
2060
2061/* end of fs_uri.c */