fs_uri.c 54 KB

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