2 This file is part of GNUnet.
3 Copyright (C) 2003--2014 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
23 * @brief Parses and produces uri strings.
24 * @author Igor Wronsky, Christian Grothoff
26 * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
27 * The specific structure of "IDENTIFIER" depends on the module and
28 * maybe differenciated into additional subcategories if applicable.
29 * This module only deals with fs identifiers (MODULE = "fs").
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:
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).
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.
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.
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.
79 * The encoding for hexadecimal values is defined in the hashing.c
80 * module in the gnunetutil library and discussed there.
84 #include "gnunet_fs_service.h"
85 #include "gnunet_signatures.h"
95 * Get a unique key from a URI. This is for putting URIs
96 * into HashMaps. The key may change between FS implementations.
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
103 GNUNET_FS_uri_to_key (const struct GNUNET_FS_Uri *uri,
104 struct GNUNET_HashCode *key)
108 case GNUNET_FS_URI_CHK:
109 *key = uri->data.chk.chk.query;
112 case GNUNET_FS_URI_SKS:
113 GNUNET_CRYPTO_hash (uri->data.sks.identifier,
114 strlen (uri->data.sks.identifier),
118 case GNUNET_FS_URI_KSK:
119 if (uri->data.ksk.keywordCount > 0)
121 GNUNET_CRYPTO_hash (uri->data.ksk.keywords[0],
122 strlen (uri->data.ksk.keywords[0]),
128 memset (key, 0, sizeof(struct GNUNET_HashCode));
129 return GNUNET_SYSERR;
133 case GNUNET_FS_URI_LOC:
134 GNUNET_CRYPTO_hash (&uri->data.loc.fi,
135 sizeof(struct FileIdentifier)
136 + sizeof(struct GNUNET_PeerIdentity),
141 memset (key, 0, sizeof(struct GNUNET_HashCode));
142 return GNUNET_SYSERR;
148 * Convert keyword URI to a human readable format
149 * (i.e. the search query that was used in the first place)
151 * @param uri ksk uri to convert to a string
152 * @return string with the keywords
155 GNUNET_FS_uri_ksk_to_string_fancy (const struct GNUNET_FS_Uri *uri)
162 unsigned int keywordCount;
164 if ((NULL == uri) || (GNUNET_FS_URI_KSK != uri->type))
169 keywords = uri->data.ksk.keywords;
170 keywordCount = uri->data.ksk.keywordCount;
171 n = keywordCount + 1;
172 for (i = 0; i < keywordCount; i++)
174 keyword = keywords[i];
175 n += strlen (keyword) - 1;
176 if (NULL != strstr (&keyword[1], " "))
178 if (keyword[0] == '+')
181 ret = GNUNET_malloc (n);
183 for (i = 0; i < keywordCount; i++)
185 keyword = keywords[i];
186 if (NULL != strstr (&keyword[1], " "))
189 if (keyword[0] == '+')
190 strcat (ret, keyword);
192 strcat (ret, &keyword[1]);
197 if (keyword[0] == '+')
198 strcat (ret, keyword);
200 strcat (ret, &keyword[1]);
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 '+'.
214 * @param in string with %-encoding
215 * @param emsg where to store the parser error message (if any)
216 * @return decodded string with leading space (or preserved plus)
219 percent_decode_keyword (const char *in, char **emsg)
227 out = GNUNET_strdup (in);
230 while (out[rpos] != '\0')
232 if (out[rpos] == '%')
234 if (1 != sscanf (&out[rpos + 1], "%2X", &hx))
237 *emsg = GNUNET_strdup (
238 _ ( /* xgettext:no-c-format */
239 "Malformed KSK URI (`%' must be followed by HEX number)"));
244 continue; /* skip double quote */
245 out[wpos++] = (char) hx;
249 out[wpos++] = out[rpos++];
255 ret = GNUNET_strdup (out);
259 /* need to prefix with space */
260 ret = GNUNET_malloc (strlen (out) + 2);
269 #define GNUNET_FS_URI_KSK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_KSK_INFIX
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
278 static struct GNUNET_FS_Uri *
279 uri_ksk_parse (const char *s, char **emsg)
281 struct GNUNET_FS_Uri *ret;
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] == '+'))
298 GNUNET_strdup (_ ("Malformed KSK URI (must not begin or end with `+')"));
303 for (i = pos; i < slen; i++)
305 if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22")))
307 saw_quote = (saw_quote + 1) % 2;
311 if ((s[i] == '+') && (saw_quote == 0))
316 *emsg = GNUNET_strdup (_ ("Malformed KSK URI (`++' not allowed)"));
323 *emsg = GNUNET_strdup (_ ("Malformed KSK URI (quotes not balanced)"));
327 dup = GNUNET_strdup (s);
328 keywords = GNUNET_new_array (max, char *);
329 for (i = slen - 1; i >= (int) pos; i--)
331 if ((s[i] == '%') && (&s[i] == strstr (&s[i], "%22")))
333 saw_quote = (saw_quote + 1) % 2;
336 if ((dup[i] == '+') && (saw_quote == 0))
338 keywords[--max] = percent_decode_keyword (&dup[i + 1], emsg);
339 if (NULL == keywords[max])
344 keywords[--max] = percent_decode_keyword (&dup[pos], emsg);
345 if (NULL == keywords[max])
347 GNUNET_assert (0 == max);
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;
355 for (i = 0; i < max; i++)
356 GNUNET_free_non_null (keywords[i]);
357 GNUNET_free (keywords);
363 #define GNUNET_FS_URI_SKS_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_SKS_INFIX
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
372 static struct GNUNET_FS_Uri *
373 uri_sks_parse (const char *s, char **emsg)
375 struct GNUNET_FS_Uri *ret;
376 struct GNUNET_CRYPTO_EcdsaPublicKey ns;
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], '/');
385 (GNUNET_OK != GNUNET_STRINGS_string_to_data (&s[pos],
390 *emsg = GNUNET_strdup (_ ("Malformed SKS URI (wrong syntax)"));
391 return NULL; /* malformed */
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);
402 #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
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
412 static struct GNUNET_FS_Uri *
413 uri_chk_parse (const char *s, char **emsg)
415 struct GNUNET_FS_Uri *ret;
416 struct FileIdentifier fi;
418 unsigned long long flen;
420 char h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
421 char h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
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] != '.'))
431 *emsg = GNUNET_strdup (_ ("Malformed CHK URI (wrong syntax)"));
434 GNUNET_memcpy (h1, &s[pos], sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
435 h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
437 &s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)],
438 sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
439 h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
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)) ||
444 sscanf (&s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
448 *emsg = GNUNET_strdup (_ ("Malformed CHK URI (failed to decode CHK)"));
451 fi.file_length = GNUNET_htonll (flen);
452 ret = GNUNET_new (struct GNUNET_FS_Uri);
453 ret->type = GNUNET_FS_URI_CHK;
459 GNUNET_NETWORK_STRUCT_BEGIN
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
465 struct LocUriAssembly
468 * What is being signed (rest of this struct).
470 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
473 * Expiration time of the offer.
475 struct GNUNET_TIME_AbsoluteNBO exptime;
478 * File being offered.
480 struct FileIdentifier fi;
483 * Peer offering the file.
485 struct GNUNET_PeerIdentity peer;
487 GNUNET_NETWORK_STRUCT_END
490 #define GNUNET_FS_URI_LOC_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_LOC_INFIX
492 #define SIGNATURE_ASCII_LENGTH 103
496 * Also verifies validity of the location URI.
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
502 static struct GNUNET_FS_Uri *
503 uri_loc_parse (const char *s, char **emsg)
505 struct GNUNET_FS_Uri *uri;
506 char h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
507 char h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)];
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;
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] != '.'))
525 *emsg = GNUNET_strdup (_ ("LOC URI malformed (wrong syntax)"));
528 GNUNET_memcpy (h1, &s[pos], sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
529 h1[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
531 &s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded)],
532 sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded));
533 h2[sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
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)) ||
538 sscanf (&s[pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2],
542 *emsg = GNUNET_strdup (_ ("LOC URI malformed (no CHK)"));
545 ass.fi.file_length = GNUNET_htonll (flen);
547 npos = pos + sizeof(struct GNUNET_CRYPTO_HashAsciiEncoded) * 2;
548 while ((s[npos] != '\0') && (s[npos] != '.'))
552 *emsg = GNUNET_strdup (_ ("LOC URI malformed (missing LOC)"));
556 if ((strlen (&s[npos]) <= GNUNET_CRYPTO_PKEY_ASCII_LENGTH + 1) ||
557 ('.' != s[npos + GNUNET_CRYPTO_PKEY_ASCII_LENGTH]))
560 GNUNET_strdup (_ ("LOC URI malformed (wrong syntax for public key)"));
564 GNUNET_CRYPTO_eddsa_public_key_from_string (&s[npos],
565 GNUNET_CRYPTO_PKEY_ASCII_LENGTH,
566 &ass.peer.public_key))
569 GNUNET_strdup (_ ("LOC URI malformed (could not decode public key)"));
572 npos += GNUNET_CRYPTO_PKEY_ASCII_LENGTH;
573 if (s[npos++] != '.')
575 *emsg = GNUNET_strdup (_ ("LOC URI malformed (could not find signature)"));
578 if ((strlen (&s[npos]) <= SIGNATURE_ASCII_LENGTH + 1) ||
579 ('.' != s[npos + SIGNATURE_ASCII_LENGTH]))
582 GNUNET_strdup (_ ("LOC URI malformed (wrong syntax for signature)"));
586 GNUNET_STRINGS_string_to_data (&s[npos],
587 SIGNATURE_ASCII_LENGTH,
590 struct GNUNET_CRYPTO_EddsaSignature)))
593 GNUNET_strdup (_ ("LOC URI malformed (could not decode signature)"));
596 npos += SIGNATURE_ASCII_LENGTH;
597 if (s[npos++] != '.')
599 *emsg = GNUNET_strdup (
600 _ ("LOC URI malformed (wrong syntax for expiration time)"));
603 if (1 != sscanf (&s[npos], "%llu", &exptime))
606 GNUNET_strdup (_ ("LOC URI malformed (could not parse expiration time)"));
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);
614 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT,
617 &ass.peer.public_key))
620 GNUNET_strdup (_ ("LOC URI malformed (signature failed validation)"));
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;
637 * Convert a UTF-8 String to a URI.
639 * @param uri string to parse
640 * @param emsg where to store the parser error message (if any)
641 * @return NULL on error
643 struct GNUNET_FS_Uri *
644 GNUNET_FS_uri_parse (const char *uri, char **emsg)
646 struct GNUNET_FS_Uri *ret;
653 *emsg = GNUNET_strdup (_ ("invalid argument"));
659 if ((NULL != (ret = uri_chk_parse (uri, emsg))) ||
660 (NULL != (ret = uri_ksk_parse (uri, emsg))) ||
661 (NULL != (ret = uri_sks_parse (uri, emsg))) ||
662 (NULL != (ret = uri_loc_parse (uri, emsg))))
665 *emsg = GNUNET_strdup (_ ("Unrecognized URI type"));
675 * @param uri uri to free
678 GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri)
684 case GNUNET_FS_URI_KSK:
685 for (i = 0; i < uri->data.ksk.keywordCount; i++)
686 GNUNET_free (uri->data.ksk.keywords[i]);
687 GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, 0);
690 case GNUNET_FS_URI_SKS:
691 GNUNET_free (uri->data.sks.identifier);
694 case GNUNET_FS_URI_LOC:
706 * How many keywords are ANDed in this keyword URI?
708 * @param uri ksk uri to get the number of keywords from
709 * @return 0 if this is not a keyword URI
712 GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri)
714 if (uri->type != GNUNET_FS_URI_KSK)
716 return uri->data.ksk.keywordCount;
721 * Iterate over all keywords in this keyword URI.
723 * @param uri ksk uri to get the keywords from
724 * @param iterator function to call on each keyword
725 * @param iterator_cls closure for iterator
726 * @return -1 if this is not a keyword URI, otherwise number of
727 * keywords iterated over until iterator aborted
730 GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri,
731 GNUNET_FS_KeywordIterator iterator,
737 if (uri->type != GNUNET_FS_URI_KSK)
739 if (NULL == iterator)
740 return uri->data.ksk.keywordCount;
741 for (i = 0; i < uri->data.ksk.keywordCount; i++)
743 keyword = uri->data.ksk.keywords[i];
744 /* first character of keyword indicates
745 * if it is mandatory or not */
746 if (GNUNET_OK != iterator (iterator_cls, &keyword[1],(keyword[0] == '+') ))
754 * Add the given keyword to the set of keywords represented by the URI.
755 * Does nothing if the keyword is already present.
757 * @param uri ksk uri to modify
758 * @param keyword keyword to add
759 * @param is_mandatory is this keyword mandatory?
762 GNUNET_FS_uri_ksk_add_keyword (struct GNUNET_FS_Uri *uri,
770 GNUNET_assert (uri->type == GNUNET_FS_URI_KSK);
771 for (i = 0; i < uri->data.ksk.keywordCount; i++)
773 old = uri->data.ksk.keywords[i];
774 if (0 == strcmp (&old[1], keyword))
777 GNUNET_asprintf (&n, is_mandatory ? "+%s" : " %s", keyword);
778 GNUNET_array_append (uri->data.ksk.keywords, uri->data.ksk.keywordCount, n);
783 * Remove the given keyword from the set of keywords represented by the URI.
784 * Does nothing if the keyword is not present.
786 * @param uri ksk uri to modify
787 * @param keyword keyword to add
790 GNUNET_FS_uri_ksk_remove_keyword (struct GNUNET_FS_Uri *uri,
796 GNUNET_assert (uri->type == GNUNET_FS_URI_KSK);
797 for (i = 0; i < uri->data.ksk.keywordCount; i++)
799 old = uri->data.ksk.keywords[i];
800 if (0 == strcmp (&old[1], keyword))
802 uri->data.ksk.keywords[i] =
803 uri->data.ksk.keywords[uri->data.ksk.keywordCount - 1];
804 GNUNET_array_grow (uri->data.ksk.keywords,
805 uri->data.ksk.keywordCount,
806 uri->data.ksk.keywordCount - 1);
815 * Obtain the identity of the peer offering the data
817 * @param uri the location URI to inspect
818 * @param peer where to store the identify of the peer (presumably) offering the content
819 * @return #GNUNET_SYSERR if this is not a location URI, otherwise #GNUNET_OK
822 GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri,
823 struct GNUNET_PeerIdentity *peer)
825 if (uri->type != GNUNET_FS_URI_LOC)
826 return GNUNET_SYSERR;
827 *peer = uri->data.loc.peer;
833 * Obtain the expiration of the LOC URI.
835 * @param uri location URI to get the expiration from
836 * @return expiration time of the URI
838 struct GNUNET_TIME_Absolute
839 GNUNET_FS_uri_loc_get_expiration (const struct GNUNET_FS_Uri *uri)
841 GNUNET_assert (uri->type == GNUNET_FS_URI_LOC);
842 return uri->data.loc.expirationTime;
847 * Obtain the URI of the content itself.
849 * @param uri location URI to get the content URI from
850 * @return NULL if argument is not a location URI
852 struct GNUNET_FS_Uri *
853 GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri)
855 struct GNUNET_FS_Uri *ret;
857 if (uri->type != GNUNET_FS_URI_LOC)
859 ret = GNUNET_new (struct GNUNET_FS_Uri);
860 ret->type = GNUNET_FS_URI_CHK;
861 ret->data.chk = uri->data.loc.fi;
867 * Construct a location URI (this peer will be used for the location).
868 * This function should only be called from within gnunet-service-fs,
869 * as it requires the peer's private key which is generally unavailable
870 * to processes directly under the user's control. However, for
871 * testing and as it logically fits under URIs, it is in this API.
873 * @param base_uri content offered by the sender
874 * @param sign_key private key of the peer
875 * @param expiration_time how long will the content be offered?
876 * @return the location URI, NULL on error
878 struct GNUNET_FS_Uri *
879 GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *base_uri,
880 const struct GNUNET_CRYPTO_EddsaPrivateKey *sign_key,
881 struct GNUNET_TIME_Absolute expiration_time)
883 struct GNUNET_FS_Uri *uri;
884 struct GNUNET_CRYPTO_EddsaPublicKey my_public_key;
885 struct LocUriAssembly ass;
886 struct GNUNET_TIME_Absolute et;
888 if (GNUNET_FS_URI_CHK != base_uri->type)
890 /* we round expiration time to full seconds for SKS URIs */
891 et.abs_value_us = (expiration_time.abs_value_us / 1000000LL) * 1000000LL;
892 GNUNET_CRYPTO_eddsa_key_get_public (sign_key, &my_public_key);
893 ass.purpose.size = htonl (sizeof(struct LocUriAssembly));
894 ass.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT);
895 ass.exptime = GNUNET_TIME_absolute_hton (et);
896 ass.fi = base_uri->data.chk;
897 ass.peer.public_key = my_public_key;
898 uri = GNUNET_new (struct GNUNET_FS_Uri);
899 uri->type = GNUNET_FS_URI_LOC;
900 uri->data.loc.fi = base_uri->data.chk;
901 uri->data.loc.expirationTime = et;
902 uri->data.loc.peer.public_key = my_public_key;
903 GNUNET_CRYPTO_eddsa_sign (sign_key,
905 &uri->data.loc.contentSignature);
911 * Create an SKS URI from a namespace ID and an identifier.
913 * @param ns namespace ID
914 * @param id identifier
915 * @return an FS URI for the given namespace and identifier
917 struct GNUNET_FS_Uri *
918 GNUNET_FS_uri_sks_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
921 struct GNUNET_FS_Uri *ns_uri;
923 ns_uri = GNUNET_new (struct GNUNET_FS_Uri);
924 ns_uri->type = GNUNET_FS_URI_SKS;
925 ns_uri->data.sks.ns = *ns;
926 ns_uri->data.sks.identifier = GNUNET_strdup (id);
932 * Merge the sets of keywords from two KSK URIs.
933 * (useful for merging the canonicalized keywords with
934 * the original keywords for sharing).
936 * @param u1 first uri
937 * @param u2 second uri
938 * @return merged URI, NULL on error
940 struct GNUNET_FS_Uri *
941 GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
942 const struct GNUNET_FS_Uri *u2)
944 struct GNUNET_FS_Uri *ret;
952 if ((u1 == NULL) && (u2 == NULL))
955 return GNUNET_FS_uri_dup (u2);
957 return GNUNET_FS_uri_dup (u1);
958 if ((u1->type != GNUNET_FS_URI_KSK) || (u2->type != GNUNET_FS_URI_KSK))
963 kc = u1->data.ksk.keywordCount;
964 kl = GNUNET_new_array (kc + u2->data.ksk.keywordCount, char *);
965 for (i = 0; i < u1->data.ksk.keywordCount; i++)
966 kl[i] = GNUNET_strdup (u1->data.ksk.keywords[i]);
967 for (i = 0; i < u2->data.ksk.keywordCount; i++)
969 kp = u2->data.ksk.keywords[i];
971 for (j = 0; j < u1->data.ksk.keywordCount; j++)
972 if (0 == strcmp (kp + 1, kl[j] + 1))
980 kl[kc++] = GNUNET_strdup (kp);
982 ret = GNUNET_new (struct GNUNET_FS_Uri);
983 ret->type = GNUNET_FS_URI_KSK;
984 ret->data.ksk.keywordCount = kc;
985 ret->data.ksk.keywords = kl;
993 * @param uri the URI to duplicate
994 * @return copy of the URI
996 struct GNUNET_FS_Uri *
997 GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
999 struct GNUNET_FS_Uri *ret;
1004 ret = GNUNET_new (struct GNUNET_FS_Uri);
1005 GNUNET_memcpy (ret, uri, sizeof(struct GNUNET_FS_Uri));
1008 case GNUNET_FS_URI_KSK:
1009 if (ret->data.ksk.keywordCount >=
1010 GNUNET_MAX_MALLOC_CHECKED / sizeof(char *))
1016 if (ret->data.ksk.keywordCount > 0)
1018 ret->data.ksk.keywords =
1019 GNUNET_new_array (ret->data.ksk.keywordCount, char *);
1020 for (i = 0; i < ret->data.ksk.keywordCount; i++)
1021 ret->data.ksk.keywords[i] = GNUNET_strdup (uri->data.ksk.keywords[i]);
1024 ret->data.ksk.keywords = NULL; /* just to be sure */
1027 case GNUNET_FS_URI_SKS:
1028 ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
1031 case GNUNET_FS_URI_LOC:
1042 * Create an FS URI from a single user-supplied string of keywords.
1043 * The string is broken up at spaces into individual keywords.
1044 * Keywords that start with "+" are mandatory. Double-quotes can
1045 * be used to prevent breaking up strings at spaces (and also
1046 * to specify non-mandatory keywords starting with "+").
1048 * Keywords must contain a balanced number of double quotes and
1049 * double quotes can not be used in the actual keywords (for
1050 * example, the string '""foo bar""' will be turned into two
1051 * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
1053 * @param keywords the keyword string
1054 * @param emsg where to store an error message
1055 * @return an FS URI for the given keywords, NULL
1056 * if keywords is not legal (i.e. empty).
1058 struct GNUNET_FS_Uri *
1059 GNUNET_FS_uri_ksk_create (const char *keywords, char **emsg)
1062 unsigned int num_Words;
1065 struct GNUNET_FS_Uri *uri;
1069 if (keywords == NULL)
1071 *emsg = GNUNET_strdup (_ ("No keywords specified!\n"));
1075 searchString = GNUNET_strdup (keywords);
1080 while ('\0' != *pos)
1082 if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1086 else if (0 == inWord)
1092 saw_quote = (saw_quote + 1) % 2;
1097 GNUNET_free (searchString);
1098 *emsg = GNUNET_strdup (_ ("No keywords specified!\n"));
1103 GNUNET_free (searchString);
1104 *emsg = GNUNET_strdup (_ ("Number of double-quotes not balanced!\n"));
1107 keywordarr = GNUNET_new_array (num_Words, char *);
1111 while ('\0' != *pos)
1113 if ((saw_quote == 0) && (isspace ((unsigned char) *pos)))
1118 else if (0 == inWord)
1120 keywordarr[num_Words] = pos;
1125 saw_quote = (saw_quote + 1) % 2;
1129 GNUNET_FS_uri_ksk_create_from_args (num_Words, (const char **) keywordarr);
1130 GNUNET_free (keywordarr);
1131 GNUNET_free (searchString);
1137 * Create an FS URI from a user-supplied command line of keywords.
1138 * Arguments should start with "+" to indicate mandatory
1141 * @param argc number of keywords
1142 * @param argv keywords (double quotes are not required for
1143 * keywords containing spaces; however, double
1144 * quotes are required for keywords starting with
1145 * "+"); there is no mechanism for having double
1146 * quotes in the actual keywords (if the user
1147 * did specifically specify double quotes, the
1148 * caller should convert each double quote
1149 * into two single quotes).
1150 * @return an FS URI for the given keywords, NULL
1151 * if keywords is not legal (i.e. empty).
1153 struct GNUNET_FS_Uri *
1154 GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, const char **argv)
1157 struct GNUNET_FS_Uri *uri;
1158 const char *keyword;
1166 /* allow URI to be given as one and only keyword and
1167 * handle accordingly */
1169 if ((argc == 1) && (strlen (argv[0]) > strlen (GNUNET_FS_URI_PREFIX)) &&
1170 (0 == strncmp (argv[0],
1171 GNUNET_FS_URI_PREFIX,
1172 strlen (GNUNET_FS_URI_PREFIX))) &&
1173 (NULL != (uri = GNUNET_FS_uri_parse (argv[0], &emsg))))
1175 GNUNET_free_non_null (emsg);
1176 uri = GNUNET_new (struct GNUNET_FS_Uri);
1177 uri->type = GNUNET_FS_URI_KSK;
1178 uri->data.ksk.keywordCount = argc;
1179 uri->data.ksk.keywords = GNUNET_new_array (argc, char *);
1180 for (i = 0; i < argc; i++)
1183 if (keyword[0] == '+')
1184 val = GNUNET_strdup (keyword);
1186 GNUNET_asprintf (&val, " %s", keyword);
1197 uri->data.ksk.keywords[i] = val;
1204 * Test if two URIs are equal.
1206 * @param u1 one of the URIs
1207 * @param u2 the other URI
1208 * @return #GNUNET_YES if the URIs are equal
1211 GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
1212 const struct GNUNET_FS_Uri *u2)
1218 GNUNET_assert (u1 != NULL);
1219 GNUNET_assert (u2 != NULL);
1220 if (u1->type != u2->type)
1224 case GNUNET_FS_URI_CHK:
1226 memcmp (&u1->data.chk, &u2->data.chk, sizeof(struct FileIdentifier)))
1230 case GNUNET_FS_URI_SKS:
1231 if ((0 == memcmp (&u1->data.sks.ns,
1233 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey))) &&
1234 (0 == strcmp (u1->data.sks.identifier, u2->data.sks.identifier)))
1239 case GNUNET_FS_URI_KSK:
1240 if (u1->data.ksk.keywordCount != u2->data.ksk.keywordCount)
1242 for (i = 0; i < u1->data.ksk.keywordCount; i++)
1245 for (j = 0; j < u2->data.ksk.keywordCount; j++)
1247 if (0 == strcmp (u1->data.ksk.keywords[i], u2->data.ksk.keywords[j]))
1253 if (ret == GNUNET_NO)
1258 case GNUNET_FS_URI_LOC:
1259 if (memcmp (&u1->data.loc,
1261 sizeof(struct FileIdentifier)
1262 + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
1263 + sizeof(struct GNUNET_TIME_Absolute)
1264 + sizeof(unsigned short) + sizeof(unsigned short)) != 0)
1275 * Is this a namespace URI?
1277 * @param uri the uri to check
1278 * @return #GNUNET_YES if this is an SKS uri
1281 GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
1283 return uri->type == GNUNET_FS_URI_SKS;
1288 * Get the ID of a namespace from the given
1291 * @param uri the uri to get the namespace ID from
1292 * @param pseudonym where to store the ID of the namespace
1293 * @return #GNUNET_OK on success
1296 GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
1297 struct GNUNET_CRYPTO_EcdsaPublicKey *pseudonym)
1299 if (! GNUNET_FS_uri_test_sks (uri))
1302 return GNUNET_SYSERR;
1304 *pseudonym = uri->data.sks.ns;
1310 * Get the content identifier of an SKS URI.
1312 * @param uri the sks uri
1313 * @return NULL on error (not a valid SKS URI)
1316 GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
1318 if (! GNUNET_FS_uri_test_sks (uri))
1323 return GNUNET_strdup (uri->data.sks.identifier);
1328 * Is this a keyword URI?
1330 * @param uri the uri
1331 * @return #GNUNET_YES if this is a KSK uri
1334 GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
1339 if (uri->type == GNUNET_FS_URI_KSK)
1341 for (i = 0; i < uri->data.ksk.keywordCount; i++)
1342 GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
1345 return uri->type == GNUNET_FS_URI_KSK;
1350 * Is this a file (or directory) URI?
1352 * @param uri the uri to check
1353 * @return #GNUNET_YES if this is a CHK uri
1356 GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
1358 return uri->type == GNUNET_FS_URI_CHK;
1363 * What is the size of the file that this URI
1366 * @param uri the CHK URI to inspect
1367 * @return size of the file as specified in the CHK URI
1370 GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri *uri)
1374 case GNUNET_FS_URI_CHK:
1375 return GNUNET_ntohll (uri->data.chk.file_length);
1377 case GNUNET_FS_URI_LOC:
1378 return GNUNET_ntohll (uri->data.loc.fi.file_length);
1383 return 0; /* unreachable */
1388 * Is this a location URI?
1390 * @param uri the uri to check
1391 * @return #GNUNET_YES if this is a LOC uri
1394 GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
1396 return uri->type == GNUNET_FS_URI_LOC;
1401 * Add a keyword as non-mandatory (with ' '-prefix) to the
1402 * given keyword list at offset 'index'. The array is
1403 * guaranteed to be long enough.
1405 * @param s keyword to add
1406 * @param array array to add the keyword to
1407 * @param index offset where to add the keyword
1410 insert_non_mandatory_keyword (const char *s, char **array, int index)
1414 GNUNET_asprintf (&nkword,
1415 " %s", /* space to mark as 'non mandatory' */
1417 array[index] = nkword;
1422 * Test if the given keyword @a s is already present in the
1423 * given array, ignoring the '+'-mandatory prefix in the array.
1425 * @param s keyword to test
1426 * @param array keywords to test against, with ' ' or '+' prefix to ignore
1427 * @param array_length length of the @a array
1428 * @return #GNUNET_YES if the keyword exists, #GNUNET_NO if not
1431 find_duplicate (const char *s, const char **array, int array_length)
1435 for (j = array_length - 1; j >= 0; j--)
1436 if (0 == strcmp (&array[j][1], s))
1446 normalize_metadata (enum EXTRACTOR_MetaFormat format,
1450 uint8_t *free_str = NULL;
1451 uint8_t *str_to_normalize = (uint8_t *) data;
1452 uint8_t *normalized;
1455 if (str_to_normalize == NULL)
1457 /* Don't trust libextractor */
1458 if (format == EXTRACTOR_METAFORMAT_UTF8)
1460 free_str = (uint8_t *) u8_check ((const uint8_t *) data, data_len);
1461 if (free_str == NULL)
1464 format = EXTRACTOR_METAFORMAT_C_STRING;
1466 if (format == EXTRACTOR_METAFORMAT_C_STRING)
1468 free_str = u8_strconv_from_encoding (data,
1470 iconveh_escape_sequence);
1471 if (free_str == NULL)
1475 normalized = u8_tolower (str_to_normalize,
1476 strlen ((char *) str_to_normalize),
1481 /* free_str is allocated by libunistring internally, use free() */
1482 if (free_str != NULL)
1484 if (normalized != NULL)
1486 /* u8_tolower allocates a non-NULL-terminated string! */
1487 free_str = GNUNET_malloc (r_len + 1);
1488 GNUNET_memcpy (free_str, normalized, r_len);
1489 free_str[r_len] = '\0';
1491 normalized = free_str;
1493 return (char *) normalized;
1498 * Counts the number of UTF-8 characters (not bytes) in the string,
1499 * returns that count.
1502 u8_strcount (const uint8_t *s)
1507 GNUNET_assert (s != NULL);
1510 for (count = 0; s != NULL; count++)
1511 s = u8_next (&c, s);
1517 * Break the filename up by matching [], () and {} pairs to make
1518 * keywords. In case of nesting parentheses only the inner pair counts.
1519 * You can't escape parentheses to scan something like "[blah\{foo]" to
1520 * make a "blah{foo" keyword, this function is only a heuristic!
1522 * @param s string to break down.
1523 * @param array array to fill with enclosed tokens. If NULL, then tokens
1525 * @param index index at which to start filling the array (entries prior
1526 * to it are used to check for duplicates). ignored if @a array == NULL.
1527 * @return number of tokens counted (including duplicates), or number of
1528 * tokens extracted (excluding duplicates). 0 if there are no
1529 * matching parens in the string (when counting), or when all tokens
1530 * were duplicates (when extracting).
1533 get_keywords_from_parens (const char *s, char **array, int index)
1543 ss = GNUNET_strdup (s);
1544 open_paren = ss - 1;
1545 while (NULL != (open_paren = strpbrk (open_paren + 1, "[{(")))
1549 close_paren = strpbrk (open_paren + 1, "]})");
1550 if (NULL == close_paren)
1552 switch (open_paren[0])
1555 if (']' == close_paren[0])
1560 if ('}' == close_paren[0])
1565 if (')' == close_paren[0])
1572 if (match && (close_paren - open_paren > 1))
1574 tmp = close_paren[0];
1575 close_paren[0] = '\0';
1576 /* Keywords must be at least 3 characters long */
1577 if (u8_strcount ((const uint8_t *) &open_paren[1]) <= 2)
1579 close_paren[0] = tmp;
1585 if (GNUNET_NO == find_duplicate ((const char *) &open_paren[1],
1586 (const char **) array,
1589 insert_non_mandatory_keyword ((const char *) &open_paren[1],
1594 normalized = normalize_metadata (EXTRACTOR_METAFORMAT_UTF8,
1596 close_paren - &open_paren[1]);
1597 if (normalized != NULL)
1599 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1600 (const char **) array,
1603 insert_non_mandatory_keyword ((const char *) normalized,
1608 GNUNET_free (normalized);
1613 close_paren[0] = tmp;
1622 * Where to break up keywords
1624 #define TOKENS "_. /-!?#&+@\"\'\\;:,()[]{}$<>|"
1627 * Break the filename up by TOKENS to make
1630 * @param s string to break down.
1631 * @param array array to fill with tokens. If NULL, then tokens are only
1633 * @param index index at which to start filling the array (entries prior
1634 * to it are used to check for duplicates). ignored if @a array == NULL.
1635 * @return number of tokens (>1) counted (including duplicates), or number of
1636 * tokens extracted (excluding duplicates). 0 if there are no
1637 * separators in the string (when counting), or when all tokens were
1638 * duplicates (when extracting).
1641 get_keywords_from_tokens (const char *s, char **array, int index)
1647 ss = GNUNET_strdup (s);
1648 for (p = strtok (ss, TOKENS); p != NULL; p = strtok (NULL, TOKENS))
1650 /* Keywords must be at least 3 characters long */
1651 if (u8_strcount ((const uint8_t *) p) <= 2)
1656 if (GNUNET_NO == find_duplicate (p, (const char **) array, index + seps))
1658 insert_non_mandatory_keyword (p, array, index + seps);
1662 normalize_metadata (EXTRACTOR_METAFORMAT_UTF8, p, strlen (p));
1663 if (normalized != NULL)
1665 if (GNUNET_NO == find_duplicate ((const char *) normalized,
1666 (const char **) array,
1669 insert_non_mandatory_keyword ((const char *) normalized,
1674 GNUNET_free (normalized);
1689 * Function called on each value in the meta data.
1690 * Adds it to the URI.
1692 * @param cls URI to update
1693 * @param plugin_name name of the plugin that produced this value;
1694 * special values can be used (i.e. '<zlib>' for zlib being
1695 * used in the main libextractor library and yielding
1697 * @param type libextractor-type describing the meta data
1698 * @param format basic format information about data
1699 * @param data_mime_type mime-type of data (not of the original file);
1700 * can be NULL (if mime-type is not known)
1701 * @param data actual meta-data found
1702 * @param data_len number of bytes in @a data
1703 * @return 0 (always)
1706 gather_uri_data (void *cls,
1707 const char *plugin_name,
1708 enum EXTRACTOR_MetaType type,
1709 enum EXTRACTOR_MetaFormat format,
1710 const char *data_mime_type,
1714 struct GNUNET_FS_Uri *uri = cls;
1715 char *normalized_data;
1718 if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
1719 (format != EXTRACTOR_METAFORMAT_C_STRING))
1721 /* Keywords must be at least 3 characters long
1722 * If given non-utf8 string it will, most likely, find it to be invalid,
1723 * and will return the length of its valid part, skipping the keyword.
1724 * If it does - fix the extractor, not this check!
1725 */if (u8_strcount ((const uint8_t *) data) <= 2)
1727 if ((EXTRACTOR_METATYPE_MIMETYPE == type) &&
1728 (NULL != (sep = memchr (data, '/', data_len))) && (sep != data))
1732 GNUNET_asprintf (&xtra, "mimetype:%.*s", (int) (sep - data), data);
1733 if (! find_duplicate (xtra,
1734 (const char **) uri->data.ksk.keywords,
1735 uri->data.ksk.keywordCount))
1737 insert_non_mandatory_keyword (xtra,
1738 uri->data.ksk.keywords,
1739 uri->data.ksk.keywordCount);
1740 uri->data.ksk.keywordCount++;
1745 normalized_data = normalize_metadata (format, data, data_len);
1746 if (! find_duplicate (data,
1747 (const char **) uri->data.ksk.keywords,
1748 uri->data.ksk.keywordCount))
1750 insert_non_mandatory_keyword (data,
1751 uri->data.ksk.keywords,
1752 uri->data.ksk.keywordCount);
1753 uri->data.ksk.keywordCount++;
1755 if (NULL != normalized_data)
1757 if (! find_duplicate (normalized_data,
1758 (const char **) uri->data.ksk.keywords,
1759 uri->data.ksk.keywordCount))
1761 insert_non_mandatory_keyword (normalized_data,
1762 uri->data.ksk.keywords,
1763 uri->data.ksk.keywordCount);
1764 uri->data.ksk.keywordCount++;
1766 GNUNET_free (normalized_data);
1773 * Construct a keyword-URI from meta-data (take all entries
1774 * in the meta-data and construct one large keyword URI
1775 * that lists all keywords that can be found in the meta-data).
1777 * @param md metadata to use
1778 * @return NULL on error, otherwise a KSK URI
1780 struct GNUNET_FS_Uri *
1781 GNUNET_FS_uri_ksk_create_from_meta_data (
1782 const struct GNUNET_CONTAINER_MetaData *md)
1784 struct GNUNET_FS_Uri *ret;
1786 char *full_name = NULL;
1789 int tok_keywords = 0;
1790 int paren_keywords = 0;
1794 ret = GNUNET_new (struct GNUNET_FS_Uri);
1795 ret->type = GNUNET_FS_URI_KSK;
1796 ent = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
1799 full_name = GNUNET_CONTAINER_meta_data_get_first_by_types (
1801 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
1803 if (NULL != full_name)
1805 filename = full_name;
1806 while (NULL != (ss = strstr (filename, DIR_SEPARATOR_STR)))
1808 tok_keywords = get_keywords_from_tokens (filename, NULL, 0);
1809 paren_keywords = get_keywords_from_parens (filename, NULL, 0);
1811 /* x3 because there might be a normalized variant of every keyword,
1812 plus theoretically one more for mime... */
1813 ret->data.ksk.keywords =
1814 GNUNET_new_array ((ent + tok_keywords + paren_keywords) * 3, char *);
1815 GNUNET_CONTAINER_meta_data_iterate (md, &gather_uri_data, ret);
1817 if (tok_keywords > 0)
1818 ret->data.ksk.keywordCount +=
1819 get_keywords_from_tokens (filename,
1820 ret->data.ksk.keywords,
1821 ret->data.ksk.keywordCount);
1822 if (paren_keywords > 0)
1823 ret->data.ksk.keywordCount +=
1824 get_keywords_from_parens (filename,
1825 ret->data.ksk.keywords,
1826 ret->data.ksk.keywordCount);
1828 GNUNET_free_non_null (full_name);
1834 * In URI-encoding, does the given character
1835 * need to be encoded using %-encoding?
1838 needs_percent (char c)
1840 return(! ((isalnum ((unsigned char) c)) || (c == '-') || (c == '_') ||
1841 (c == '.') || (c == '~')));
1846 * Convert a KSK URI to a string.
1848 * @param uri the URI to convert
1849 * @return NULL on error (i.e. keywordCount == 0)
1852 uri_ksk_to_string (const struct GNUNET_FS_Uri *uri)
1855 unsigned int keywordCount;
1862 const char *keyword;
1864 if (uri->type != GNUNET_FS_URI_KSK)
1866 keywords = uri->data.ksk.keywords;
1867 keywordCount = uri->data.ksk.keywordCount;
1868 n = keywordCount + strlen (GNUNET_FS_URI_PREFIX)
1869 + strlen (GNUNET_FS_URI_KSK_INFIX) + 1;
1870 for (i = 0; i < keywordCount; i++)
1872 keyword = keywords[i];
1873 slen = strlen (keyword);
1875 for (j = 0; j < slen; j++)
1877 if ((j == 0) && (keyword[j] == ' '))
1880 continue; /* skip leading space */
1882 if (needs_percent (keyword[j]))
1883 n += 2; /* will use %-encoding */
1886 ret = GNUNET_malloc (n);
1887 strcpy (ret, GNUNET_FS_URI_PREFIX);
1888 strcat (ret, GNUNET_FS_URI_KSK_INFIX);
1889 wpos = strlen (ret);
1890 for (i = 0; i < keywordCount; i++)
1892 keyword = keywords[i];
1893 slen = strlen (keyword);
1894 for (j = 0; j < slen; j++)
1896 if ((j == 0) && (keyword[j] == ' '))
1897 continue; /* skip leading space */
1898 if (needs_percent (keyword[j]))
1900 sprintf (&ret[wpos], "%%%02X", (unsigned char) keyword[j]);
1905 ret[wpos++] = keyword[j];
1908 if (i != keywordCount - 1)
1916 * Convert SKS URI to a string.
1918 * @param uri sks uri to convert
1919 * @return NULL on error
1922 uri_sks_to_string (const struct GNUNET_FS_Uri *uri)
1927 if (GNUNET_FS_URI_SKS != uri->type)
1930 GNUNET_STRINGS_data_to_string (&uri->data.sks.ns,
1931 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
1934 GNUNET_assert (NULL != ret);
1936 GNUNET_asprintf (&ret,
1938 GNUNET_FS_URI_PREFIX,
1939 GNUNET_FS_URI_SKS_INFIX,
1941 uri->data.sks.identifier);
1947 * Convert a CHK URI to a string.
1949 * @param uri chk uri to convert
1950 * @return NULL on error
1953 uri_chk_to_string (const struct GNUNET_FS_Uri *uri)
1955 const struct FileIdentifier *fi;
1957 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1958 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1960 if (uri->type != GNUNET_FS_URI_CHK)
1962 fi = &uri->data.chk;
1963 GNUNET_CRYPTO_hash_to_enc (&fi->chk.key, &keyhash);
1964 GNUNET_CRYPTO_hash_to_enc (&fi->chk.query, &queryhash);
1966 GNUNET_asprintf (&ret,
1968 GNUNET_FS_URI_PREFIX,
1969 GNUNET_FS_URI_CHK_INFIX,
1970 (const char *) &keyhash,
1971 (const char *) &queryhash,
1972 GNUNET_ntohll (fi->file_length));
1978 * Convert a LOC URI to a string.
1980 * @param uri loc uri to convert
1981 * @return NULL on error
1984 uri_loc_to_string (const struct GNUNET_FS_Uri *uri)
1987 struct GNUNET_CRYPTO_HashAsciiEncoded keyhash;
1988 struct GNUNET_CRYPTO_HashAsciiEncoded queryhash;
1990 char peer_sig[SIGNATURE_ASCII_LENGTH + 1];
1992 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.key, &keyhash);
1993 GNUNET_CRYPTO_hash_to_enc (&uri->data.loc.fi.chk.query, &queryhash);
1995 GNUNET_CRYPTO_eddsa_public_key_to_string (&uri->data.loc.peer.public_key);
1998 GNUNET_STRINGS_data_to_string (&uri->data.loc.contentSignature,
1999 sizeof(struct GNUNET_CRYPTO_EddsaSignature),
2002 GNUNET_asprintf (&ret,
2003 "%s%s%s.%s.%llu.%s.%s.%llu",
2004 GNUNET_FS_URI_PREFIX,
2005 GNUNET_FS_URI_LOC_INFIX,
2006 (const char *) &keyhash,
2007 (const char *) &queryhash,
2008 (unsigned long long) GNUNET_ntohll (
2009 uri->data.loc.fi.file_length),
2012 (unsigned long long)
2013 uri->data.loc.expirationTime.abs_value_us
2015 GNUNET_free (peer_id);
2021 * Convert a URI to a UTF-8 String.
2023 * @param uri uri to convert to a string
2024 * @return the UTF-8 string
2027 GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri)
2036 case GNUNET_FS_URI_KSK:
2037 return uri_ksk_to_string (uri);
2039 case GNUNET_FS_URI_SKS:
2040 return uri_sks_to_string (uri);
2042 case GNUNET_FS_URI_CHK:
2043 return uri_chk_to_string (uri);
2045 case GNUNET_FS_URI_LOC:
2046 return uri_loc_to_string (uri);
2055 /* end of fs_uri.c */