Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2006 Richard Wilson <info@tinct.net>
  3.  * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
  4.  * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
  5.  *
  6.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  7.  *
  8.  * NetSurf is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; version 2 of the License.
  11.  *
  12.  * NetSurf is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19.  */
  20.  
  21. /** \file
  22.  * URL parsing and joining (implementation).
  23.  */
  24.  
  25. #include <sys/types.h>
  26. #include <assert.h>
  27. #include <ctype.h>
  28. #include <stdbool.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <unistd.h>
  32.  
  33. #include "curl/curl.h"
  34. #include "utils/config.h"
  35. #include "utils/log.h"
  36. #include "utils/url.h"
  37. #include "utils/utils.h"
  38.  
  39. struct url_components_internal {
  40.         char *buffer;   /* buffer used for all the following data */
  41.         char *scheme;
  42.         char *authority;
  43.         char *path;
  44.         char *query;
  45.         char *fragment;
  46. };
  47.  
  48.  
  49. regex_t url_re, url_up_re;
  50.  
  51. /**
  52.  * Initialise URL routines.
  53.  *
  54.  * Compiles regular expressions required by the url_ functions.
  55.  */
  56.  
  57. void url_init(void)
  58. {
  59.         /* regex from RFC 2396 */
  60.         regcomp_wrapper(&url_re, "^[[:space:]]*"
  61. #define URL_RE_SCHEME 2
  62.                         "(([a-zA-Z][-a-zA-Z0-9+.]*):)?"
  63. #define URL_RE_AUTHORITY 4
  64.                         "(//([^/?#[:space:]]*))?"
  65. #define URL_RE_PATH 5
  66.                         "([^?#[:space:]]*)"
  67. #define URL_RE_QUERY 7
  68.                         "(\\?([^#[:space:]]*))?"
  69. #define URL_RE_FRAGMENT 9
  70.                         "(#([^[:space:]]*))?"
  71.                         "[[:space:]]*$", REG_EXTENDED);
  72.         regcomp_wrapper(&url_up_re,
  73.                         "/([^/]?|[.][^./]|[^./][.]|[^./][^./]|[^/][^/][^/]+)"
  74.                         "/[.][.](/|$)",
  75.                         REG_EXTENDED);
  76. }
  77.  
  78.  
  79. /**
  80.  * Check whether a host string is an IP address.  It should support and
  81.  * detect IPv4 addresses (all of dotted-quad or subsets, decimal or
  82.  * hexadecimal notations) and IPv6 addresses (including those containing
  83.  * embedded IPv4 addresses.)
  84.  *
  85.  * \param  host a hostname terminated by '\0'
  86.  * \return true if the hostname is an IP address, false otherwise
  87.  */
  88. bool url_host_is_ip_address(const char *host)
  89. {
  90.         struct in_addr ipv4;
  91.         size_t host_len = strlen(host);
  92.         const char *sane_host;
  93.         const char *slash;
  94. #ifndef NO_IPV6
  95.         struct in6_addr ipv6;
  96.         char ipv6_addr[64];
  97. #endif
  98.         /* FIXME TODO: Some parts of urldb.c (and perhaps other parts of
  99.          * NetSurf) make confusions between hosts and "prefixes", we can
  100.          * sometimes be erroneously passed more than just a host.  Sometimes
  101.          * we may be passed trailing slashes, or even whole path segments.
  102.          * A specific criminal in this class is urldb_iterate_partial, which
  103.          * takes a prefix to search for, but passes that prefix to functions
  104.          * that expect only hosts.
  105.          *
  106.          * For the time being, we will accept such calls; we check if there
  107.          * is a / in the host parameter, and if there is, we take a copy and
  108.          * replace the / with a \0.  This is not a permanent solution; we
  109.          * should search through NetSurf and find all the callers that are
  110.          * in error and fix them.  When doing this task, it might be wise
  111.          * to replace the hideousness below with code that doesn't have to do
  112.          * this, and add assert(strchr(host, '/') == NULL); somewhere.
  113.          * -- rjek - 2010-11-04
  114.          */
  115.  
  116.         slash = strchr(host, '/');
  117.         if (slash == NULL) {
  118.                 sane_host = host;
  119.         } else {
  120.                 char *c = strdup(host);
  121.                 c[slash - host] = '\0';
  122.                 sane_host = c;
  123.                 host_len = slash - host - 1;
  124.                 LOG(("WARNING: called with non-host '%s'", host));
  125.         }
  126.  
  127.         if (strspn(sane_host, "0123456789abcdefABCDEF[].:") < host_len)
  128.                 goto out_false;
  129.  
  130.         if (inet_aton(sane_host, &ipv4) != 0) {
  131.                 /* This can only be a sane IPv4 address if it contains 3 dots.
  132.                  * Helpfully, inet_aton is happy to treat "a", "a.b", "a.b.c",
  133.                  * and "a.b.c.d" as valid IPv4 address strings where we only
  134.                  * support the full, dotted-quad, form.
  135.                  */
  136.                 int num_dots = 0;
  137.                 size_t index;
  138.  
  139.                 for (index = 0; index < host_len; index++) {
  140.                         if (sane_host[index] == '.')
  141.                                 num_dots++;
  142.                 }
  143.  
  144.                 if (num_dots == 3)
  145.                         goto out_true;
  146.                 else
  147.                         goto out_false;
  148.         }
  149.  
  150. #ifndef NO_IPV6
  151.         if (sane_host[0] != '[' || sane_host[host_len] != ']')
  152.                 goto out_false;
  153.  
  154.         strncpy(ipv6_addr, sane_host + 1, sizeof(ipv6_addr));
  155.         ipv6_addr[sizeof(ipv6_addr) - 1] = '\0';
  156.  
  157.         if (inet_pton(AF_INET6, ipv6_addr, &ipv6) == 1)
  158.                 goto out_true;
  159. #endif
  160.  
  161. out_false:
  162.         if (slash != NULL) free((void *)sane_host);
  163.         return false;
  164.  
  165. out_true:
  166.         if (slash != NULL) free((void *)sane_host);
  167.         return true;
  168. }
  169.  
  170. /**
  171.  * Split a URL into separate components
  172.  *
  173.  * URLs passed to this function are assumed to be valid and no error checking
  174.  * or recovery is attempted.
  175.  *
  176.  * See RFC 3986 for reference.
  177.  *
  178.  * \param  url       a valid absolute or relative URL
  179.  * \param  result    pointer to buffer to hold components
  180.  * \return  URL_FUNC_OK on success
  181.  */
  182.  
  183. static url_func_result url_get_components(const char *url,
  184.                 struct url_components *result)
  185. {
  186.         int storage_length;
  187.         char *storage_end;
  188.         const char *scheme;
  189.         const char *authority;
  190.         const char *path;
  191.         const char *query;
  192.         const char *fragment;
  193.         struct url_components_internal *internal;
  194.  
  195.         assert(url);
  196.  
  197.         /* clear our return value */
  198.         internal = (struct url_components_internal *)result;
  199.         memset(result, 0x00, sizeof(struct url_components));
  200.  
  201.         /* get enough storage space for a URL with termination at each node */
  202.         storage_length = strlen(url) + 8;
  203.         internal->buffer = malloc(storage_length);
  204.         if (!internal->buffer)
  205.                 return URL_FUNC_NOMEM;
  206.         storage_end = internal->buffer;
  207.  
  208.         /* look for a valid scheme */
  209.         scheme = url;
  210.         if (isalpha(*scheme)) {
  211.                 for (scheme = url + 1;
  212.                                 ((*scheme != ':') && (*scheme != '\0'));
  213.                                 scheme++) {
  214.                         if (!isalnum(*scheme) && (*scheme != '+') &&
  215.                                         (*scheme != '-') && (*scheme != '.'))
  216.                                 break;
  217.                 }
  218.  
  219.                 if (*scheme == ':') {
  220.                         memcpy(storage_end, url, scheme - url);
  221.                         storage_end[scheme - url] = '\0';
  222.                         result->scheme = storage_end;
  223.                         storage_end += scheme - url + 1;
  224.                         scheme++;
  225.                 } else {
  226.                         scheme = url;
  227.                 }
  228.         }
  229.  
  230.  
  231.         /* look for an authority */
  232.         authority = scheme;
  233.         if ((authority[0] == '/') && (authority[1] == '/')) {
  234.                 authority = strpbrk(scheme + 2, "/?#");
  235.                 if (!authority)
  236.                         authority = scheme + strlen(scheme);
  237.                 memcpy(storage_end, scheme + 2, authority - scheme - 2);
  238.                 storage_end[authority - scheme - 2] = '\0';
  239.                 result->authority = storage_end;
  240.                 storage_end += authority - scheme - 1;
  241.         }
  242.  
  243.  
  244.         /* look for a path */
  245.         path = authority;
  246.         if ((*path != '?') && (*path != '#') && (*path != '\0')) {
  247.                 path = strpbrk(path, "?#");
  248.                 if (!path)
  249.                         path = authority + strlen(authority);
  250.                 memcpy(storage_end, authority, path - authority);
  251.                 storage_end[path - authority] = '\0';
  252.                 result->path = storage_end;
  253.                 storage_end += path - authority + 1;
  254.         }
  255.  
  256.  
  257.         /* look for a query */
  258.         query = path;
  259.         if (*query == '?') {
  260.                 query = strchr(query, '#');
  261.                 if (!query)
  262.                         query = path + strlen(path);
  263.                 memcpy(storage_end, path + 1, query - path - 1);
  264.                 storage_end[query - path - 1] = '\0';
  265.                 result->query = storage_end;
  266.                 storage_end += query - path;
  267.         }
  268.  
  269.  
  270.         /* look for a fragment */
  271.         fragment = query;
  272.         if (*fragment == '#') {
  273.                 fragment = query + strlen(query);
  274.  
  275.                 /* make a copy of the result for the caller */
  276.                 memcpy(storage_end, query + 1, fragment - query - 1);
  277.                 storage_end[fragment - query - 1] = '\0';
  278.                 result->fragment = storage_end;
  279.                 storage_end += fragment - query;
  280.         }
  281.  
  282.         assert((result->buffer + storage_length) >= storage_end);
  283.         return URL_FUNC_OK;
  284. }
  285.  
  286.  
  287. /**
  288.  * Reform a URL from separate components
  289.  *
  290.  * See RFC 3986 for reference.
  291.  *
  292.  * \param  components  the components to reform into a URL
  293.  * \return  a new URL allocated on the heap, or NULL on failure
  294.  */
  295.  
  296. static char *url_reform_components(const struct url_components *components)
  297. {
  298.         int scheme_len = 0, authority_len = 0, path_len = 0, query_len = 0,
  299.                         fragment_len = 0;
  300.         char *result, *url;
  301.  
  302.         /* 5.3 */
  303.         if (components->scheme)
  304.                 scheme_len = strlen(components->scheme) + 1;
  305.         if (components->authority)
  306.                 authority_len = strlen(components->authority) + 2;
  307.         if (components->path)
  308.                 path_len = strlen(components->path);
  309.         if (components->query)
  310.                 query_len = strlen(components->query) + 1;
  311.         if (components->fragment)
  312.                 fragment_len = strlen(components->fragment) + 1;
  313.  
  314.         /* claim memory */
  315.         url = result = malloc(scheme_len + authority_len + path_len +
  316.                         query_len + fragment_len + 1);
  317.         if (!url) {
  318.                 LOG(("malloc failed"));
  319.                 return NULL;
  320.         }
  321.  
  322.         /* rebuild URL */
  323.         if (components->scheme) {
  324.                 sprintf(url, "%s:", components->scheme);
  325.                 url += scheme_len;
  326.         }
  327.         if (components->authority) {
  328.                 sprintf(url, "//%s", components->authority);
  329.                 url += authority_len;
  330.         }
  331.         if (components->path) {
  332.                 sprintf(url, "%s", components->path);
  333.                 url += path_len;
  334.         }
  335.         if (components->query) {
  336.                 sprintf(url, "?%s", components->query);
  337.                 url += query_len;
  338.         }
  339.         if (components->fragment)
  340.                 sprintf(url, "#%s", components->fragment);
  341.         return result;
  342. }
  343.  
  344.  
  345. /**
  346.  * Release some url components from memory
  347.  *
  348.  * \param  result  pointer to buffer containing components
  349.  */
  350. static void url_destroy_components(const struct url_components *components)
  351. {
  352.         const struct url_components_internal *internal;
  353.  
  354.         assert(components);
  355.  
  356.         internal = (const struct url_components_internal *)components;
  357.         if (internal->buffer)
  358.                 free(internal->buffer);
  359. }
  360.  
  361.  
  362. /**
  363.  * Resolve a relative URL to absolute form.
  364.  *
  365.  * \param  rel     relative URL
  366.  * \param  base    base URL, must be absolute and cleaned as by nsurl_create()
  367.  * \param  result  pointer to pointer to buffer to hold absolute url
  368.  * \return  URL_FUNC_OK on success
  369.  */
  370.  
  371. url_func_result url_join(const char *rel, const char *base, char **result)
  372. {
  373.         url_func_result status = URL_FUNC_NOMEM;
  374.         struct url_components_internal base_components = {0,0,0,0,0,0};
  375.         struct url_components_internal *base_ptr = &base_components;
  376.         struct url_components_internal rel_components = {0,0,0,0,0,0};
  377.         struct url_components_internal *rel_ptr = &rel_components;
  378.         struct url_components_internal merged_components = {0,0,0,0,0,0};
  379.         struct url_components_internal *merged_ptr = &merged_components;
  380.         char *merge_path = NULL, *split_point;
  381.         char *input, *output, *start = NULL;
  382.         int len, buf_len;
  383.  
  384.         (*result) = 0;
  385.  
  386.         assert(base);
  387.         assert(rel);
  388.  
  389.  
  390.         /* break down the relative URL (not cached, corruptable) */
  391.         status = url_get_components(rel, (struct url_components *) rel_ptr);
  392.         if (status != URL_FUNC_OK) {
  393.                 LOG(("relative url '%s' failed to get components", rel));
  394.                 return URL_FUNC_FAILED;
  395.         }
  396.  
  397.         /* [1] relative URL is absolute, use it entirely */
  398.         merged_components = rel_components;
  399.         if (rel_components.scheme)
  400.                 goto url_join_reform_url;
  401.  
  402.         /* break down the base URL (possibly cached, not corruptable) */
  403.         status = url_get_components(base, (struct url_components *) base_ptr);
  404.         if (status != URL_FUNC_OK) {
  405.                 url_destroy_components((struct url_components *) rel_ptr);
  406.                 LOG(("base url '%s' failed to get components", base));
  407.                 return URL_FUNC_FAILED;
  408.         }
  409.  
  410.         /* [2] relative authority takes presidence */
  411.         merged_components.scheme = base_components.scheme;
  412.         if (rel_components.authority)
  413.                 goto url_join_reform_url;
  414.  
  415.         /* [3] handle empty paths */
  416.         merged_components.authority = base_components.authority;
  417.         if (!rel_components.path) {
  418.                 merged_components.path = base_components.path;
  419.                 if (!rel_components.query)
  420.                         merged_components.query = base_components.query;
  421.                 goto url_join_reform_url;
  422.         }
  423.  
  424.         /* [4] handle valid paths */
  425.         if (rel_components.path[0] == '/')
  426.                 merged_components.path = rel_components.path;
  427.         else {
  428.                 /* 5.2.3 */
  429.                 if ((base_components.authority) && (!base_components.path)) {
  430.                         merge_path = malloc(strlen(rel_components.path) + 2);
  431.                         if (!merge_path) {
  432.                                 LOG(("malloc failed"));
  433.                                 goto url_join_no_mem;
  434.                         }
  435.                         sprintf(merge_path, "/%s", rel_components.path);
  436.                         merged_components.path = merge_path;
  437.                 } else {
  438.                         split_point = base_components.path ?
  439.                                         strrchr(base_components.path, '/') :
  440.                                         NULL;
  441.                         if (!split_point) {
  442.                                 merged_components.path = rel_components.path;
  443.                         } else {
  444.                                 len = ++split_point - base_components.path;
  445.                                 buf_len = len + 1 + strlen(rel_components.path);
  446.                                 merge_path = malloc(buf_len);
  447.                                 if (!merge_path) {
  448.                                         LOG(("malloc failed"));
  449.                                         goto url_join_no_mem;
  450.                                 }
  451.                                 memcpy(merge_path, base_components.path, len);
  452.                                 memcpy(merge_path + len, rel_components.path,
  453.                                                 strlen(rel_components.path));
  454.                                 merge_path[buf_len - 1] = '\0';
  455.                                 merged_components.path = merge_path;
  456.                         }
  457.                 }
  458.         }
  459.  
  460. url_join_reform_url:
  461.         /* 5.2.4 */
  462.         input = merged_components.path;
  463.         if ((input) && (strchr(input, '.'))) {
  464.                 /* [1] remove all dot references */
  465.                 output = start = malloc(strlen(input) + 1);
  466.                 if (!output) {
  467.                         LOG(("malloc failed"));
  468.                         goto url_join_no_mem;
  469.                 }
  470.                 merged_components.path = output;
  471.                 *output = '\0';
  472.  
  473.                 while (*input != '\0') {
  474.                         /* [2A] */
  475.                         if (input[0] == '.') {
  476.                                 if (input[1] == '/') {
  477.                                         input = input + 2;
  478.                                         continue;
  479.                                 } else if ((input[1] == '.') &&
  480.                                                 (input[2] == '/')) {
  481.                                         input = input + 3;
  482.                                         continue;
  483.                                 }
  484.                         }
  485.  
  486.                         /* [2B] */
  487.                         if ((input[0] == '/') && (input[1] == '.')) {
  488.                                 if (input[2] == '/') {
  489.                                         input = input + 2;
  490.                                         continue;
  491.                                 } else if (input[2] == '\0') {
  492.                                         input = input + 1;
  493.                                         *input = '/';
  494.                                         continue;
  495.                                 }
  496.  
  497.                                 /* [2C] */
  498.                                 if ((input[2] == '.') && ((input[3] == '/') ||
  499.                                                 (input[3] == '\0'))) {
  500.                                         if (input[3] == '/') {
  501.                                                 input = input + 3;
  502.                                         } else {
  503.                                                 input = input + 2;
  504.                                                 *input = '/';
  505.                                         }
  506.  
  507.                                         if ((output > start) &&
  508.                                                         (output[-1] == '/'))
  509.                                                 *--output = '\0';
  510.                                         split_point = strrchr(start, '/');
  511.                                         if (!split_point)
  512.                                                 output = start;
  513.                                         else
  514.                                                 output = split_point;
  515.                                         *output = '\0';
  516.                                         continue;
  517.                                 }
  518.                         }
  519.  
  520.  
  521.                         /* [2D] */
  522.                         if (input[0] == '.') {
  523.                                 if (input[1] == '\0') {
  524.                                         input = input + 1;
  525.                                         continue;
  526.                                 } else if ((input[1] == '.') &&
  527.                                                 (input[2] == '\0')) {
  528.                                         input = input + 2;
  529.                                         continue;
  530.                                 }
  531.                         }
  532.  
  533.                         /* [2E] */
  534.                         if (*input == '/')
  535.                                 *output++ = *input++;
  536.                         while ((*input != '/') && (*input != '\0'))
  537.                                 *output++ = *input++;
  538.                         *output = '\0';
  539.                 }
  540.                 /* [3] */
  541.                 merged_components.path = start;
  542.         }
  543.  
  544.         /* 5.3 */
  545.         *result = url_reform_components((struct url_components *) merged_ptr);
  546.         if (!(*result))
  547.                 goto url_join_no_mem;
  548.  
  549.         /* return success */
  550.         status = URL_FUNC_OK;
  551.  
  552. url_join_no_mem:
  553.         free(start);
  554.         free(merge_path);
  555.         url_destroy_components((struct url_components *) base_ptr);
  556.         url_destroy_components((struct url_components *) rel_ptr);
  557.         return status;
  558. }
  559.  
  560.  
  561. /**
  562.  * Return the host name from an URL.
  563.  *
  564.  * \param  url     an absolute URL
  565.  * \param  result  pointer to pointer to buffer to hold host name
  566.  * \return  URL_FUNC_OK on success
  567.  */
  568.  
  569. url_func_result url_host(const char *url, char **result)
  570. {
  571.         url_func_result status;
  572.         struct url_components components;
  573.         const char *host_start, *host_end;
  574.  
  575.         assert(url);
  576.  
  577.         status = url_get_components(url, &components);
  578.         if (status == URL_FUNC_OK) {
  579.                 if (!components.authority) {
  580.                         url_destroy_components(&components);
  581.                         return URL_FUNC_FAILED;
  582.                 }
  583.                 host_start = strchr(components.authority, '@');
  584.                 host_start = host_start ? host_start + 1 : components.authority;
  585.  
  586.                 /* skip over an IPv6 address if there is one */
  587.                 if (host_start[0] == '[') {
  588.                         host_end = strchr(host_start, ']') + 1;
  589.                 } else {
  590.                         host_end = strchr(host_start, ':');
  591.                 }
  592.  
  593.                 if (!host_end)
  594.                         host_end = components.authority +
  595.                                         strlen(components.authority);
  596.  
  597.                 *result = malloc(host_end - host_start + 1);
  598.                 if (!(*result)) {
  599.                         url_destroy_components(&components);
  600.                         return URL_FUNC_FAILED;
  601.                 }
  602.                 memcpy((*result), host_start, host_end - host_start);
  603.                 (*result)[host_end - host_start] = '\0';
  604.         }
  605.         url_destroy_components(&components);
  606.         return status;
  607. }
  608.  
  609.  
  610. /**
  611.  * Return the scheme name from an URL.
  612.  *
  613.  * See RFC 3986, 3.1 for reference.
  614.  *
  615.  * \param  url     an absolute URL
  616.  * \param  result  pointer to pointer to buffer to hold scheme name
  617.  * \return  URL_FUNC_OK on success
  618.  */
  619.  
  620. url_func_result url_scheme(const char *url, char **result)
  621. {
  622.         url_func_result status;
  623.         struct url_components components;
  624.  
  625.         assert(url);
  626.  
  627.         status = url_get_components(url, &components);
  628.         if (status == URL_FUNC_OK) {
  629.                 if (!components.scheme) {
  630.                         status = URL_FUNC_FAILED;
  631.                 } else {
  632.                         *result = strdup(components.scheme);
  633.                         if (!(*result))
  634.                                 status = URL_FUNC_NOMEM;
  635.                 }
  636.         }
  637.         url_destroy_components(&components);
  638.         return status;
  639. }
  640.  
  641.  
  642. /**
  643.  * Extract path segment from an URL
  644.  *
  645.  * \param url     an absolute URL
  646.  * \param result  pointer to pointer to buffer to hold result
  647.  * \return URL_FUNC_OK on success
  648.  */
  649.  
  650. url_func_result url_path(const char *url, char **result)
  651. {
  652.         url_func_result status;
  653.         struct url_components components;
  654.  
  655.         assert(url);
  656.  
  657.         status = url_get_components(url, &components);
  658.         if (status == URL_FUNC_OK) {
  659.                 if (!components.path) {
  660.                         status = URL_FUNC_FAILED;
  661.                 } else {
  662.                         *result = strdup(components.path);
  663.                         if (!(*result))
  664.                                 status = URL_FUNC_NOMEM;
  665.                 }
  666.         }
  667.         url_destroy_components(&components);
  668.         return status;
  669. }
  670.  
  671. /**
  672.  * Attempt to find a nice filename for a URL.
  673.  *
  674.  * \param  url     an absolute URL
  675.  * \param  result  pointer to pointer to buffer to hold filename
  676.  * \param  remove_extensions  remove any extensions from the filename
  677.  * \return  URL_FUNC_OK on success
  678.  */
  679.  
  680. url_func_result url_nice(const char *url, char **result,
  681.                 bool remove_extensions)
  682. {
  683.         int m;
  684.         regmatch_t match[10];
  685.         regoff_t start, end;
  686.         size_t i;
  687.         char *dot;
  688.  
  689.         *result = 0;
  690.  
  691.         m = regexec(&url_re, url, 10, match, 0);
  692.         if (m) {
  693.                 LOG(("url '%s' failed to match regex", url));
  694.                 return URL_FUNC_FAILED;
  695.         }
  696.  
  697.         /* extract the last component of the path, if possible */
  698.         if (match[URL_RE_PATH].rm_so == -1 || match[URL_RE_PATH].rm_so ==
  699.                         match[URL_RE_PATH].rm_eo)
  700.                 goto no_path;  /* no path, or empty */
  701.         for (end = match[URL_RE_PATH].rm_eo - 1;
  702.                         end != match[URL_RE_PATH].rm_so && url[end] == '/';
  703.                         end--)
  704.                 ;
  705.         if (end == match[URL_RE_PATH].rm_so)
  706.                 goto no_path;  /* path is a string of '/' */
  707.         end++;
  708.         for (start = end - 1;
  709.                         start != match[URL_RE_PATH].rm_so && url[start] != '/';
  710.                         start--)
  711.                 ;
  712.         if (url[start] == '/')
  713.                 start++;
  714.  
  715.         if (!strncasecmp(url + start, "index.", 6) ||
  716.                         !strncasecmp(url + start, "default.", 8)) {
  717.                 /* try again */
  718.                 if (start == match[URL_RE_PATH].rm_so)
  719.                         goto no_path;
  720.                 for (end = start - 1;
  721.                                 end != match[URL_RE_PATH].rm_so &&
  722.                                 url[end] == '/';
  723.                                 end--)
  724.                         ;
  725.                 if (end == match[URL_RE_PATH].rm_so)
  726.                         goto no_path;
  727.                 end++;
  728.                 for (start = end - 1;
  729.                                 start != match[URL_RE_PATH].rm_so &&
  730.                                 url[start] != '/';
  731.                                 start--)
  732.                 ;
  733.                 if (url[start] == '/')
  734.                         start++;
  735.         }
  736.  
  737.         *result = malloc(end - start + 1);
  738.         if (!*result) {
  739.                 LOG(("malloc failed"));
  740.                 return URL_FUNC_NOMEM;
  741.         }
  742.         strncpy(*result, url + start, end - start);
  743.         (*result)[end - start] = 0;
  744.  
  745.         if (remove_extensions) {
  746.                 dot = strchr(*result, '.');
  747.                 if (dot && dot != *result)
  748.                         *dot = 0;
  749.         }
  750.  
  751.         return URL_FUNC_OK;
  752.  
  753. no_path:
  754.  
  755.         /* otherwise, use the host name, with '.' replaced by '_' */
  756.         if (match[URL_RE_AUTHORITY].rm_so != -1 &&
  757.                         match[URL_RE_AUTHORITY].rm_so !=
  758.                         match[URL_RE_AUTHORITY].rm_eo) {
  759.                 *result = malloc(match[URL_RE_AUTHORITY].rm_eo -
  760.                                 match[URL_RE_AUTHORITY].rm_so + 1);
  761.                 if (!*result) {
  762.                         LOG(("malloc failed"));
  763.                         return URL_FUNC_NOMEM;
  764.                 }
  765.                 strncpy(*result, url + match[URL_RE_AUTHORITY].rm_so,
  766.                                 match[URL_RE_AUTHORITY].rm_eo -
  767.                                 match[URL_RE_AUTHORITY].rm_so);
  768.                 (*result)[match[URL_RE_AUTHORITY].rm_eo -
  769.                                 match[URL_RE_AUTHORITY].rm_so] = 0;
  770.  
  771.                 for (i = 0; (*result)[i]; i++)
  772.                         if ((*result)[i] == '.')
  773.                                 (*result)[i] = '_';
  774.  
  775.                 return URL_FUNC_OK;
  776.         }
  777.  
  778.         return URL_FUNC_FAILED;
  779. }
  780.  
  781. /**
  782.  * Convert an escaped string to plain.
  783.  * \param result unescaped string owned by caller must be freed with free()
  784.  * \return  URL_FUNC_OK on success
  785.  */
  786. url_func_result url_unescape(const char *str, char **result)
  787. {
  788.         char *curlstr;
  789.         char *retstr;
  790.  
  791.         curlstr = curl_unescape(str, 0);
  792.         if (curlstr == NULL) {
  793.                 return URL_FUNC_NOMEM;
  794.         }
  795.  
  796.         retstr = strdup(curlstr);
  797.         curl_free(curlstr);
  798.  
  799.         if (retstr == NULL) {
  800.                 return URL_FUNC_NOMEM;
  801.         }
  802.  
  803.         *result = retstr;
  804.         return URL_FUNC_OK;
  805. }
  806.  
  807. /**
  808.  * Escape a string suitable for inclusion in an URL.
  809.  *
  810.  * \param  unescaped      the unescaped string
  811.  * \param  toskip         number of bytes to skip in unescaped string
  812.  * \param  sptoplus       true iff spaces should be converted to +
  813.  * \param  escexceptions  NULL or a string of characters excluded to be escaped
  814.  * \param  result         pointer to pointer to buffer to hold escaped string
  815.  * \return  URL_FUNC_OK on success
  816.  */
  817.  
  818. url_func_result url_escape(const char *unescaped, size_t toskip,
  819.                 bool sptoplus, const char *escexceptions, char **result)
  820. {
  821.         size_t len;
  822.         char *escaped, *d, *tmpres;
  823.         const char *c;
  824.  
  825.         if (!unescaped || !result)
  826.                 return URL_FUNC_FAILED;
  827.  
  828.         *result = NULL;
  829.  
  830.         len = strlen(unescaped);
  831.         if (len < toskip)
  832.                 return URL_FUNC_FAILED;
  833.         len -= toskip;
  834.  
  835.         escaped = malloc(len * 3 + 1);
  836.         if (!escaped)
  837.                 return URL_FUNC_NOMEM;
  838.  
  839.         for (c = unescaped + toskip, d = escaped; *c; c++) {
  840.                 /* Check if we should escape this byte.
  841.                  * '~' is unreserved and should not be percent encoded, if
  842.                  * you believe the spec; however, leaving it unescaped
  843.                  * breaks a bunch of websites, so we escape it anyway. */
  844.                 if (!isascii(*c)
  845.                         || (strchr(":/?#[]@" /* gen-delims */
  846.                                   "!$&'()*+,;=" /* sub-delims */
  847.                                   "<>%\"{}|\\^`~" /* others */, *c)
  848.                                 && (!escexceptions || !strchr(escexceptions, *c)))
  849.                         || *c <= 0x20 || *c == 0x7f) {
  850.                         if (*c == 0x20 && sptoplus) {
  851.                                 *d++ = '+';
  852.                         } else {
  853.                                 *d++ = '%';
  854.                                 *d++ = "0123456789ABCDEF"[((*c >> 4) & 0xf)];
  855.                                 *d++ = "0123456789ABCDEF"[(*c & 0xf)];
  856.                         }
  857.                 } else {
  858.                         /* unreserved characters: [a-zA-Z0-9-._] */
  859.                         *d++ = *c;
  860.                 }
  861.         }
  862.         *d++ = '\0';
  863.  
  864.         tmpres = malloc(d - escaped + toskip);
  865.         if (!tmpres) {
  866.                 free(escaped);
  867.                 return URL_FUNC_NOMEM;
  868.         }
  869.  
  870.         memcpy(tmpres, unescaped, toskip);
  871.         memcpy(tmpres + toskip, escaped, d - escaped);
  872.         *result = tmpres;
  873.  
  874.         free(escaped);
  875.  
  876.         return URL_FUNC_OK;
  877. }
  878.  
  879.  
  880. #ifdef TEST
  881.  
  882. int main(int argc, char *argv[])
  883. {
  884.         int i;
  885.         url_func_result res;
  886.         char *s;
  887.         url_init();
  888.         for (i = 1; i != argc; i++) {
  889. /*              printf("==> '%s'\n", argv[i]);
  890.                 res = url_normalize(argv[i], &s);
  891.                 if (res == URL_FUNC_OK) {
  892.                         printf("<== '%s'\n", s);
  893.                         free(s);
  894.                 }*/
  895. /*              printf("==> '%s'\n", argv[i]);
  896.                 res = url_host(argv[i], &s);
  897.                 if (res == URL_FUNC_OK) {
  898.                         printf("<== '%s'\n", s);
  899.                         free(s);
  900.                 }*/
  901.                 if (1 != i) {
  902.                         res = url_join(argv[i], argv[1], &s);
  903.                         if (res == URL_FUNC_OK) {
  904.                                 printf("'%s' + '%s' \t= '%s'\n", argv[1],
  905.                                                 argv[i], s);
  906.                                 free(s);
  907.                         }
  908.                 }
  909. /*              printf("'%s' => ", argv[i]);
  910.                 res = url_nice(argv[i], &s, true);
  911.                 if (res == URL_FUNC_OK) {
  912.                         printf("'%s', ", s);
  913.                         free(s);
  914.                 } else {
  915.                         printf("failed %u, ", res);
  916.                 }
  917.                 res = url_nice(argv[i], &s, false);
  918.                 if (res == URL_FUNC_OK) {
  919.                         printf("'%s', ", s);
  920.                         free(s);
  921.                 } else {
  922.                         printf("failed %u, ", res);
  923.                 }
  924.                 printf("\n");*/
  925.         }
  926.         return 0;
  927. }
  928.  
  929. void regcomp_wrapper(regex_t *preg, const char *regex, int cflags)
  930. {
  931.         char errbuf[200];
  932.         int r;
  933.         r = regcomp(preg, regex, cflags);
  934.         if (r) {
  935.                 regerror(r, preg, errbuf, sizeof errbuf);
  936.                 fprintf(stderr, "Failed to compile regexp '%s'\n", regex);
  937.                 fprintf(stderr, "error: %s\n", errbuf);
  938.                 exit(1);
  939.         }
  940. }
  941.  
  942. #endif
  943.