📄 uri.c
字号:
/* This file is part of GNUnet. (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//** * @file applications/fs/ecrs/uri.c * @brief Parses and produces uri strings. * @author Igor Wronsky, Christian Grothoff * * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER". * The specific structure of "IDENTIFIER" depends on the module and * maybe differenciated into additional subcategories if applicable. * This module only deals with ecrs identifiers (MODULE = "ecrs"). * <p> * * This module only parses URIs for the AFS module. The ECRS URIs fall * into four categories, "chk", "sks", "ksk" and "loc". The first three * categories were named in analogy (!) to Freenet, but they do NOT * work in exactly the same way. They are very similar from the user's * point of view (unique file identifier, subspace, keyword), but the * implementation is rather different in pretty much every detail. * The concrete URI formats are: * * <ul><li> * * First, there are URIs that identify a file. They have the format * "gnunet://ecrs/chk/HEX1.HEX2.SIZE". These URIs can be used to * download the file. The description, filename, mime-type and other * meta-data is NOT part of the file-URI since a URI uniquely * identifies a resource (and the contents of the file would be the * same even if it had a different description). * * </li><li> * * The second category identifies entries in a namespace. The format * is "gnunet://ecrs/sks/NAMESPACE/IDENTIFIER" where the namespace * should be given in HEX. Applications may allow using a nickname * for the namespace if the nickname is not ambiguous. The identifier * can be either an ASCII sequence or a HEX-encoding. If the * identifier is in ASCII but the format is ambiguous and could denote * a HEX-string a "/" is appended to indicate ASCII encoding. * * </li> <li> * * The third category identifies ordinary searches. The format is * "gnunet://ecrs/ksk/KEYWORD[+KEYWORD]*". Using the "+" syntax * it is possible to encode searches with the boolean "AND" operator. * "+" is used since it indicates a commutative 'and' operation and * is unlikely to be used in a keyword by itself. * * </li><li> * * The last category identifies a datum on a specific machine. The * format is "gnunet://ecrs/loc/HEX1.HEX2.SIZE.PEER.SIG.EXPTIME". PEER is * the BinName of the public key of the peer storing the datum. The * signature (SIG) certifies that this peer has this content. * HEX1, HEX2 and SIZE correspond to a 'chk' URI. * * </li></ul> * * The encoding for hexadecimal values is defined in the hashing.c * module (GNUNET_EncName) in the gnunetutil library and discussed there. * <p> */#include "platform.h"#include "ecrs.h"#include "gnunet_protocols.h"#include "gnunet_ecrs_lib.h"/** * In URI-encoding, does the given character * need to be encoded using %-encoding? */static intneeds_percent (char c){ return (!((isalnum (c)) || (c == '-') || (c == '_') || (c == '.') || (c == '~')));}/** * Generate a keyword URI. * @return NULL on error (i.e. keywordCount == 0) */static char *createKeywordURI (char **keywords, unsigned int keywordCount){ size_t n; char *ret; unsigned int i; unsigned int j; unsigned int wpos; size_t slen; const char *keyword; n = keywordCount + strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_SEARCH_INFIX) + 1; for (i = 0; i < keywordCount; i++) { keyword = keywords[i]; slen = strlen (keyword); n += slen; for (j = 0; j < slen; j++) { if ((j == 0) && (keyword[j] == ' ')) { n--; continue; /* skip leading space */ } if (needs_percent (keyword[j])) n += 2; /* will use %-encoding */ } } ret = GNUNET_malloc (n); strcpy (ret, GNUNET_ECRS_URI_PREFIX); strcat (ret, GNUNET_ECRS_SEARCH_INFIX); wpos = strlen (ret); for (i = 0; i < keywordCount; i++) { keyword = keywords[i]; slen = strlen (keyword); for (j = 0; j < slen; j++) { if ((j == 0) && (keyword[j] == ' ')) continue; /* skip leading space */ if (needs_percent (keyword[j])) { sprintf (&ret[wpos], "%%%02X", keyword[j]); wpos += 3; } else { ret[wpos++] = keyword[j]; } } if (i != keywordCount - 1) ret[wpos++] = '+'; } return ret;}/** * Generate a subspace URI. */static char *createSubspaceURI (const GNUNET_HashCode * namespace, const char *identifier){ size_t n; char *ret; GNUNET_EncName ns; n = sizeof (GNUNET_EncName) + strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_SUBSPACE_INFIX) + 1 + strlen (identifier); ret = GNUNET_malloc (n); GNUNET_hash_to_enc (namespace, &ns); GNUNET_snprintf (ret, n, "%s%s%s/%s", GNUNET_ECRS_URI_PREFIX, GNUNET_ECRS_SUBSPACE_INFIX, (const char *) &ns, identifier); return ret;}/** * Generate a file URI. */static char *createFileURI (const GNUNET_EC_FileIdentifier * fi){ char *ret; GNUNET_EncName keyhash; GNUNET_EncName queryhash; size_t n; GNUNET_hash_to_enc (&fi->chk.key, &keyhash); GNUNET_hash_to_enc (&fi->chk.query, &queryhash); n = strlen (GNUNET_ECRS_URI_PREFIX) + 2 * sizeof (GNUNET_EncName) + 8 + 16 + 32 + strlen (GNUNET_ECRS_FILE_INFIX); ret = GNUNET_malloc (n); GNUNET_snprintf (ret, n, "%s%s%s.%s.%llu", GNUNET_ECRS_URI_PREFIX, GNUNET_ECRS_FILE_INFIX, (char *) &keyhash, (char *) &queryhash, GNUNET_ntohll (fi->file_length)); return ret;}#include "bincoder.c"/** * Create a (string) location URI from a Location. */static char *createLocURI (const Location * loc){ size_t n; char *ret; GNUNET_EncName keyhash; GNUNET_EncName queryhash; char *peerId; char *peerSig; GNUNET_hash_to_enc (&loc->fi.chk.key, &keyhash); GNUNET_hash_to_enc (&loc->fi.chk.query, &queryhash); n = 2148; peerId = bin2enc (&loc->peer, sizeof (GNUNET_RSA_PublicKey)); peerSig = bin2enc (&loc->contentSignature, sizeof (GNUNET_RSA_Signature)); ret = GNUNET_malloc (n); GNUNET_snprintf (ret, n, "%s%s%s.%s.%llu.%s.%s.%u", GNUNET_ECRS_URI_PREFIX, GNUNET_ECRS_LOCATION_INFIX, (char *) &keyhash, (char *) &queryhash, GNUNET_ntohll (loc->fi.file_length), peerId, peerSig, loc->expirationTime); GNUNET_free (peerSig); GNUNET_free (peerId); return ret;}/** * Convert a URI to a UTF-8 String. */char *GNUNET_ECRS_uri_to_string (const struct GNUNET_ECRS_URI *uri){ if (uri == NULL) { GNUNET_GE_BREAK (NULL, 0); return NULL; } switch (uri->type) { case ksk: return createKeywordURI (uri->data.ksk.keywords, uri->data.ksk.keywordCount); case sks: return createSubspaceURI (&uri->data.sks.namespace, uri->data.sks.identifier); case chk: return createFileURI (&uri->data.fi); case loc: return createLocURI (&uri->data.loc); default: GNUNET_GE_BREAK (NULL, 0); return NULL; }}/** * Convert keyword URI to a human readable format * (i.e. the search query that was used in the first place) */char *GNUNET_ECRS_ksk_uri_to_human_readable_string (const struct GNUNET_ECRS_URI *uri){ size_t n; char *ret; unsigned int i; const char *keyword; char **keywords; unsigned int keywordCount; if ((uri == NULL) || (uri->type != ksk)) { GNUNET_GE_BREAK (NULL, 0); return NULL; } keywords = uri->data.ksk.keywords; keywordCount = uri->data.ksk.keywordCount; n = keywordCount + 1; for (i = 0; i < keywordCount; i++) { keyword = keywords[i]; n += strlen (keyword) - 1; if (NULL != strstr (&keyword[1], " ")) n += 2; if (keyword[0] == '+') n++; } ret = GNUNET_malloc (n); strcpy (ret, ""); for (i = 0; i < keywordCount; i++) { keyword = keywords[i]; if (NULL != strstr (&keyword[1], " ")) { strcat (ret, "\""); if (keyword[0] == '+') strcat (ret, keyword); else strcat (ret, &keyword[1]); strcat (ret, "\""); } else { if (keyword[0] == '+') strcat (ret, keyword); else strcat (ret, &keyword[1]); } strcat (ret, " "); } return ret;}/** * Given a keyword with %-encoding (and possibly quotes to protect * spaces), return a copy of the keyword without %-encoding and * without double-quotes (%22). Also, add a space at the beginning * if there is not a '+'. */static char *percent_decode_keyword (const char *in){ char *out; char *ret; unsigned int rpos; unsigned int wpos; unsigned int hx; out = GNUNET_strdup (in); rpos = 0; wpos = 0; while (out[rpos] != '\0') { if (out[rpos] == '%') { if (1 != sscanf (&out[rpos + 1], "%2X", &hx)) { GNUNET_free (out); return NULL; } rpos += 3; if (hx == '"') continue; /* skip double quote */ out[wpos++] = (char) hx; } else { out[wpos++] = out[rpos++]; } } out[wpos] = '\0'; if (out[0] == '+') { ret = GNUNET_strdup (out); } else { /* need to prefix with space */ ret = GNUNET_malloc (strlen (out) + 2); strcpy (ret, " "); strcat (ret, out); } GNUNET_free (out); return ret;}/** * Parses an ECRS search URI. * * @param uri an uri string * @param keyword will be set to an array with the keywords * @return GNUNET_SYSERR if this is not a search URI, otherwise * the number of keywords placed in the array */static intparseKeywordURI (struct GNUNET_GE_Context *ectx, const char *uri, char ***keywords){ unsigned int pos; int ret; int iret; int i; size_t slen; char *dup; int saw_quote; GNUNET_GE_ASSERT (ectx, uri != NULL); slen = strlen (uri); pos = strlen (GNUNET_ECRS_URI_PREFIX); if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) return GNUNET_SYSERR; if (0 != strncmp (&uri[pos], GNUNET_ECRS_SEARCH_INFIX, strlen (GNUNET_ECRS_SEARCH_INFIX))) return GNUNET_SYSERR; pos += strlen (GNUNET_ECRS_SEARCH_INFIX); if (slen == pos) { /* no keywords */ (*keywords) = NULL; return 0; } if ((uri[slen - 1] == '+') || (uri[pos] == '+')) return GNUNET_SYSERR; /* no keywords / malformed */ ret = 1; saw_quote = 0; for (i = pos; i < slen; i++) { if ((uri[i] == '%') && (&uri[i] == strstr (&uri[i], "%22"))) { saw_quote = (saw_quote + 1) % 2; i += 3; continue; } if ((uri[i] == '+') && (saw_quote == 0)) { ret++; if (uri[i - 1] == '+') return GNUNET_SYSERR; /* "++" not allowed */ } } if (saw_quote == 1) return GNUNET_SYSERR; /* quotes not balanced */ iret = ret; dup = GNUNET_strdup (uri); (*keywords) = GNUNET_malloc (ret * sizeof (char *)); for (i = 0; i < ret; i++) (*keywords)[i] = NULL; for (i = slen - 1; i >= pos; i--) { if ((uri[i] == '%') && (&uri[i] == strstr (&uri[i], "%22"))) { saw_quote = (saw_quote + 1) % 2; i += 3; continue; } if ((dup[i] == '+') && (saw_quote == 0)) { (*keywords)[--ret] = percent_decode_keyword (&dup[i + 1]); if (NULL == (*keywords)[ret]) goto CLEANUP; dup[i] = '\0'; } } (*keywords)[--ret] = percent_decode_keyword (&dup[pos]); if (NULL == (*keywords)[ret]) goto CLEANUP; GNUNET_GE_ASSERT (ectx, ret == 0); GNUNET_free (dup); return iret;CLEANUP: for (i = 0; i < ret; i++) GNUNET_free_non_null ((*keywords)[i]); GNUNET_free (*keywords); *keywords = NULL; GNUNET_free (dup); return GNUNET_SYSERR;}/** * Parses an AFS namespace / subspace identifier URI. * * @param uri an uri string * @param namespace set to the namespace ID * @param identifier set to the ID in the namespace * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a namespace URI */static intparseSubspaceURI (struct GNUNET_GE_Context *ectx, const char *uri, GNUNET_HashCode * namespace, char **identifier){ unsigned int pos; size_t slen; char *up; GNUNET_GE_ASSERT (ectx, uri != NULL); slen = strlen (uri); pos = strlen (GNUNET_ECRS_URI_PREFIX); if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) return GNUNET_SYSERR; if (0 != strncmp (&uri[pos], GNUNET_ECRS_SUBSPACE_INFIX, strlen (GNUNET_ECRS_SUBSPACE_INFIX))) return GNUNET_SYSERR; pos += strlen (GNUNET_ECRS_SUBSPACE_INFIX); if ((slen < pos + sizeof (GNUNET_EncName) + 1) || (!((uri[pos + sizeof (GNUNET_EncName) - 1] == '/') || (uri[pos + sizeof (GNUNET_EncName) - 1] == '\\')))) return GNUNET_SYSERR; up = GNUNET_strdup (uri); up[pos + sizeof (GNUNET_EncName) - 1] = '\0'; if ((GNUNET_OK != GNUNET_enc_to_hash (&up[pos], namespace))) { GNUNET_free (up); return GNUNET_SYSERR; } *identifier = GNUNET_strdup (&up[pos + sizeof (GNUNET_EncName)]); GNUNET_free (up); return GNUNET_OK;}/** * Parses an URI that identifies a file * * @param uri an uri string * @param fi the file identifier * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a file URI */static intparseFileURI (struct GNUNET_GE_Context *ectx, const char *uri, GNUNET_EC_FileIdentifier * fi){ unsigned int pos; size_t slen; char *dup; GNUNET_GE_ASSERT (ectx, uri != NULL); slen = strlen (uri); pos = strlen (GNUNET_ECRS_URI_PREFIX); if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) return GNUNET_SYSERR; if (0 != strncmp (&uri[pos], GNUNET_ECRS_FILE_INFIX, strlen (GNUNET_ECRS_FILE_INFIX))) return GNUNET_SYSERR; pos += strlen (GNUNET_ECRS_FILE_INFIX); if ((slen < pos + 2 * sizeof (GNUNET_EncName) + 1) || (uri[pos + sizeof (GNUNET_EncName) - 1] != '.') || (uri[pos + sizeof (GNUNET_EncName) * 2 - 1] != '.')) return GNUNET_SYSERR; dup = GNUNET_strdup (uri); dup[pos + sizeof (GNUNET_EncName) - 1] = '\0'; dup[pos + sizeof (GNUNET_EncName) * 2 - 1] = '\0'; if ((GNUNET_OK != GNUNET_enc_to_hash (&dup[pos], &fi->chk.key)) || (GNUNET_OK != GNUNET_enc_to_hash (&dup[pos + sizeof (GNUNET_EncName)], &fi->chk.query)) || (1 != SSCANF (&dup[pos + sizeof (GNUNET_EncName) * 2], "%llu", &fi->file_length))) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -