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