📄 uri.c
字号:
int retries = 0; /* Strip starting spaces */ while (*url == ' ') url++; if (!*url) return NULL; newurl = expand_tilde(url); /* XXX: Post data copy. */ if (!newurl) return NULL;parse_uri: /* Yay a goto loop. If we get some URI parse error and try to * fix it we go back to here and try again. */ /* Ordinary parse */ uri_errno = parse_uri(&uri, newurl); /* Bail out if the same error occurs twice */ if (uri_errno == prev_errno || retries++ > MAX_TRANSLATION_ATTEMPTS) { if (retries > MAX_TRANSLATION_ATTEMPTS) { ERROR("Maximum number of parsing attempts exceeded " "for %s.", url); } mem_free(newurl); return NULL; } prev_errno = uri_errno; switch (uri_errno) { case URI_ERRNO_OK: /* Fix translation of 1.2.3.4:5 so IP address part won't be * interpreted as the protocol name. */ if (uri.protocol == PROTOCOL_UNKNOWN) { unsigned char *ipscan = newurl; /* FIXME: This is looking more and more like the code * for the URI_ERRNO_INVALID_PROTOCOL label and * find_uri_protocol(). Unite? --jonas */ /* Check if it could be just some local file with ':' * in its name. */ if (check_uri_file(newurl) >= 0) { struct string str; if (!init_string(&str)) return NULL; add_to_string(&str, "file://"); if (!dir_sep(*newurl)) add_to_string(&str, "./"); encode_file_uri_string(&str, newurl); mem_free(newurl); newurl = str.source; /* Work around the infinite loop prevention */ prev_errno = URI_ERRNO_EMPTY; goto parse_uri; } /* Check if it's <IP-address>:<port> */ while (isdigit(*ipscan) || *ipscan == '.') ipscan++; if (!*ipscan || *ipscan == ':' || *ipscan == '/') { insert_in_string(&newurl, 0, "http://", 7); /* Work around the infinite loop prevention */ prev_errno = URI_ERRNO_EMPTY; goto parse_uri; } } /* If file:// URI is transformed we need to reparse. */ if (uri.protocol == PROTOCOL_FILE && cwd && *cwd && transform_file_url(&uri, cwd)) return normalize_uri_reparse(struri(&uri)); /* Translate the proxied URI too if proxy:// */ if (uri.protocol == PROTOCOL_PROXY) { unsigned char *data = translate_url(uri.data, cwd); int pos = uri.data - struri(&uri); if (!data) break; struri(&uri)[pos] = 0; insert_in_string(&struri(&uri), pos, data, strlen(data)); mem_free(data); return normalize_uri_reparse(struri(&uri)); } return normalize_uri_noparse(&uri); case URI_ERRNO_TOO_MANY_SLASHES: { unsigned char *from, *to; assert(uri.string[uri.protocollen] == ':' && uri.string[uri.protocollen + 1] == '/' && uri.string[uri.protocollen + 2] == '/'); from = to = uri.string + uri.protocollen + 3; while (*from == '/') from++; assert(to < from); memmove(to, from, strlen(from) + 1); goto parse_uri; } case URI_ERRNO_NO_SLASHES: { /* Try prefix:some.url -> prefix://some.url.. */ int slashes = 2; /* Check if only one '/' is needed. */ if (uri.string[uri.protocollen + 1] == '/') slashes--; insert_in_string(&newurl, uri.protocollen + 1, "//", slashes); goto parse_uri; } case URI_ERRNO_TRAILING_DOTS: { /* Trim trailing '.'s */ unsigned char *from = uri.host + uri.hostlen; unsigned char *to = from; assert(uri.host < to && to[-1] == '.' && *from != '.'); while (uri.host < to && to[-1] == '.') to--; assert(to < from); memmove(to, from, strlen(from) + 1); goto parse_uri; } case URI_ERRNO_NO_PORT_COLON: assert(uri.portlen == 0 && uri.string < uri.port && uri.port[-1] == ':'); memmove(uri.port - 1, uri.port, strlen(uri.port) + 1); goto parse_uri; case URI_ERRNO_NO_HOST_SLASH: { int offset = uri.port ? uri.port + uri.portlen - struri(&uri) : uri.host + uri.hostlen - struri(&uri) + uri.ipv6 /* ']' */; assertm(uri.host, "uri.host not set after no host slash error"); insert_in_string(&newurl, offset, "/", 1); goto parse_uri; } case URI_ERRNO_INVALID_PROTOCOL: { /* No protocol name */ enum protocol protocol = find_uri_protocol(newurl); struct string str; if (!init_string(&str)) return NULL; switch (protocol) { case PROTOCOL_FTP: add_to_string(&str, "ftp://"); encode_uri_string(&str, newurl, 0); break; case PROTOCOL_HTTP: add_to_string(&str, "http://"); add_to_string(&str, newurl); break; case PROTOCOL_FILE: default: add_to_string(&str, "file://"); if (!dir_sep(*newurl)) add_to_string(&str, "./"); encode_file_uri_string(&str, newurl); } mem_free(newurl); newurl = str.source; goto parse_uri; } case URI_ERRNO_EMPTY: case URI_ERRNO_IPV6_SECURITY: case URI_ERRNO_NO_HOST: case URI_ERRNO_INVALID_PORT: case URI_ERRNO_INVALID_PORT_RANGE: /* None of these can be handled properly. */ break; } mem_free(newurl); return NULL;}struct uri *get_composed_uri(struct uri *uri, enum uri_component components){ unsigned char *string; assert(uri); if_assert_failed return NULL; string = get_uri_string(uri, components); if (!string) return NULL; uri = get_uri(string, 0); mem_free(string); return uri;}struct uri *get_translated_uri(unsigned char *uristring, unsigned char *cwd){ struct uri *uri; uristring = translate_url(uristring, cwd); if (!uristring) return NULL; uri = get_uri(uristring, 0); mem_free(uristring); return uri;}unsigned char *get_extension_from_uri(struct uri *uri){ unsigned char *extension = NULL; int afterslash = 1; unsigned char *pos = uri->data; assert(pos); for (; *pos && !end_of_dir(*pos); pos++) { if (!afterslash && !extension && *pos == '.') { extension = pos; } else if (is_uri_dir_sep(uri, *pos)) { extension = NULL; afterslash = 1; } else { afterslash = 0; } } if (extension && extension < pos) return memacpy(extension, pos - extension); return NULL;}/* URI encoding, escaping unallowed characters. */static inline intsafe_char(unsigned char c){ /* RFC 2396, Page 8, Section 2.3 ;-) */ return isident(c) || c == '.' || c == '!' || c == '~' || c == '*' || c == '\''|| c == '(' || c == ')';}voidencode_uri_string(struct string *string, unsigned char *name, int convert_slashes){ unsigned char n[4]; n[0] = '%'; n[3] = '\0'; for (; *name; name++) {#if 0 /* This is probably correct only for query part of URI..? */ if (*name == ' ') add_char_to_string(data, len, '+'); else#endif if (safe_char(*name) || (!convert_slashes && *name == '/')) { add_char_to_string(string, *name); } else { /* Hex it. */ n[1] = hx((((int) *name) & 0xF0) >> 4); n[2] = hx(((int) *name) & 0xF); add_bytes_to_string(string, n, sizeof(n) - 1); } }}/* This function is evil, it modifies its parameter. *//* XXX: but decoded string is _never_ longer than encoded string so it's an * efficient way to do that, imho. --Zas */voiddecode_uri(unsigned char *src){ unsigned char *dst = src; unsigned char c; do { c = *src++; if (c == '%') { int x1 = unhx(*src); if (x1 >= 0) { int x2 = unhx(*(src + 1)); if (x2 >= 0) { x1 = (x1 << 4) + x2; if (x1 != 0) { /* don't allow %00 */ c = (unsigned char) x1; src += 2; } } }#if 0 } else if (c == '+') { /* As the comment in encode_uri_string suggests, '+' * should only be decoded in the query part of a URI * (should that be 'URL'?). I'm not bold enough to * disable this code, tho. -- Miciah */ c = ' ';#endif } *dst++ = c; } while (c != '\0');}voiddecode_uri_string(struct string *string){ decode_uri(string->source); string->length = strlen(string->source);}voiddecode_uri_for_display(unsigned char *src){ decode_uri(src); for (; *src; src++) if (!isprint(*src) || iscntrl(*src)) *src = '*';}voiddecode_uri_string_for_display(struct string *string){ decode_uri_for_display(string->source); string->length = strlen(string->source);}/* URI list */#define URI_LIST_GRANULARITY 0x3#define realloc_uri_list(list) \ mem_align_alloc(&(list)->uris, (list)->size, (list)->size + 1, \ struct uri *, URI_LIST_GRANULARITY)struct uri *add_to_uri_list(struct uri_list *list, struct uri *uri){ if (!realloc_uri_list(list)) return NULL; list->uris[list->size++] = get_uri_reference(uri); return uri;};voidfree_uri_list(struct uri_list *list){ struct uri *uri; int index; if (!list->uris) return; foreach_uri (uri, index, list) { done_uri(uri); } mem_free(list->uris); list->size = 0;}/* URI cache */struct uri_cache_entry { struct uri uri; unsigned char string[1];};struct uri_cache { struct hash *map; struct object object;};static struct uri_cache uri_cache;#ifdef CONFIG_DEBUGstatic inline voidcheck_uri_sanity(struct uri *uri){ int pos; for (pos = 0; pos < uri->protocollen; pos++) if (isupper(uri->string[pos])) goto error; if (uri->hostlen) for (pos = 0; pos < uri->hostlen; pos++) if (isupper(uri->host[pos])) goto error; return;error: INTERNAL("Uppercase letters detected in protocol or host part (%s).", struri(uri));}#else#define check_uri_sanity(uri)#endifstatic inline struct uri_cache_entry *get_uri_cache_entry(unsigned char *string, int length){ struct uri_cache_entry *entry; struct hash_item *item; assert(string && length > 0); if_assert_failed return NULL; item = get_hash_item(uri_cache.map, string, length); if (item) return item->value; /* Setup a new entry */ entry = mem_calloc(1, sizeof(*entry) + length); if (!entry) return NULL; object_nolock(&entry->uri, "uri"); memcpy(&entry->string, string, length); string = entry->string; if (parse_uri(&entry->uri, string) != URI_ERRNO_OK || !add_hash_item(uri_cache.map, string, length, entry)) { mem_free(entry); return NULL; } object_lock(&uri_cache); return entry;}struct uri *get_uri(unsigned char *string, enum uri_component components){ struct uri_cache_entry *entry; assert(string); if (components) { struct uri uri; if (parse_uri(&uri, string) != URI_ERRNO_OK) return NULL; return get_composed_uri(&uri, components); } if (!is_object_used(&uri_cache)) { uri_cache.map = init_hash(hash_size(3), strhash); if (!uri_cache.map) return NULL; object_nolock(&uri_cache, "uri_cache"); } entry = get_uri_cache_entry(string, strlen(string)); if (!entry) { if (!is_object_used(&uri_cache)) free_hash(uri_cache.map); return NULL; } check_uri_sanity(&entry->uri); object_nolock(&entry->uri, "uri"); object_lock(&entry->uri); return &entry->uri;}voiddone_uri(struct uri *uri){ unsigned char *string = struri(uri); int length = strlen(string); struct hash_item *item; struct uri_cache_entry *entry; assert(is_object_used(&uri_cache)); object_unlock(uri); if (is_object_used(uri)) return; item = get_hash_item(uri_cache.map, string, length); entry = item ? item->value : NULL; assertm(entry, "Releasing unknown URI [%s]", string); del_hash_item(uri_cache.map, item); mem_free(entry); /* Last URI frees the cache */ object_unlock(&uri_cache); if (!is_object_used(&uri_cache)) free_hash(uri_cache.map);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -