Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2011 Michael Drake <tlsa@netsurf-browser.org>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /** \file
  20.  * NetSurf URL handling (implementation).
  21.  */
  22.  
  23. #include <assert.h>
  24. #include <ctype.h>
  25. #include <libwapcaplet/libwapcaplet.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29. #include "utils/errors.h"
  30. #include "utils/log.h"
  31. #include "utils/nsurl.h"
  32. #include "utils/utils.h"
  33.  
  34.  
  35. /* Define to enable NSURL debugging */
  36. #undef NSURL_DEBUG
  37.  
  38. static bool nsurl__is_unreserved(unsigned char c)
  39. {
  40.         /* From RFC3986 section 2.3 (unreserved characters)
  41.          *
  42.          *      unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
  43.          *
  44.          */
  45.         static const bool unreserved[256] = {
  46.                 false, false, false, false, false, false, false, false, /* 00 */
  47.                 false, false, false, false, false, false, false, false, /* 08 */
  48.                 false, false, false, false, false, false, false, false, /* 10 */
  49.                 false, false, false, false, false, false, false, false, /* 18 */
  50.                 false, false, false, false, false, false, false, false, /* 20 */
  51.                 false, false, false, false, false, true,  true,  false, /* 28 */
  52.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 30 */
  53.                 true,  true,  false, false, false, false, false, false, /* 38 */
  54.                 false, true,  true,  true,  true,  true,  true,  true,  /* 40 */
  55.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 48 */
  56.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 50 */
  57.                 true,  true,  true,  false, false, false, false, true,  /* 58 */
  58.                 false, true,  true,  true,  true,  true,  true,  true,  /* 60 */
  59.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 68 */
  60.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 70 */
  61.                 true,  true,  true,  false, false, false, true,  false, /* 78 */
  62.                 false, false, false, false, false, false, false, false, /* 80 */
  63.                 false, false, false, false, false, false, false, false, /* 88 */
  64.                 false, false, false, false, false, false, false, false, /* 90 */
  65.                 false, false, false, false, false, false, false, false, /* 98 */
  66.                 false, false, false, false, false, false, false, false, /* A0 */
  67.                 false, false, false, false, false, false, false, false, /* A8 */
  68.                 false, false, false, false, false, false, false, false, /* B0 */
  69.                 false, false, false, false, false, false, false, false, /* B8 */
  70.                 false, false, false, false, false, false, false, false, /* C0 */
  71.                 false, false, false, false, false, false, false, false, /* C8 */
  72.                 false, false, false, false, false, false, false, false, /* D0 */
  73.                 false, false, false, false, false, false, false, false, /* D8 */
  74.                 false, false, false, false, false, false, false, false, /* E0 */
  75.                 false, false, false, false, false, false, false, false, /* E8 */
  76.                 false, false, false, false, false, false, false, false, /* F0 */
  77.                 false, false, false, false, false, false, false, false  /* F8 */
  78.         };
  79.         return unreserved[c];
  80. }
  81.  
  82. /* The ASCII codes which should not be percent escaped */
  83. static bool nsurl__is_no_escape(unsigned char c)
  84. {
  85.         static const bool no_escape[256] = {
  86.                 false, false, false, false, false, false, false, false, /* 00 */
  87.                 false, false, false, false, false, false, false, false, /* 08 */
  88.                 false, false, false, false, false, false, false, false, /* 10 */
  89.                 false, false, false, false, false, false, false, false, /* 18 */
  90.                 false, true,  false, true,  true,  false, true,  true,  /* 20 */
  91.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 28 */
  92.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 30 */
  93.                 true,  true,  true,  true,  false, true,  false, true,  /* 38 */
  94.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 40 */
  95.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 48 */
  96.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 50 */
  97.                 true,  true,  true,  true,  false, true,  false, true,  /* 58 */
  98.                 false, true,  true,  true,  true,  true,  true,  true,  /* 60 */
  99.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 68 */
  100.                 true,  true,  true,  true,  true,  true,  true,  true,  /* 70 */
  101.                 true,  true,  true,  false, true,  false, true,  false, /* 78 */
  102.                 false, false, false, false, false, false, false, false, /* 80 */
  103.                 false, false, false, false, false, false, false, false, /* 88 */
  104.                 false, false, false, false, false, false, false, false, /* 90 */
  105.                 false, false, false, false, false, false, false, false, /* 98 */
  106.                 false, false, false, false, false, false, false, false, /* A0 */
  107.                 false, false, false, false, false, false, false, false, /* A8 */
  108.                 false, false, false, false, false, false, false, false, /* B0 */
  109.                 false, false, false, false, false, false, false, false, /* B8 */
  110.                 false, false, false, false, false, false, false, false, /* C0 */
  111.                 false, false, false, false, false, false, false, false, /* C8 */
  112.                 false, false, false, false, false, false, false, false, /* D0 */
  113.                 false, false, false, false, false, false, false, false, /* D8 */
  114.                 false, false, false, false, false, false, false, false, /* E0 */
  115.                 false, false, false, false, false, false, false, false, /* E8 */
  116.                 false, false, false, false, false, false, false, false, /* F0 */
  117.                 false, false, false, false, false, false, false, false, /* F8 */
  118.         };
  119.         return no_escape[c];
  120. }
  121.  
  122.  
  123. /** nsurl scheme type */
  124. enum scheme_type {
  125.         NSURL_SCHEME_OTHER,
  126.         NSURL_SCHEME_HTTP,
  127.         NSURL_SCHEME_HTTPS,
  128.         NSURL_SCHEME_FTP,
  129.         NSURL_SCHEME_MAILTO
  130. };
  131.  
  132.  
  133. /** nsurl components */
  134. struct nsurl_components {
  135.         lwc_string *scheme;
  136.         lwc_string *username;
  137.         lwc_string *password;
  138.         lwc_string *host;
  139.         lwc_string *port;
  140.         lwc_string *path;
  141.         lwc_string *query;
  142.         lwc_string *fragment;
  143.  
  144.         enum scheme_type scheme_type;
  145. };
  146.  
  147.  
  148. /**
  149.  * NetSurf URL object
  150.  *
  151.  * [scheme]://[username][:password]@[host]:[port][/path][?query][#fragment]
  152.  */
  153. struct nsurl {
  154.         struct nsurl_components components;
  155.  
  156.         int count;      /* Number of references to NetSurf URL object */
  157.  
  158.         size_t length;  /* Length of string */
  159.         char string[FLEX_ARRAY_LEN_DECL];       /* Full URL as a string */
  160. };
  161.  
  162.  
  163. /** Marker set, indicating positions of sections within a URL string */
  164. struct url_markers {
  165.         size_t start; /** start of URL */
  166.         size_t scheme_end;
  167.         size_t authority;
  168.  
  169.         size_t colon_first;
  170.         size_t at;
  171.         size_t colon_last;
  172.  
  173.         size_t path;
  174.         size_t query;
  175.         size_t fragment;
  176.  
  177.         size_t end; /** end of URL */
  178.  
  179.         enum scheme_type scheme_type;
  180. };
  181.  
  182.  
  183. /** Marker set, indicating positions of sections within a URL string */
  184. struct nsurl_component_lengths {
  185.         size_t scheme;
  186.         size_t username;
  187.         size_t password;
  188.         size_t host;
  189.         size_t port;
  190.         size_t path;
  191.         size_t query;
  192.         size_t fragment;
  193. };
  194.  
  195.  
  196. /** Flags indicating which parts of a URL string are required for a nsurl */
  197. enum nsurl_string_flags {
  198.         NSURL_F_SCHEME                  = (1 << 0),
  199.         NSURL_F_SCHEME_PUNCTUATION      = (1 << 1),
  200.         NSURL_F_AUTHORITY_PUNCTUATION   = (1 << 2),
  201.         NSURL_F_USERNAME                = (1 << 3),
  202.         NSURL_F_PASSWORD                = (1 << 4),
  203.         NSURL_F_CREDENTIALS_PUNCTUATION = (1 << 5),
  204.         NSURL_F_HOST                    = (1 << 6),
  205.         NSURL_F_PORT                    = (1 << 7),
  206.         NSURL_F_AUTHORITY               = (NSURL_F_USERNAME |
  207.                                                 NSURL_F_PASSWORD |
  208.                                                 NSURL_F_HOST |
  209.                                                 NSURL_F_PORT),
  210.         NSURL_F_PATH                    = (1 << 8),
  211.         NSURL_F_QUERY                   = (1 << 9),
  212.         NSURL_F_FRAGMENT_PUNCTUATION    = (1 << 10),
  213.         NSURL_F_FRAGMENT                = (1 << 11)
  214. };
  215.  
  216.  
  217. /** Sections of a URL */
  218. enum url_sections {
  219.         URL_SCHEME,
  220.         URL_CREDENTIALS,
  221.         URL_HOST,
  222.         URL_PATH,
  223.         URL_QUERY,
  224.         URL_FRAGMENT
  225. };
  226.  
  227.  
  228. #define nsurl__component_copy(c) (c == NULL) ? NULL : lwc_string_ref(c)
  229.  
  230. #define nsurl__component_compare(c1, c2, match)                 \
  231.         if (c1 && c2 && lwc_error_ok ==                         \
  232.                         lwc_string_isequal(c1, c2, match)) {    \
  233.                 /* do nothing */                                \
  234.         } else if (c1 || c2) {                                  \
  235.                 *match = false;                                 \
  236.         }
  237.  
  238.  
  239. /**
  240.  * Obtains a set of markers delimiting sections in a URL string
  241.  *
  242.  * \param url_s         URL string
  243.  * \param markers       Updated to mark sections in the URL string
  244.  * \param joining       True iff URL string is a relative URL for joining
  245.  */
  246. static void nsurl__get_string_markers(const char * const url_s,
  247.                 struct url_markers *markers, bool joining)
  248. {
  249.         const char *pos = url_s; /** current position in url_s */
  250.         bool is_http = false;
  251.         bool trailing_whitespace = false;
  252.  
  253.         /* Initialise marker set */
  254.         struct url_markers marker = { 0, 0, 0,   0, 0, 0,
  255.                                       0, 0, 0,   0, NSURL_SCHEME_OTHER };
  256.  
  257.         /* Skip any leading whitespace in url_s */
  258.         while (isspace(*pos))
  259.                 pos++;
  260.  
  261.         /* Record start point */
  262.         marker.start = pos - url_s;
  263.  
  264.         marker.scheme_end = marker.authority = marker.colon_first = marker.at =
  265.                         marker.colon_last = marker.path = marker.start;
  266.  
  267.         if (*pos == '\0') {
  268.                 /* Nothing but whitespace, early exit */
  269.                 marker.query = marker.fragment = marker.end = marker.path;
  270.                 *markers = marker;
  271.                 return;
  272.         }
  273.  
  274.         /* Get scheme */
  275.         if (isalpha(*pos)) {
  276.                 pos++;
  277.  
  278.                 while (*pos != ':' && *pos != '\0') {
  279.                         if (!isalnum(*pos) && *pos != '+' &&
  280.                                         *pos != '-' && *pos != '.') {
  281.                                 /* This character is not valid in the
  282.                                  * scheme */
  283.                                 break;
  284.                         }
  285.                         pos++;
  286.                 }
  287.  
  288.                 if (*pos == ':') {
  289.                         /* This delimits the end of the scheme */
  290.                         size_t off;
  291.  
  292.                         marker.scheme_end = pos - url_s;
  293.  
  294.                         off = marker.scheme_end - marker.start;
  295.  
  296.                         /* Detect http(s) and mailto for scheme specifc
  297.                          * normalisation */
  298.                         if (off == SLEN("http") &&
  299.                                         (((*(pos - off + 0) == 'h') ||
  300.                                           (*(pos - off + 0) == 'H')) &&
  301.                                          ((*(pos - off + 1) == 't') ||
  302.                                           (*(pos - off + 1) == 'T')) &&
  303.                                          ((*(pos - off + 2) == 't') ||
  304.                                           (*(pos - off + 2) == 'T')) &&
  305.                                          ((*(pos - off + 3) == 'p') ||
  306.                                           (*(pos - off + 3) == 'P')))) {
  307.                                 marker.scheme_type = NSURL_SCHEME_HTTP;
  308.                                 is_http = true;
  309.                         } else if (off == SLEN("https") &&
  310.                                         (((*(pos - off + 0) == 'h') ||
  311.                                           (*(pos - off + 0) == 'H')) &&
  312.                                          ((*(pos - off + 1) == 't') ||
  313.                                           (*(pos - off + 1) == 'T')) &&
  314.                                          ((*(pos - off + 2) == 't') ||
  315.                                           (*(pos - off + 2) == 'T')) &&
  316.                                          ((*(pos - off + 3) == 'p') ||
  317.                                           (*(pos - off + 3) == 'P')) &&
  318.                                          ((*(pos - off + 4) == 's') ||
  319.                                           (*(pos - off + 4) == 'S')))) {
  320.                                 marker.scheme_type = NSURL_SCHEME_HTTPS;
  321.                                 is_http = true;
  322.                         } else if (off == SLEN("ftp") &&
  323.                                         (((*(pos - off + 0) == 'f') ||
  324.                                           (*(pos - off + 0) == 'F')) &&
  325.                                          ((*(pos - off + 1) == 't') ||
  326.                                           (*(pos - off + 1) == 'T')) &&
  327.                                          ((*(pos - off + 2) == 'p') ||
  328.                                           (*(pos - off + 2) == 'P')))) {
  329.                                 marker.scheme_type = NSURL_SCHEME_FTP;
  330.                         } else if (off == SLEN("mailto") &&
  331.                                         (((*(pos - off + 0) == 'm') ||
  332.                                           (*(pos - off + 0) == 'M')) &&
  333.                                          ((*(pos - off + 1) == 'a') ||
  334.                                           (*(pos - off + 1) == 'A')) &&
  335.                                          ((*(pos - off + 2) == 'i') ||
  336.                                           (*(pos - off + 2) == 'I')) &&
  337.                                          ((*(pos - off + 3) == 'l') ||
  338.                                           (*(pos - off + 3) == 'L')) &&
  339.                                          ((*(pos - off + 4) == 't') ||
  340.                                           (*(pos - off + 4) == 'T')) &&
  341.                                          ((*(pos - off + 5) == 'o') ||
  342.                                           (*(pos - off + 5) == 'O')))) {
  343.                                 marker.scheme_type = NSURL_SCHEME_MAILTO;
  344.                         }
  345.  
  346.                         /* Skip over colon */
  347.                         pos++;
  348.  
  349.                         /* Mark place as start of authority */
  350.                         marker.authority = marker.colon_first = marker.at =
  351.                                         marker.colon_last = marker.path =
  352.                                         pos - url_s;
  353.  
  354.                 } else {
  355.                         /* Not found a scheme  */
  356.                         if (joining == false) {
  357.                                 /* Assuming no scheme == http */
  358.                                 marker.scheme_type = NSURL_SCHEME_HTTP;
  359.                                 is_http = true;
  360.                         }
  361.                 }
  362.         }
  363.  
  364.         /* Get authority
  365.          *
  366.          * Two slashes always indicates the start of an authority.
  367.          *
  368.          * We are more relaxed in the case of http:
  369.          *   a. when joining, one or more slashes indicates start of authority
  370.          *   b. when not joining, we assume authority if no scheme was present
  371.          * and in the case of mailto: when we assume there is an authority.
  372.          */
  373.         if ((*pos == '/' && *(pos + 1) == '/') ||
  374.                         (is_http && ((joining && *pos == '/') ||
  375.                                         (joining == false &&
  376.                                         marker.scheme_end != marker.start))) ||
  377.                         marker.scheme_type == NSURL_SCHEME_MAILTO) {
  378.  
  379.                 /* Skip over leading slashes */
  380.                 if (*pos == '/') {
  381.                         if (is_http == false) {
  382.                                 if (*pos == '/') pos++;
  383.                                 if (*pos == '/') pos++;
  384.                         } else {
  385.                                 while (*pos == '/')
  386.                                         pos++;
  387.                         }
  388.  
  389.                         marker.authority = marker.colon_first = marker.at =
  390.                                         marker.colon_last = marker.path =
  391.                                         pos - url_s;
  392.                 }
  393.  
  394.                 /* Need to get (or complete) the authority */
  395.                 while (*pos != '\0') {
  396.                         if (*pos == '/' || *pos == '?' || *pos == '#') {
  397.                                 /* End of the authority */
  398.                                 break;
  399.  
  400.                         } else if (*pos == ':' && marker.colon_first ==
  401.                                         marker.authority) {
  402.                                 /* could be username:password or host:port
  403.                                  * separator */
  404.                                 marker.colon_first = pos - url_s;
  405.  
  406.                         } else if (*pos == ':' && marker.colon_first !=
  407.                                         marker.authority) {
  408.                                 /* could be host:port separator */
  409.                                 marker.colon_last = pos - url_s;
  410.  
  411.                         } else if (*pos == '@' && marker.at ==
  412.                                         marker.authority) {
  413.                                 /* Credentials @ host separator */
  414.                                 marker.at = pos - url_s;
  415.                         }
  416.  
  417.                         pos++;
  418.                 }
  419.  
  420.                 marker.path = pos - url_s;
  421.  
  422.         } else if ((*pos == '\0' || *pos == '/') &&
  423.                         joining == false && is_http == true) {
  424.                 marker.path = pos - url_s;
  425.         }
  426.  
  427.         /* Get path
  428.          *
  429.          * Needs to start with '/' if there's no authority
  430.          */
  431.         if (*pos == '/' || ((marker.path == marker.authority) &&
  432.                         (*pos != '?') && (*pos != '#') && (*pos != '\0'))) {
  433.                 while (*(++pos) != '\0') {
  434.                         if (*pos == '?' || *pos == '#') {
  435.                                 /* End of the path */
  436.                                 break;
  437.                         }
  438.                 }
  439.         }
  440.  
  441.         marker.query = pos - url_s;
  442.  
  443.         /* Get query */
  444.         if (*pos == '?') {
  445.                 while (*(++pos) != '\0') {
  446.                         if (*pos == '#') {
  447.                                 /* End of the query */
  448.                                 break;
  449.                         }
  450.                 }
  451.         }
  452.  
  453.         marker.fragment = pos - url_s;
  454.  
  455.         /* Get fragment */
  456.         if (*pos == '#') {
  457.                 while (*(++pos) != '\0')
  458.                         ;
  459.         }
  460.  
  461.         /* We got to the end of url_s.
  462.          * Need to skip back over trailing whitespace to find end of URL */
  463.         pos--;
  464.         if (pos >= url_s && isspace(*pos)) {
  465.                 trailing_whitespace = true;
  466.                 while (pos >= url_s && isspace(*pos))
  467.                         pos--;
  468.         }
  469.  
  470.         marker.end = pos + 1 - url_s;
  471.  
  472.         if (trailing_whitespace == true) {
  473.                 /* Ensure last url section doesn't pass end */
  474.                 if (marker.fragment > marker.end)
  475.                         marker.fragment = marker.end;
  476.                 if (marker.query > marker.end)
  477.                         marker.query = marker.end;
  478.                 if (marker.path > marker.end)
  479.                         marker.path = marker.end;
  480.                 if (marker.colon_last > marker.end)
  481.                         marker.colon_last = marker.end;
  482.                 if (marker.at > marker.end)
  483.                         marker.at = marker.end;
  484.                 if (marker.colon_last > marker.end)
  485.                         marker.colon_last = marker.end;
  486.                 if (marker.fragment > marker.end)
  487.                         marker.fragment = marker.end;
  488.         }
  489.  
  490.         /* Got all the URL components pegged out now */
  491.         *markers = marker;
  492. }
  493.  
  494.  
  495. /**
  496.  * Remove dot segments from a path, as per rfc 3986, 5.2.4
  497.  *
  498.  * \param path          path to remove dot segments from ('\0' terminated)
  499.  * \param output        path with dot segments removed
  500.  * \return size of output
  501.  */
  502. static size_t nsurl__remove_dot_segments(char *path, char *output)
  503. {
  504.         char *path_pos = path;
  505.         char *output_pos = output;
  506.  
  507.         while (*path_pos != '\0') {
  508. #ifdef NSURL_DEBUG
  509.                 LOG((" in:%s", path_pos));
  510.                 LOG(("out:%.*s", output_pos - output, output));
  511. #endif
  512.                 if (*path_pos == '.') {
  513.                         if (*(path_pos + 1) == '.' &&
  514.                                         *(path_pos + 2) == '/') {
  515.                                 /* Found prefix of "../" */
  516.                                 path_pos += SLEN("../");
  517.                                 continue;
  518.  
  519.                         } else if (*(path_pos + 1) == '/') {
  520.                                 /* Found prefix of "./" */
  521.                                 path_pos += SLEN("./");
  522.                                 continue;
  523.                         }
  524.                 } else if (*path_pos == '/' && *(path_pos + 1) == '.') {
  525.                         if (*(path_pos + 2) == '/') {
  526.                                 /* Found prefix of "/./" */
  527.                                 path_pos += SLEN("/.");
  528.                                 continue;
  529.  
  530.                         } else if (*(path_pos + 2) == '\0') {
  531.                                 /* Found "/." at end of path */
  532.                                 *(output_pos++) = '/';
  533.  
  534.                                 /* End of input path */
  535.                                 break;
  536.  
  537.                         } else if (*(path_pos + 2) == '.') {
  538.                                 if (*(path_pos + 3) == '/') {
  539.                                         /* Found prefix of "/../" */
  540.                                         path_pos += SLEN("/..");
  541.  
  542.                                         if (output_pos > output)
  543.                                                 output_pos--;
  544.                                         while (output_pos > output &&
  545.                                                         *output_pos != '/')
  546.                                                 output_pos--;
  547.  
  548.                                         continue;
  549.  
  550.                                 } else if (*(path_pos + 3) == '\0') {
  551.                                         /* Found "/.." at end of path */
  552.  
  553.                                         while (output_pos > output &&
  554.                                                         *(output_pos -1 ) !='/')
  555.                                                 output_pos--;
  556.  
  557.                                         /* End of input path */
  558.                                         break;
  559.                                 }
  560.                         }
  561.                 } else if (*path_pos == '.') {
  562.                         if (*(path_pos + 1) == '\0') {
  563.                                 /* Found "." at end of path */
  564.  
  565.                                 /* End of input path */
  566.                                 break;
  567.  
  568.                         } else if (*(path_pos + 1) == '.' &&
  569.                                         *(path_pos + 2) == '\0') {
  570.                                 /* Found ".." at end of path */
  571.  
  572.                                 /* End of input path */
  573.                                 break;
  574.                         }
  575.                 }
  576.                 /* Copy first character into output path */
  577.                 *output_pos++ = *path_pos++;
  578.  
  579.                 /* Copy up to but not including next '/' */
  580.                   while ((*path_pos != '/') && (*path_pos != '\0'))
  581.                         *output_pos++ = *path_pos++;
  582.         }
  583.  
  584.         return output_pos - output;
  585. }
  586.  
  587.  
  588. /**
  589.  * Get the length of the longest section
  590.  *
  591.  * \param m     markers delimiting url sections in a string
  592.  * \return the length of the longest section
  593.  */
  594. static size_t nsurl__get_longest_section(struct url_markers *m)
  595. {
  596.         size_t length = m->scheme_end - m->start;       /* scheme */
  597.  
  598.         if (length < m->at - m->authority)              /* credentials */
  599.                 length = m->at - m->authority;
  600.  
  601.         if (length < m->path - m->at)                   /* host */
  602.                 length = m->path - m->at;
  603.  
  604.         if (length < m->query - m->path)                /* path */
  605.                 length = m->query - m->path;
  606.  
  607.         if (length < m->fragment - m->query)            /* query */
  608.                 length = m->fragment - m->query;
  609.  
  610.         if (length < m->end - m->fragment)              /* fragment */
  611.                 length = m->end - m->fragment;
  612.  
  613.         return length;
  614. }
  615.  
  616.  
  617. /**
  618.  * Converts two hexadecimal digits to a single number
  619.  *
  620.  * \param c1    most significant hex digit
  621.  * \param c2    least significant hex digit
  622.  * \return the total value of the two digit hex number, or -ve if input not hex
  623.  *
  624.  * For unescaping url encoded characters.
  625.  */
  626. static inline int nsurl__get_ascii_offset(char c1, char c2)
  627. {
  628.         int offset;
  629.  
  630.         /* Use 1st char as most significant hex digit */
  631.         if (isdigit(c1))
  632.                 offset = 16 * (c1 - '0');
  633.         else if (c1 >= 'a' && c1 <= 'f')
  634.                 offset = 16 * (c1 - 'a' + 10);
  635.         else if (c1 >= 'A' && c1 <= 'F')
  636.                 offset = 16 * (c1 - 'A' + 10);
  637.         else
  638.                 /* Not valid hex */
  639.                 return -1;
  640.  
  641.         /* Use 2nd char as least significant hex digit and sum */
  642.         if (isdigit(c2))
  643.                 offset += c2 - '0';
  644.         else if (c2 >= 'a' && c2 <= 'f')
  645.                 offset += c2 - 'a' + 10;
  646.         else if (c2 >= 'A' && c2 <= 'F')
  647.                 offset += c2 - 'A' + 10;
  648.         else
  649.                 /* Not valid hex */
  650.                 return -1;
  651.  
  652.         return offset;
  653. }
  654.  
  655.  
  656. /**
  657.  * Create the components of a NetSurf URL object for a section of a URL string
  658.  *
  659.  * \param url_s         URL string
  660.  * \param section       Sets which section of URL string is to be normalised
  661.  * \param pegs          Set of markers delimiting the URL string's sections
  662.  * \param pos_norm      A buffer large enough for the normalised string (*3 + 1)
  663.  * \param url           A NetSurf URL object, to which components may be added
  664.  * \return NSERROR_OK on success, appropriate error otherwise
  665.  *
  666.  * The section of url_s is normalised appropriately.
  667.  */
  668. static nserror nsurl__create_from_section(const char * const url_s,
  669.                 const enum url_sections section,
  670.                 const struct url_markers *pegs,
  671.                 char *pos_norm,
  672.                 struct nsurl_components *url)
  673. {
  674.         int ascii_offset;
  675.         int start = 0;
  676.         int end = 0;
  677.         const char *pos;
  678.         const char *pos_url_s;
  679.         char *norm_start = pos_norm;
  680.         size_t copy_len;
  681.         size_t length;
  682.         enum {
  683.                 NSURL_F_NO_PORT         = (1 << 0)
  684.         } flags = 0;
  685.  
  686.         switch (section) {
  687.         case URL_SCHEME:
  688.                 start = pegs->start;
  689.                 end = pegs->scheme_end;
  690.                 break;
  691.  
  692.         case URL_CREDENTIALS:
  693.                 start = pegs->authority;
  694.                 end = pegs->at;
  695.                 break;
  696.  
  697.         case URL_HOST:
  698.                 start = (pegs->at == pegs->authority &&
  699.                                 *(url_s + pegs->at) != '@') ?
  700.                                 pegs->at :
  701.                                 pegs->at + 1;
  702.                 end = pegs->path;
  703.                 break;
  704.  
  705.         case URL_PATH:
  706.                 start = pegs->path;
  707.                 end = pegs->query;
  708.                 break;
  709.  
  710.         case URL_QUERY:
  711.                 start = pegs->query;
  712.                 end = pegs->fragment;
  713.                 break;
  714.  
  715.         case URL_FRAGMENT:
  716.                 start = (*(url_s + pegs->fragment) != '#') ?
  717.                                 pegs->fragment :
  718.                                 pegs->fragment + 1;
  719.                 end = pegs->end;
  720.                 break;
  721.         }
  722.  
  723.         if (end < start)
  724.                 end = start;
  725.  
  726.         length = end - start;
  727.  
  728.         /* Stage 1: Normalise the required section */
  729.  
  730.         pos = pos_url_s = url_s + start;
  731.         copy_len = 0;
  732.         for (; pos < url_s + end; pos++) {
  733.                 if (*pos == '%' && (pos + 2 < url_s + end)) {
  734.                         /* Might be an escaped character needing unescaped */
  735.  
  736.                         /* Find which character which was escaped */
  737.                         ascii_offset = nsurl__get_ascii_offset(*(pos + 1),
  738.                                         *(pos + 2));
  739.  
  740.                         if (ascii_offset < 0) {
  741.                                 /* % with invalid hex digits. */
  742.                                 copy_len++;
  743.                                 continue;
  744.                         }
  745.  
  746.                         if (nsurl__is_unreserved(ascii_offset) == false) {
  747.                                 /* This character should be escaped after all,
  748.                                  * just let it get copied */
  749.                                 copy_len += 3;
  750.                                 pos += 2;
  751.                                 continue;
  752.                         }
  753.  
  754.                         if (copy_len > 0) {
  755.                                 /* Copy up to here */
  756.                                 memcpy(pos_norm, pos_url_s, copy_len);
  757.                                 pos_norm += copy_len;
  758.                                 copy_len = 0;
  759.                         }
  760.  
  761.                         /* Put the unescaped character in the normalised URL */
  762.                         *(pos_norm++) = (char)ascii_offset;
  763.                         pos += 2;
  764.                         pos_url_s = pos + 1;
  765.  
  766.                         length -= 2;
  767.  
  768.                 } else if (nsurl__is_no_escape(*pos) == false) {
  769.  
  770.                         /* This needs to be escaped */
  771.                         if (copy_len > 0) {
  772.                                 /* Copy up to here */
  773.                                 memcpy(pos_norm, pos_url_s, copy_len);
  774.                                 pos_norm += copy_len;
  775.                                 copy_len = 0;
  776.                         }
  777.  
  778.                         /* escape */
  779.                         *(pos_norm++) = '%';
  780.                         *(pos_norm++) = digit2uppercase_hex(
  781.                                         ((unsigned char)*pos) >> 4);
  782.                         *(pos_norm++) = digit2uppercase_hex(
  783.                                         ((unsigned char)*pos) & 0xf);
  784.                         pos_url_s = pos + 1;
  785.  
  786.                         length += 2;
  787.  
  788.                 } else if ((section == URL_SCHEME || section == URL_HOST) &&
  789.                                 isupper(*pos)) {
  790.                         /* Lower case this letter */
  791.  
  792.                         if (copy_len > 0) {
  793.                                 /* Copy up to here */
  794.                                 memcpy(pos_norm, pos_url_s, copy_len);
  795.                                 pos_norm += copy_len;
  796.                                 copy_len = 0;
  797.                         }
  798.                         /* Copy lower cased letter into normalised URL */
  799.                         *(pos_norm++) = tolower(*pos);
  800.                         pos_url_s = pos + 1;
  801.  
  802.                 } else {
  803.                         /* This character is safe in normalised URL */
  804.                         copy_len++;
  805.                 }
  806.         }
  807.  
  808.         if (copy_len > 0) {
  809.                 /* Copy up to here */
  810.                 memcpy(pos_norm, pos_url_s, copy_len);
  811.                 pos_norm += copy_len;
  812.         }
  813.  
  814.         /* Mark end of section */
  815.         (*pos_norm) = '\0';
  816.  
  817.         /* Stage 2: Create the URL components for the required section */
  818.         switch (section) {
  819.         case URL_SCHEME:
  820.                 if (length == 0) {
  821.                         /* No scheme, assuming http, and add to URL */
  822.                         if (lwc_intern_string("http", SLEN("http"),
  823.                                         &url->scheme) != lwc_error_ok) {
  824.                                 return NSERROR_NOMEM;
  825.                         }
  826.                 } else {
  827.                         /* Add scheme to URL */
  828.                         if (lwc_intern_string(norm_start, length,
  829.                                         &url->scheme) != lwc_error_ok) {
  830.                                 return NSERROR_NOMEM;
  831.                         }
  832.                 }
  833.  
  834.                 break;
  835.  
  836.         case URL_CREDENTIALS:
  837.                 url->username = NULL;
  838.                 url->password = NULL;
  839.  
  840.                 if (length != 0 && *norm_start != ':') {
  841.                         char *sec_start = norm_start;
  842.                         if (pegs->colon_first != pegs->authority &&
  843.                                         pegs->at > pegs->colon_first + 1) {
  844.                                 /* there's a password */
  845.                                 sec_start += pegs->colon_first -
  846.                                                 pegs->authority + 1;
  847.                                 if (lwc_intern_string(sec_start,
  848.                                                 pegs->at - pegs->colon_first -1,
  849.                                                 &url->password) !=
  850.                                                 lwc_error_ok) {
  851.                                         return NSERROR_NOMEM;
  852.                                 }
  853.  
  854.                                 /* update start pos and length for username */
  855.                                 sec_start = norm_start;
  856.                                 length -= pegs->at - pegs->colon_first;
  857.                         } else if (pegs->colon_first != pegs->authority &&
  858.                                         pegs->at == pegs->colon_first + 1) {
  859.                                 /* strip username colon */
  860.                                 length--;
  861.                         }
  862.  
  863.                         /* Username */
  864.                         if (lwc_intern_string(sec_start, length,
  865.                                         &url->username) != lwc_error_ok) {
  866.                                 return NSERROR_NOMEM;
  867.                         }
  868.                 }
  869.  
  870.                 break;
  871.  
  872.         case URL_HOST:
  873.                 url->host = NULL;
  874.                 url->port = NULL;
  875.  
  876.                 if (length != 0) {
  877.                         size_t colon = 0;
  878.                         char *sec_start = norm_start;
  879.                         if (pegs->at < pegs->colon_first &&
  880.                                         pegs->colon_last == pegs->authority) {
  881.                                 /* There's one colon and it's after @ marker */
  882.                                 colon = pegs->colon_first;
  883.                         } else if (pegs->colon_last != pegs->authority) {
  884.                                 /* There's more than one colon */
  885.                                 colon = pegs->colon_last;
  886.                         } else {
  887.                                 /* There's no colon that could be a port
  888.                                  * separator */
  889.                                 flags |= NSURL_F_NO_PORT;
  890.                         }
  891.  
  892.                         if (!(flags & NSURL_F_NO_PORT)) {
  893.                                 /* Determine whether colon is a port separator
  894.                                  */
  895.                                 sec_start += colon - pegs->at;
  896.                                 while (++sec_start < norm_start + length) {
  897.                                         if (!isdigit(*sec_start)) {
  898.                                                 /* Character after port isn't a
  899.                                                  * digit; not a port separator
  900.                                                  */
  901.                                                 flags |= NSURL_F_NO_PORT;
  902.                                                 break;
  903.                                         }
  904.                                 }
  905.                         }
  906.  
  907.                         if (!(flags & NSURL_F_NO_PORT)) {
  908.                                 /* There's a port */
  909.                                 size_t skip = (pegs->at == pegs->authority) ?
  910.                                                 1 : 0;
  911.                                 sec_start = norm_start + colon - pegs->at +
  912.                                                 skip;
  913.                                 if (url->scheme != NULL &&
  914.                                                 url->scheme_type ==
  915.                                                 NSURL_SCHEME_HTTP &&
  916.                                                 length -
  917.                                                 (colon - pegs->at + skip) == 2 &&
  918.                                                 *sec_start == '8' &&
  919.                                                 *(sec_start + 1) == '0') {
  920.                                         /* Scheme is http, and port is default
  921.                                          * (80) */
  922.                                         flags |= NSURL_F_NO_PORT;
  923.                                 }
  924.  
  925.                                 if (length - (colon - pegs->at + skip) <= 0) {
  926.                                         /* No space for a port after the colon
  927.                                          */
  928.                                         flags |= NSURL_F_NO_PORT;
  929.                                 }
  930.  
  931.                                 /* Add non-redundant ports to NetSurf URL */
  932.                                 sec_start = norm_start + colon - pegs->at +
  933.                                                 skip;
  934.                                 if (!(flags & NSURL_F_NO_PORT) &&
  935.                                                 lwc_intern_string(sec_start,
  936.                                                 length -
  937.                                                 (colon - pegs->at + skip),
  938.                                                 &url->port) != lwc_error_ok) {
  939.                                         return NSERROR_NOMEM;
  940.                                 }
  941.  
  942.                                 /* update length for host */
  943.                                 skip = (pegs->at == pegs->authority) ? 0 : 1;
  944.                                 length = colon - pegs->at - skip;
  945.                         }
  946.  
  947.                         /* host */
  948.                         if (lwc_intern_string(norm_start, length,
  949.                                         &url->host) != lwc_error_ok) {
  950.                                 return NSERROR_NOMEM;
  951.                         }
  952.                 }
  953.  
  954.                 break;
  955.  
  956.         case URL_PATH:
  957.                 if (length != 0) {
  958.                         if (lwc_intern_string(norm_start, length,
  959.                                         &url->path) != lwc_error_ok) {
  960.                                 return NSERROR_NOMEM;
  961.                         }
  962.                 } else if (url->host != NULL &&
  963.                                 url->scheme_type != NSURL_SCHEME_MAILTO) {
  964.                         /* Set empty path to "/", if there's a host */
  965.                         if (lwc_intern_string("/", SLEN("/"),
  966.                                         &url->path) != lwc_error_ok) {
  967.                                 return NSERROR_NOMEM;
  968.                         }
  969.                 } else {
  970.                         url->path = NULL;
  971.                 }
  972.  
  973.                 break;
  974.  
  975.         case URL_QUERY:
  976.                 if (length != 0) {
  977.                         if (lwc_intern_string(norm_start, length,
  978.                                         &url->query) != lwc_error_ok) {
  979.                                 return NSERROR_NOMEM;
  980.                         }
  981.                 } else {
  982.                         url->query = NULL;
  983.                 }
  984.  
  985.                 break;
  986.  
  987.         case URL_FRAGMENT:
  988.                 if (length != 0) {
  989.                         if (lwc_intern_string(norm_start, length,
  990.                                         &url->fragment) != lwc_error_ok) {
  991.                                 return NSERROR_NOMEM;
  992.                         }
  993.                 } else {
  994.                         url->fragment = NULL;
  995.                 }
  996.  
  997.                 break;
  998.         }
  999.  
  1000.         return NSERROR_OK;
  1001. }
  1002.  
  1003.  
  1004. /**
  1005.  * Get nsurl string info; total length, component lengths, & components present
  1006.  *
  1007.  * \param url           NetSurf URL components
  1008.  * \param parts         Which parts of the URL are required in the string
  1009.  * \param url_l         Updated to total string length
  1010.  * \param lengths       Updated with individual component lengths
  1011.  * \param pflags        Updated to contain relevant string flags
  1012.  */
  1013. static void nsurl__get_string_data(const struct nsurl_components *url,
  1014.                 nsurl_component parts, size_t *url_l,
  1015.                 struct nsurl_component_lengths *lengths,
  1016.                 enum nsurl_string_flags *pflags)
  1017. {
  1018.         enum nsurl_string_flags flags = *pflags;
  1019.         *url_l = 0;
  1020.  
  1021.         /* Intersection of required parts and available parts gives
  1022.          * the output parts */
  1023.         if (url->scheme && parts & NSURL_SCHEME) {
  1024.                 flags |= NSURL_F_SCHEME;
  1025.  
  1026.                 lengths->scheme = lwc_string_length(url->scheme);
  1027.                 *url_l += lengths->scheme;
  1028.         }
  1029.  
  1030.         if (url->username && parts & NSURL_USERNAME) {
  1031.                 flags |= NSURL_F_USERNAME;
  1032.  
  1033.                 lengths->username = lwc_string_length(url->username);
  1034.                 *url_l += lengths->username;
  1035.         }
  1036.  
  1037.         if (url->password && parts & NSURL_PASSWORD) {
  1038.                 flags |= NSURL_F_PASSWORD;
  1039.  
  1040.                 lengths->password = lwc_string_length(url->password);
  1041.                 *url_l += SLEN(":") + lengths->password;
  1042.         }
  1043.  
  1044.         if (url->host && parts & NSURL_HOST) {
  1045.                 flags |= NSURL_F_HOST;
  1046.  
  1047.                 lengths->host = lwc_string_length(url->host);
  1048.                 *url_l += lengths->host;
  1049.         }
  1050.  
  1051.         if (url->port && parts & NSURL_PORT) {
  1052.                 flags |= NSURL_F_PORT;
  1053.  
  1054.                 lengths->port = lwc_string_length(url->port);
  1055.                 *url_l += SLEN(":") + lengths->port;
  1056.         }
  1057.  
  1058.         if (url->path && parts & NSURL_PATH) {
  1059.                 flags |= NSURL_F_PATH;
  1060.  
  1061.                 lengths->path = lwc_string_length(url->path);
  1062.                 *url_l += lengths->path;
  1063.         }
  1064.  
  1065.         if (url->query && parts & NSURL_QUERY) {
  1066.                 flags |= NSURL_F_QUERY;
  1067.  
  1068.                 lengths->query = lwc_string_length(url->query);
  1069.                 *url_l += lengths->query;
  1070.         }
  1071.  
  1072.         if (url->fragment && parts & NSURL_FRAGMENT) {
  1073.                 flags |= NSURL_F_FRAGMENT;
  1074.  
  1075.                 lengths->fragment = lwc_string_length(url->fragment);
  1076.                 *url_l += lengths->fragment;
  1077.         }
  1078.  
  1079.         /* Turn on any spanned punctuation */
  1080.         if ((flags & NSURL_F_SCHEME) && (parts > NSURL_SCHEME)) {
  1081.                 flags |= NSURL_F_SCHEME_PUNCTUATION;
  1082.  
  1083.                 *url_l += SLEN(":");
  1084.         }
  1085.  
  1086.         if ((flags & NSURL_F_SCHEME) && (flags > NSURL_F_SCHEME) &&
  1087.                         url->path && lwc_string_data(url->path)[0] == '/') {
  1088.                 flags |= NSURL_F_AUTHORITY_PUNCTUATION;
  1089.  
  1090.                 *url_l += SLEN("//");
  1091.         }
  1092.  
  1093.         if ((flags & (NSURL_F_USERNAME | NSURL_F_PASSWORD)) &&
  1094.                                 flags & NSURL_F_HOST) {
  1095.                 flags |= NSURL_F_CREDENTIALS_PUNCTUATION;
  1096.  
  1097.                 *url_l += SLEN("@");
  1098.         }
  1099.  
  1100.         if ((flags & ~NSURL_F_FRAGMENT) && (flags & NSURL_F_FRAGMENT)) {
  1101.                 flags |= NSURL_F_FRAGMENT_PUNCTUATION;
  1102.  
  1103.                 *url_l += SLEN("#");
  1104.         }
  1105.  
  1106.         *pflags = flags;
  1107. }
  1108.  
  1109.  
  1110. /**
  1111.  * Get nsurl string info; total length, component lengths, & components present
  1112.  *
  1113.  * \param url           NetSurf URL components
  1114.  * \param url_s         Updated to contain the string
  1115.  * \param l             Individual component lengths
  1116.  * \param flags         String flags
  1117.  */
  1118. static void nsurl_get_string(const struct nsurl_components *url, char *url_s,
  1119.                 struct nsurl_component_lengths *l,
  1120.                 enum nsurl_string_flags flags)
  1121. {
  1122.         char *pos;
  1123.  
  1124.         /* Copy the required parts into the url string */
  1125.         pos = url_s;
  1126.  
  1127.         if (flags & NSURL_F_SCHEME) {
  1128.                 memcpy(pos, lwc_string_data(url->scheme), l->scheme);
  1129.                 pos += l->scheme;
  1130.         }
  1131.  
  1132.         if (flags & NSURL_F_SCHEME_PUNCTUATION) {
  1133.                 *(pos++) = ':';
  1134.         }
  1135.  
  1136.         if (flags & NSURL_F_AUTHORITY_PUNCTUATION) {
  1137.                 *(pos++) = '/';
  1138.                 *(pos++) = '/';
  1139.         }
  1140.  
  1141.         if (flags & NSURL_F_USERNAME) {
  1142.                 memcpy(pos, lwc_string_data(url->username), l->username);
  1143.                 pos += l->username;
  1144.         }
  1145.  
  1146.         if (flags & NSURL_F_PASSWORD) {
  1147.                 *(pos++) = ':';
  1148.                 memcpy(pos, lwc_string_data(url->password), l->password);
  1149.                 pos += l->password;
  1150.         }
  1151.  
  1152.         if (flags & NSURL_F_CREDENTIALS_PUNCTUATION) {
  1153.                 *(pos++) = '@';
  1154.         }
  1155.  
  1156.         if (flags & NSURL_F_HOST) {
  1157.                 memcpy(pos, lwc_string_data(url->host), l->host);
  1158.                 pos += l->host;
  1159.         }
  1160.  
  1161.         if (flags & NSURL_F_PORT) {
  1162.                 *(pos++) = ':';
  1163.                 memcpy(pos, lwc_string_data(url->port), l->port);
  1164.                 pos += l->port;
  1165.         }
  1166.  
  1167.         if (flags & NSURL_F_PATH) {
  1168.                 memcpy(pos, lwc_string_data(url->path), l->path);
  1169.                 pos += l->path;
  1170.         }
  1171.  
  1172.         if (flags & NSURL_F_QUERY) {
  1173.                 memcpy(pos, lwc_string_data(url->query), l->query);
  1174.                 pos += l->query;
  1175.         }
  1176.  
  1177.         if (flags & NSURL_F_FRAGMENT) {
  1178.                 if (flags & NSURL_F_FRAGMENT_PUNCTUATION)
  1179.                         *(pos++) = '#';
  1180.                 memcpy(pos, lwc_string_data(url->fragment), l->fragment);
  1181.                 pos += l->fragment;
  1182.         }
  1183.  
  1184.         *pos = '\0';
  1185. }
  1186.  
  1187.  
  1188. #ifdef NSURL_DEBUG
  1189. /**
  1190.  * Dump a NetSurf URL's internal components
  1191.  *
  1192.  * \param url   The NetSurf URL to dump components of
  1193.  */
  1194. static void nsurl__dump(const nsurl *url)
  1195. {
  1196.         if (url->components.scheme)
  1197.                 LOG(("  Scheme: %s", lwc_string_data(url->components.scheme)));
  1198.  
  1199.         if (url->components.username)
  1200.                 LOG(("Username: %s",
  1201.                                 lwc_string_data(url->components.username)));
  1202.  
  1203.         if (url->components.password)
  1204.                 LOG(("Password: %s",
  1205.                                 lwc_string_data(url->components.password)));
  1206.  
  1207.         if (url->components.host)
  1208.                 LOG(("    Host: %s", lwc_string_data(url->components.host)));
  1209.  
  1210.         if (url->components.port)
  1211.                 LOG(("    Port: %s", lwc_string_data(url->components.port)));
  1212.  
  1213.         if (url->components.path)
  1214.                 LOG(("    Path: %s", lwc_string_data(url->components.path)));
  1215.  
  1216.         if (url->components.query)
  1217.                 LOG(("   Query: %s", lwc_string_data(url->components.query)));
  1218.  
  1219.         if (url->components.fragment)
  1220.                 LOG(("Fragment: %s",
  1221.                                 lwc_string_data(url->components.fragment)));
  1222. }
  1223. #endif
  1224.  
  1225. /******************************************************************************
  1226.  * NetSurf URL Public API                                                     *
  1227.  ******************************************************************************/
  1228.  
  1229. /* exported interface, documented in nsurl.h */
  1230. nserror nsurl_create(const char * const url_s, nsurl **url)
  1231. {
  1232.         struct url_markers m;
  1233.         struct nsurl_components c;
  1234.         size_t length;
  1235.         char *buff;
  1236.         struct nsurl_component_lengths str_len = { 0, 0, 0, 0,  0, 0, 0, 0 };
  1237.         enum nsurl_string_flags str_flags = 0;
  1238.         nserror e = NSERROR_OK;
  1239.  
  1240.         assert(url_s != NULL);
  1241.  
  1242.         /* Peg out the URL sections */
  1243.         nsurl__get_string_markers(url_s, &m, false);
  1244.  
  1245.         /* Get the length of the longest section */
  1246.         length = nsurl__get_longest_section(&m);
  1247.  
  1248.         /* Allocate enough memory to url escape the longest section */
  1249.         buff = malloc(length * 3 + 1);
  1250.         if (buff == NULL)
  1251.                 return NSERROR_NOMEM;
  1252.  
  1253.         /* Set scheme type */
  1254.         c.scheme_type = m.scheme_type;
  1255.  
  1256.         /* Build NetSurf URL object from sections */
  1257.         e |= nsurl__create_from_section(url_s, URL_SCHEME, &m, buff, &c);
  1258.         e |= nsurl__create_from_section(url_s, URL_CREDENTIALS, &m, buff, &c);
  1259.         e |= nsurl__create_from_section(url_s, URL_HOST, &m, buff, &c);
  1260.         e |= nsurl__create_from_section(url_s, URL_PATH, &m, buff, &c);
  1261.         e |= nsurl__create_from_section(url_s, URL_QUERY, &m, buff, &c);
  1262.         e |= nsurl__create_from_section(url_s, URL_FRAGMENT, &m, buff, &c);
  1263.  
  1264.         /* Finished with buffer */
  1265.         free(buff);
  1266.  
  1267.         if (e != NSERROR_OK)
  1268.                 return NSERROR_NOMEM;
  1269.  
  1270.         /* Get the string length and find which parts of url are present */
  1271.         nsurl__get_string_data(&c, NSURL_WITH_FRAGMENT, &length,
  1272.                         &str_len, &str_flags);
  1273.  
  1274.         /* Create NetSurf URL object */
  1275.         *url = malloc(sizeof(nsurl) + length + 1); /* Add 1 for \0 */
  1276.         if (*url == NULL)
  1277.                 return NSERROR_NOMEM;
  1278.  
  1279.         (*url)->components = c;
  1280.         (*url)->length = length;
  1281.  
  1282.         /* Fill out the url string */
  1283.         nsurl_get_string(&c, (*url)->string, &str_len, str_flags);
  1284.  
  1285.         /* Give the URL a reference */
  1286.         (*url)->count = 1;
  1287.  
  1288.         return NSERROR_OK;
  1289. }
  1290.  
  1291.  
  1292. /* exported interface, documented in nsurl.h */
  1293. nsurl *nsurl_ref(nsurl *url)
  1294. {
  1295.         assert(url != NULL);
  1296.  
  1297.         url->count++;
  1298.  
  1299.         return url;
  1300. }
  1301.  
  1302.  
  1303. /* exported interface, documented in nsurl.h */
  1304. void nsurl_unref(nsurl *url)
  1305. {
  1306.         assert(url != NULL);
  1307.         assert(url->count > 0);
  1308.  
  1309.         if (--url->count > 0)
  1310.                 return;
  1311.  
  1312. #ifdef NSURL_DEBUG
  1313.         nsurl__dump(url);
  1314. #endif
  1315.  
  1316.         /* Release lwc strings */
  1317.         if (url->components.scheme)
  1318.                 lwc_string_unref(url->components.scheme);
  1319.  
  1320.         if (url->components.username)
  1321.                 lwc_string_unref(url->components.username);
  1322.  
  1323.         if (url->components.password)
  1324.                 lwc_string_unref(url->components.password);
  1325.  
  1326.         if (url->components.host)
  1327.                 lwc_string_unref(url->components.host);
  1328.  
  1329.         if (url->components.port)
  1330.                 lwc_string_unref(url->components.port);
  1331.  
  1332.         if (url->components.path)
  1333.                 lwc_string_unref(url->components.path);
  1334.  
  1335.         if (url->components.query)
  1336.                 lwc_string_unref(url->components.query);
  1337.  
  1338.         if (url->components.fragment)
  1339.                 lwc_string_unref(url->components.fragment);
  1340.  
  1341.         /* Free the NetSurf URL */
  1342.         free(url);
  1343. }
  1344.  
  1345.  
  1346. /* exported interface, documented in nsurl.h */
  1347. bool nsurl_compare(const nsurl *url1, const nsurl *url2, nsurl_component parts)
  1348. {
  1349.         bool match = true;
  1350.  
  1351.         assert(url1 != NULL);
  1352.         assert(url2 != NULL);
  1353.  
  1354.         /* Compare URL components */
  1355.  
  1356.         /* Path, host and query first, since they're most likely to differ */
  1357.  
  1358.         if (parts & NSURL_PATH) {
  1359.                 nsurl__component_compare(url1->components.path,
  1360.                                 url2->components.path, &match);
  1361.  
  1362.                 if (match == false)
  1363.                         return false;
  1364.         }
  1365.  
  1366.         if (parts & NSURL_HOST) {
  1367.                 nsurl__component_compare(url1->components.host,
  1368.                                 url2->components.host, &match);
  1369.  
  1370.                 if (match == false)
  1371.                         return false;
  1372.         }
  1373.  
  1374.         if (parts & NSURL_QUERY) {
  1375.                 nsurl__component_compare(url1->components.query,
  1376.                                 url2->components.query, &match);
  1377.  
  1378.                 if (match == false)
  1379.                         return false;
  1380.         }
  1381.  
  1382.         if (parts & NSURL_SCHEME) {
  1383.                 nsurl__component_compare(url1->components.scheme,
  1384.                                 url2->components.scheme, &match);
  1385.  
  1386.                 if (match == false)
  1387.                         return false;
  1388.         }
  1389.  
  1390.         if (parts & NSURL_USERNAME) {
  1391.                 nsurl__component_compare(url1->components.username,
  1392.                                 url2->components.username, &match);
  1393.  
  1394.                 if (match == false)
  1395.                         return false;
  1396.         }
  1397.  
  1398.         if (parts & NSURL_PASSWORD) {
  1399.                 nsurl__component_compare(url1->components.password,
  1400.                                 url2->components.password, &match);
  1401.  
  1402.                 if (match == false)
  1403.                         return false;
  1404.         }
  1405.  
  1406.         if (parts & NSURL_PORT) {
  1407.                 nsurl__component_compare(url1->components.port,
  1408.                                 url2->components.port, &match);
  1409.  
  1410.                 if (match == false)
  1411.                         return false;
  1412.         }
  1413.  
  1414.         if (parts & NSURL_FRAGMENT) {
  1415.                 nsurl__component_compare(url1->components.fragment,
  1416.                                 url2->components.fragment, &match);
  1417.  
  1418.                 if (match == false)
  1419.                         return false;
  1420.         }
  1421.  
  1422.         return true;
  1423. }
  1424.  
  1425.  
  1426. /* exported interface, documented in nsurl.h */
  1427. nserror nsurl_get(const nsurl *url, nsurl_component parts,
  1428.                 char **url_s, size_t *url_l)
  1429. {
  1430.         struct nsurl_component_lengths str_len = { 0, 0, 0, 0,  0, 0, 0, 0 };
  1431.         enum nsurl_string_flags str_flags = 0;
  1432.  
  1433.         /* Get the string length and find which parts of url need copied */
  1434.         nsurl__get_string_data(&(url->components), parts, url_l,
  1435.                         &str_len, &str_flags);
  1436.  
  1437.         if (*url_l == 0) {
  1438.                 return NSERROR_BAD_URL;
  1439.         }
  1440.  
  1441.         /* Allocate memory for url string */
  1442.         *url_s = malloc(*url_l + 1); /* adding 1 for '\0' */
  1443.         if (*url_s == NULL) {
  1444.                 return NSERROR_NOMEM;
  1445.         }
  1446.  
  1447.         /* Copy the required parts into the url string */
  1448.         nsurl_get_string(&(url->components), *url_s, &str_len, str_flags);
  1449.  
  1450.         return NSERROR_OK;
  1451. }
  1452.  
  1453.  
  1454. /* exported interface, documented in nsurl.h */
  1455. lwc_string *nsurl_get_component(const nsurl *url, nsurl_component part)
  1456. {
  1457.         assert(url != NULL);
  1458.  
  1459.         switch (part) {
  1460.         case NSURL_SCHEME:
  1461.                 return (url->components.scheme != NULL) ?
  1462.                                 lwc_string_ref(url->components.scheme) : NULL;
  1463.  
  1464.         case NSURL_USERNAME:
  1465.                 return (url->components.username != NULL) ?
  1466.                                 lwc_string_ref(url->components.username) : NULL;
  1467.  
  1468.         case NSURL_PASSWORD:
  1469.                 return (url->components.password != NULL) ?
  1470.                                 lwc_string_ref(url->components.password) : NULL;
  1471.  
  1472.         case NSURL_HOST:
  1473.                 return (url->components.host != NULL) ?
  1474.                                 lwc_string_ref(url->components.host) : NULL;
  1475.  
  1476.         case NSURL_PORT:
  1477.                 return (url->components.port != NULL) ?
  1478.                                 lwc_string_ref(url->components.port) : NULL;
  1479.  
  1480.         case NSURL_PATH:
  1481.                 return (url->components.path != NULL) ?
  1482.                                 lwc_string_ref(url->components.path) : NULL;
  1483.  
  1484.         case NSURL_QUERY:
  1485.                 return (url->components.query != NULL) ?
  1486.                                 lwc_string_ref(url->components.query) : NULL;
  1487.  
  1488.         case NSURL_FRAGMENT:
  1489.                 return (url->components.fragment != NULL) ?
  1490.                                 lwc_string_ref(url->components.fragment) : NULL;
  1491.  
  1492.         default:
  1493.                 LOG(("Unsupported value passed to part param."));
  1494.                 assert(0);
  1495.         }
  1496.  
  1497.         return NULL;
  1498. }
  1499.  
  1500.  
  1501. /* exported interface, documented in nsurl.h */
  1502. bool nsurl_has_component(const nsurl *url, nsurl_component part)
  1503. {
  1504.         assert(url != NULL);
  1505.  
  1506.         switch (part) {
  1507.         case NSURL_SCHEME:
  1508.                 if (url->components.scheme != NULL)
  1509.                         return true;
  1510.                 else
  1511.                         return false;
  1512.  
  1513.         case NSURL_CREDENTIALS:
  1514.                 /* Only username required for credentials section */
  1515.                 /* Fall through */
  1516.         case NSURL_USERNAME:
  1517.                 if (url->components.username != NULL)
  1518.                         return true;
  1519.                 else
  1520.                         return false;
  1521.  
  1522.         case NSURL_PASSWORD:
  1523.                 if (url->components.password != NULL)
  1524.                         return true;
  1525.                 else
  1526.                         return false;
  1527.  
  1528.         case NSURL_HOST:
  1529.                 if (url->components.host != NULL)
  1530.                         return true;
  1531.                 else
  1532.                         return false;
  1533.  
  1534.         case NSURL_PORT:
  1535.                 if (url->components.port != NULL)
  1536.                         return true;
  1537.                 else
  1538.                         return false;
  1539.  
  1540.         case NSURL_PATH:
  1541.                 if (url->components.path != NULL)
  1542.                         return true;
  1543.                 else
  1544.                         return false;
  1545.  
  1546.         case NSURL_QUERY:
  1547.                 if (url->components.query != NULL)
  1548.                         return true;
  1549.                 else
  1550.                         return false;
  1551.  
  1552.         case NSURL_FRAGMENT:
  1553.                 if (url->components.fragment != NULL)
  1554.                         return true;
  1555.                 else
  1556.                         return false;
  1557.  
  1558.         default:
  1559.                 LOG(("Unsupported value passed to part param."));
  1560.                 assert(0);
  1561.         }
  1562.  
  1563.         return false;
  1564. }
  1565.  
  1566.  
  1567. /* exported interface, documented in nsurl.h */
  1568. const char *nsurl_access(const nsurl *url)
  1569. {
  1570.         assert(url != NULL);
  1571.  
  1572.         return url->string;
  1573. }
  1574.  
  1575.  
  1576. /* exported interface, documented in nsurl.h */
  1577. const char *nsurl_access_leaf(const nsurl *url)
  1578. {
  1579.         size_t path_len;
  1580.         const char *path;
  1581.         const char *leaf;
  1582.  
  1583.         if (url->components.path == NULL)
  1584.                 return "";
  1585.  
  1586.         path = lwc_string_data(url->components.path);
  1587.         path_len = lwc_string_length(url->components.path);
  1588.  
  1589.         if (path_len == 0)
  1590.                 return "";
  1591.  
  1592.         if (path_len == 1 && *path == '/')
  1593.                 return "/";
  1594.  
  1595.         leaf = path + path_len;
  1596.  
  1597.         do {
  1598.                 leaf--;
  1599.         } while ((leaf != path) && (*leaf != '/'));
  1600.  
  1601.         if (*leaf == '/')
  1602.                 leaf++;
  1603.  
  1604.         return leaf;
  1605. }
  1606.  
  1607.  
  1608. /* exported interface, documented in nsurl.h */
  1609. size_t nsurl_length(const nsurl *url)
  1610. {
  1611.         assert(url != NULL);
  1612.  
  1613.         return url->length;
  1614. }
  1615.  
  1616.  
  1617. /* exported interface, documented in nsurl.h */
  1618. nserror nsurl_join(const nsurl *base, const char *rel, nsurl **joined)
  1619. {
  1620.         struct url_markers m;
  1621.         struct nsurl_components c;
  1622.         size_t length;
  1623.         char *buff;
  1624.         char *buff_pos;
  1625.         char *buff_start;
  1626.         struct nsurl_component_lengths str_len = { 0, 0, 0, 0,  0, 0, 0, 0 };
  1627.         enum nsurl_string_flags str_flags = 0;
  1628.         nserror error = 0;
  1629.         enum {
  1630.                 NSURL_F_REL             =  0,
  1631.                 NSURL_F_BASE_SCHEME     = (1 << 0),
  1632.                 NSURL_F_BASE_AUTHORITY  = (1 << 1),
  1633.                 NSURL_F_BASE_PATH       = (1 << 2),
  1634.                 NSURL_F_MERGED_PATH     = (1 << 3),
  1635.                 NSURL_F_BASE_QUERY      = (1 << 4)
  1636.         } joined_parts;
  1637.  
  1638.         assert(base != NULL);
  1639.         assert(rel != NULL);
  1640.  
  1641.         /* Peg out the URL sections */
  1642.         nsurl__get_string_markers(rel, &m, true);
  1643.  
  1644.         /* Get the length of the longest section */
  1645.         length = nsurl__get_longest_section(&m);
  1646.  
  1647.         /* Initially assume that the joined URL can be formed entierly from
  1648.          * the relative URL. */
  1649.         joined_parts = NSURL_F_REL;
  1650.  
  1651.         /* Update joined_compnents to indicate any required parts from the
  1652.          * base URL. */
  1653.         if (m.scheme_end - m.start <= 0) {
  1654.                 /* The relative url has no scheme.
  1655.                  * Use base URL's scheme. */
  1656.                 joined_parts |= NSURL_F_BASE_SCHEME;
  1657.  
  1658.                 if (m.path - m.authority <= 0) {
  1659.                         /* The relative URL has no authority.
  1660.                          * Use base URL's authority. */
  1661.                         joined_parts |= NSURL_F_BASE_AUTHORITY;
  1662.  
  1663.                         if (m.query - m.path <= 0) {
  1664.                                 /* The relative URL has no path.
  1665.                                  * Use base URL's path. */
  1666.                                 joined_parts |= NSURL_F_BASE_PATH;
  1667.  
  1668.                                 if (m.fragment - m.query <= 0) {
  1669.                                         /* The relative URL has no query.
  1670.                                          * Use base URL's query. */
  1671.                                         joined_parts |= NSURL_F_BASE_QUERY;
  1672.                                 }
  1673.  
  1674.                         } else if (*(rel + m.path) != '/') {
  1675.                                 /* Relative URL has relative path */
  1676.                                 joined_parts |= NSURL_F_MERGED_PATH;
  1677.                         }
  1678.                 }
  1679.         }
  1680.  
  1681.         /* Allocate enough memory to url escape the longest section, plus
  1682.          * space for path merging (if required). */
  1683.         if (joined_parts & NSURL_F_MERGED_PATH) {
  1684.                 /* Need to merge paths */
  1685.                 length += (base->components.path != NULL) ?
  1686.                                 lwc_string_length(base->components.path) : 0;
  1687.         }
  1688.         length *= 4;
  1689.         /* Plus space for removing dots from path */
  1690.         length += (m.query - m.path) + ((base->components.path != NULL) ?
  1691.                         lwc_string_length(base->components.path) : 0);
  1692.  
  1693.         buff = malloc(length + 5);
  1694.         if (buff == NULL)
  1695.                 return NSERROR_NOMEM;
  1696.  
  1697.         buff_pos = buff;
  1698.  
  1699.         /* Form joined URL from base or rel components, as appropriate */
  1700.  
  1701.         if (joined_parts & NSURL_F_BASE_SCHEME) {
  1702.                 c.scheme_type = base->components.scheme_type;
  1703.  
  1704.                 c.scheme = nsurl__component_copy(base->components.scheme);
  1705.         } else {
  1706.                 c.scheme_type = m.scheme_type;
  1707.  
  1708.                 error |= nsurl__create_from_section(rel, URL_SCHEME, &m,
  1709.                                 buff, &c);
  1710.         }
  1711.  
  1712.         if (joined_parts & NSURL_F_BASE_AUTHORITY) {
  1713.                 c.username = nsurl__component_copy(base->components.username);
  1714.                 c.password = nsurl__component_copy(base->components.password);
  1715.                 c.host = nsurl__component_copy(base->components.host);
  1716.                 c.port = nsurl__component_copy(base->components.port);
  1717.         } else {
  1718.                 error |= nsurl__create_from_section(rel, URL_CREDENTIALS, &m,
  1719.                                 buff, &c);
  1720.                 error |= nsurl__create_from_section(rel, URL_HOST, &m,
  1721.                                 buff, &c);
  1722.         }
  1723.  
  1724.         if (joined_parts & NSURL_F_BASE_PATH) {
  1725.                 c.path = nsurl__component_copy(base->components.path);
  1726.  
  1727.         } else if (joined_parts & NSURL_F_MERGED_PATH) {
  1728.                 struct url_markers m_path;
  1729.                 size_t new_length;
  1730.  
  1731.                 if (base->components.host != NULL &&
  1732.                                         base->components.path == NULL) {
  1733.                         /* Append relative path to "/". */
  1734.                         *(buff_pos++) = '/';
  1735.                         memcpy(buff_pos, rel + m.path, m.query - m.path);
  1736.                         buff_pos += m.query - m.path;
  1737.  
  1738.                 } else {
  1739.                         /* Append relative path to all but last segment of
  1740.                          * base path. */
  1741.                         size_t path_end = lwc_string_length(
  1742.                                         base->components.path);
  1743.                         const char *path = lwc_string_data(
  1744.                                         base->components.path);
  1745.  
  1746.                         while (*(path + path_end) != '/' &&
  1747.                                         path_end != 0) {
  1748.                                 path_end--;
  1749.                         }
  1750.                         if (*(path + path_end) == '/')
  1751.                                 path_end++;
  1752.  
  1753.                         /* Copy the base part */
  1754.                         memcpy(buff_pos, path, path_end);
  1755.                         buff_pos += path_end;
  1756.  
  1757.                         /* Copy the relative part */
  1758.                         memcpy(buff_pos, rel + m.path, m.query - m.path);
  1759.                         buff_pos += m.query - m.path;
  1760.                 }
  1761.  
  1762.                 /* add termination to string */
  1763.                 *buff_pos++ = '\0';
  1764.  
  1765.                 new_length = nsurl__remove_dot_segments(buff, buff_pos);
  1766.  
  1767.                 m_path.path = 0;
  1768.                 m_path.query = new_length;
  1769.  
  1770.                 buff_start = buff_pos + new_length;
  1771.                 error |= nsurl__create_from_section(buff_pos, URL_PATH, &m_path,
  1772.                                 buff_start, &c);
  1773.  
  1774.         } else {
  1775.                 struct url_markers m_path;
  1776.                 size_t new_length;
  1777.  
  1778.                 memcpy(buff_pos, rel + m.path, m.query - m.path);
  1779.                 buff_pos += m.query - m.path;
  1780.                 *(buff_pos++) = '\0';
  1781.  
  1782.                 new_length = nsurl__remove_dot_segments(buff, buff_pos);
  1783.  
  1784.                 m_path.path = 0;
  1785.                 m_path.query = new_length;
  1786.  
  1787.                 buff_start = buff_pos + new_length;
  1788.                 error |= nsurl__create_from_section(buff_pos, URL_PATH, &m_path,
  1789.                                 buff_start, &c);
  1790.         }
  1791.  
  1792.         if (joined_parts & NSURL_F_BASE_QUERY)
  1793.                 c.query = nsurl__component_copy(base->components.query);
  1794.         else
  1795.                 error |= nsurl__create_from_section(rel, URL_QUERY, &m,
  1796.                                 buff, &c);
  1797.  
  1798.         error |= nsurl__create_from_section(rel, URL_FRAGMENT, &m,
  1799.                         buff, &c);
  1800.  
  1801.         /* Free temporary buffer */
  1802.         free(buff);
  1803.  
  1804.         if (error != NSERROR_OK)
  1805.                 return NSERROR_NOMEM;
  1806.  
  1807.         /* Get the string length and find which parts of url are present */
  1808.         nsurl__get_string_data(&c, NSURL_WITH_FRAGMENT, &length,
  1809.                         &str_len, &str_flags);
  1810.  
  1811.         /* Create NetSurf URL object */
  1812.         *joined = malloc(sizeof(nsurl) + length + 1); /* Add 1 for \0 */
  1813.         if (*joined == NULL)
  1814.                 return NSERROR_NOMEM;
  1815.  
  1816.         (*joined)->components = c;
  1817.         (*joined)->length = length;
  1818.  
  1819.         /* Fill out the url string */
  1820.         nsurl_get_string(&c, (*joined)->string, &str_len, str_flags);
  1821.  
  1822.         /* Give the URL a reference */
  1823.         (*joined)->count = 1;
  1824.  
  1825.         return NSERROR_OK;
  1826. }
  1827.  
  1828.  
  1829. /* exported interface, documented in nsurl.h */
  1830. nserror nsurl_defragment(const nsurl *url, nsurl **no_frag)
  1831. {
  1832.         size_t length;
  1833.         char *pos;
  1834.  
  1835.         /* Find the change in length from url to new_url */
  1836.         length = url->length;
  1837.         if (url->components.fragment != NULL) {
  1838.                 length -= 1 + lwc_string_length(url->components.fragment);
  1839.         }
  1840.  
  1841.         /* Create NetSurf URL object */
  1842.         *no_frag = malloc(sizeof(nsurl) + length + 1); /* Add 1 for \0 */
  1843.         if (*no_frag == NULL) {
  1844.                 return NSERROR_NOMEM;
  1845.         }
  1846.  
  1847.         /* Copy components */
  1848.         (*no_frag)->components.scheme =
  1849.                         nsurl__component_copy(url->components.scheme);
  1850.         (*no_frag)->components.username =
  1851.                         nsurl__component_copy(url->components.username);
  1852.         (*no_frag)->components.password =
  1853.                         nsurl__component_copy(url->components.password);
  1854.         (*no_frag)->components.host =
  1855.                         nsurl__component_copy(url->components.host);
  1856.         (*no_frag)->components.port =
  1857.                         nsurl__component_copy(url->components.port);
  1858.         (*no_frag)->components.path =
  1859.                         nsurl__component_copy(url->components.path);
  1860.         (*no_frag)->components.query =
  1861.                         nsurl__component_copy(url->components.query);
  1862.         (*no_frag)->components.fragment = NULL;
  1863.  
  1864.         (*no_frag)->components.scheme_type = url->components.scheme_type;
  1865.  
  1866.         (*no_frag)->length = length;
  1867.  
  1868.         /* Fill out the url string */
  1869.         pos = (*no_frag)->string;
  1870.         memcpy(pos, url->string, length);
  1871.         pos += length;
  1872.         *pos = '\0';
  1873.  
  1874.         /* Give the URL a reference */
  1875.         (*no_frag)->count = 1;
  1876.  
  1877.         return NSERROR_OK;
  1878. }
  1879.  
  1880.  
  1881. /* exported interface, documented in nsurl.h */
  1882. nserror nsurl_refragment(const nsurl *url, lwc_string *frag, nsurl **new_url)
  1883. {
  1884.         int frag_len;
  1885.         int base_len;
  1886.         char *pos;
  1887.         size_t len;
  1888.  
  1889.         assert(url != NULL);
  1890.         assert(frag != NULL);
  1891.  
  1892.         /* Find the change in length from url to new_url */
  1893.         base_len = url->length;
  1894.         if (url->components.fragment != NULL) {
  1895.                 base_len -= 1 + lwc_string_length(url->components.fragment);
  1896.         }
  1897.         frag_len = lwc_string_length(frag);
  1898.  
  1899.         /* Set new_url's length */
  1900.         len = base_len + 1 /* # */ + frag_len;
  1901.  
  1902.         /* Create NetSurf URL object */
  1903.         *new_url = malloc(sizeof(nsurl) + len + 1); /* Add 1 for \0 */
  1904.         if (*new_url == NULL) {
  1905.                 return NSERROR_NOMEM;
  1906.         }
  1907.  
  1908.         (*new_url)->length = len;
  1909.  
  1910.         /* Set string */
  1911.         pos = (*new_url)->string;
  1912.         memcpy(pos, url->string, base_len);
  1913.         pos += base_len;
  1914.         *pos = '#';
  1915.         memcpy(++pos, lwc_string_data(frag), frag_len);
  1916.         pos += frag_len;
  1917.         *pos = '\0';
  1918.  
  1919.         /* Copy components */
  1920.         (*new_url)->components.scheme =
  1921.                         nsurl__component_copy(url->components.scheme);
  1922.         (*new_url)->components.username =
  1923.                         nsurl__component_copy(url->components.username);
  1924.         (*new_url)->components.password =
  1925.                         nsurl__component_copy(url->components.password);
  1926.         (*new_url)->components.host =
  1927.                         nsurl__component_copy(url->components.host);
  1928.         (*new_url)->components.port =
  1929.                         nsurl__component_copy(url->components.port);
  1930.         (*new_url)->components.path =
  1931.                         nsurl__component_copy(url->components.path);
  1932.         (*new_url)->components.query =
  1933.                         nsurl__component_copy(url->components.query);
  1934.         (*new_url)->components.fragment =
  1935.                         lwc_string_ref(frag);
  1936.  
  1937.         (*new_url)->components.scheme_type = url->components.scheme_type;
  1938.  
  1939.         /* Give the URL a reference */
  1940.         (*new_url)->count = 1;
  1941.  
  1942.         return NSERROR_OK;
  1943. }
  1944.  
  1945.  
  1946. /* exported interface, documented in nsurl.h */
  1947. nserror nsurl_replace_query(const nsurl *url, const char *query,
  1948.                 nsurl **new_url)
  1949. {
  1950.         int query_len;
  1951.         int base_len;
  1952.         char *pos;
  1953.         size_t len;
  1954.         lwc_string *lwc_query;
  1955.  
  1956.         assert(url != NULL);
  1957.         assert(query != NULL);
  1958.         assert(query[0] == '?');
  1959.  
  1960.         /* Get the length of the new query */
  1961.         query_len = strlen(query);
  1962.  
  1963.         /* Find the change in length from url to new_url */
  1964.         base_len = url->length;
  1965.         if (url->components.query != NULL) {
  1966.                 base_len -= lwc_string_length(url->components.query);
  1967.         }
  1968.         if (url->components.fragment != NULL) {
  1969.                 base_len -= 1 + lwc_string_length(url->components.fragment);
  1970.         }
  1971.  
  1972.         /* Set new_url's length */
  1973.         len = base_len + query_len;
  1974.  
  1975.         /* Create NetSurf URL object */
  1976.         *new_url = malloc(sizeof(nsurl) + len + 1); /* Add 1 for \0 */
  1977.         if (*new_url == NULL) {
  1978.                 return NSERROR_NOMEM;
  1979.         }
  1980.  
  1981.         if (lwc_intern_string(query, query_len, &lwc_query) != lwc_error_ok) {
  1982.                 free(*new_url);
  1983.                 return NSERROR_NOMEM;
  1984.         }
  1985.  
  1986.         (*new_url)->length = len;
  1987.  
  1988.         /* Set string */
  1989.         pos = (*new_url)->string;
  1990.         memcpy(pos, url->string, base_len);
  1991.         pos += base_len;
  1992.         memcpy(pos, query, query_len);
  1993.         pos += query_len;
  1994.         if (url->components.fragment != NULL) {
  1995.                 const char *frag = lwc_string_data(url->components.fragment);
  1996.                 size_t frag_len = lwc_string_length(url->components.fragment);
  1997.                 *pos = '#';
  1998.                 memcpy(++pos, frag, frag_len);
  1999.                 pos += frag_len;
  2000.         }
  2001.         *pos = '\0';
  2002.  
  2003.         /* Copy components */
  2004.         (*new_url)->components.scheme =
  2005.                         nsurl__component_copy(url->components.scheme);
  2006.         (*new_url)->components.username =
  2007.                         nsurl__component_copy(url->components.username);
  2008.         (*new_url)->components.password =
  2009.                         nsurl__component_copy(url->components.password);
  2010.         (*new_url)->components.host =
  2011.                         nsurl__component_copy(url->components.host);
  2012.         (*new_url)->components.port =
  2013.                         nsurl__component_copy(url->components.port);
  2014.         (*new_url)->components.path =
  2015.                         nsurl__component_copy(url->components.path);
  2016.         (*new_url)->components.query = lwc_query;
  2017.         (*new_url)->components.fragment =
  2018.                         nsurl__component_copy(url->components.fragment);
  2019.  
  2020.         (*new_url)->components.scheme_type = url->components.scheme_type;
  2021.  
  2022.         /* Give the URL a reference */
  2023.         (*new_url)->count = 1;
  2024.  
  2025.         return NSERROR_OK;
  2026. }
  2027.  
  2028.  
  2029. /* exported interface, documented in nsurl.h */
  2030. nserror nsurl_parent(const nsurl *url, nsurl **new_url)
  2031. {
  2032.         lwc_string *lwc_path;
  2033.         size_t old_path_len, new_path_len;
  2034.         size_t len;
  2035.         const char* path = NULL;
  2036.         char *pos;
  2037.  
  2038.         assert(url != NULL);
  2039.  
  2040.         old_path_len = (url->components.path == NULL) ? 0 :
  2041.                         lwc_string_length(url->components.path);
  2042.  
  2043.         /* Find new path length */
  2044.         if (old_path_len == 0) {
  2045.                 new_path_len = old_path_len;
  2046.         } else {
  2047.                 path = lwc_string_data(url->components.path);
  2048.  
  2049.                 new_path_len = old_path_len;
  2050.                 if (old_path_len > 1) {
  2051.                         /* Skip over any trailing / */
  2052.                         if (path[new_path_len - 1] == '/')
  2053.                                 new_path_len--;
  2054.  
  2055.                         /* Work back to next / */
  2056.                         while (new_path_len > 0 &&
  2057.                                         path[new_path_len - 1] != '/')
  2058.                                 new_path_len--;
  2059.                 }
  2060.         }
  2061.  
  2062.         /* Find the length of new_url */
  2063.         len = url->length;
  2064.         if (url->components.query != NULL) {
  2065.                 len -= lwc_string_length(url->components.query);
  2066.         }
  2067.         if (url->components.fragment != NULL) {
  2068.                 len -= 1; /* # */
  2069.                 len -= lwc_string_length(url->components.fragment);
  2070.         }
  2071.         len -= old_path_len - new_path_len;
  2072.  
  2073.         /* Create NetSurf URL object */
  2074.         *new_url = malloc(sizeof(nsurl) + len + 1); /* Add 1 for \0 */
  2075.         if (*new_url == NULL) {
  2076.                 return NSERROR_NOMEM;
  2077.         }
  2078.  
  2079.         /* Make new path */
  2080.         if (old_path_len == 0) {
  2081.                 lwc_path = NULL;
  2082.         } else if (old_path_len == new_path_len) {
  2083.                 lwc_path = lwc_string_ref(url->components.path);
  2084.         } else {
  2085.                 if (lwc_intern_string(path, old_path_len - new_path_len,
  2086.                                 &lwc_path) != lwc_error_ok) {
  2087.                         free(*new_url);
  2088.                         return NSERROR_NOMEM;
  2089.                 }
  2090.         }
  2091.  
  2092.         (*new_url)->length = len;
  2093.  
  2094.         /* Set string */
  2095.         pos = (*new_url)->string;
  2096.         memcpy(pos, url->string, len);
  2097.         pos += len;
  2098.         *pos = '\0';
  2099.  
  2100.         /* Copy components */
  2101.         (*new_url)->components.scheme =
  2102.                         nsurl__component_copy(url->components.scheme);
  2103.         (*new_url)->components.username =
  2104.                         nsurl__component_copy(url->components.username);
  2105.         (*new_url)->components.password =
  2106.                         nsurl__component_copy(url->components.password);
  2107.         (*new_url)->components.host =
  2108.                         nsurl__component_copy(url->components.host);
  2109.         (*new_url)->components.port =
  2110.                         nsurl__component_copy(url->components.port);
  2111.         (*new_url)->components.path = lwc_path;
  2112.         (*new_url)->components.query = NULL;
  2113.         (*new_url)->components.fragment = NULL;
  2114.  
  2115.         (*new_url)->components.scheme_type = url->components.scheme_type;
  2116.  
  2117.         /* Give the URL a reference */
  2118.         (*new_url)->count = 1;
  2119.  
  2120.         return NSERROR_OK;
  2121. }
  2122.  
  2123.