Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  ***************************************************************************/
     22 
     23 #include "curl_setup.h"
     24 
     25 #include <curl/curl.h>
     26 
     27 #include "dotdot.h"
     28 #include "curl_memory.h"
     29 
     30 /* The last #include file should be: */
     31 #include "memdebug.h"
     32 
     33 /*
     34  * "Remove Dot Segments"
     35  * https://tools.ietf.org/html/rfc3986#section-5.2.4
     36  */
     37 
     38 /*
     39  * Curl_dedotdotify()
     40  * @unittest: 1395
     41  *
     42  * This function gets a zero-terminated path with dot and dotdot sequences
     43  * passed in and strips them off according to the rules in RFC 3986 section
     44  * 5.2.4.
     45  *
     46  * The function handles a query part ('?' + stuff) appended but it expects
     47  * that fragments ('#' + stuff) have already been cut off.
     48  *
     49  * RETURNS
     50  *
     51  * an allocated dedotdotified output string
     52  */
     53 char *Curl_dedotdotify(const char *input)
     54 {
     55   size_t inlen = strlen(input);
     56   char *clone;
     57   size_t clen = inlen; /* the length of the cloned input */
     58   char *out = malloc(inlen + 1);
     59   char *outptr;
     60   char *orgclone;
     61   char *queryp;
     62   if(!out)
     63     return NULL; /* out of memory */
     64 
     65   *out = 0; /* zero terminates, for inputs like "./" */
     66 
     67   /* get a cloned copy of the input */
     68   clone = strdup(input);
     69   if(!clone) {
     70     free(out);
     71     return NULL;
     72   }
     73   orgclone = clone;
     74   outptr = out;
     75 
     76   if(!*clone) {
     77     /* zero length string, return that */
     78     free(out);
     79     return clone;
     80   }
     81 
     82   /*
     83    * To handle query-parts properly, we must find it and remove it during the
     84    * dotdot-operation and then append it again at the end to the output
     85    * string.
     86    */
     87   queryp = strchr(clone, '?');
     88   if(queryp)
     89     *queryp = 0;
     90 
     91   do {
     92 
     93     /*  A.  If the input buffer begins with a prefix of "../" or "./", then
     94         remove that prefix from the input buffer; otherwise, */
     95 
     96     if(!strncmp("./", clone, 2)) {
     97       clone += 2;
     98       clen -= 2;
     99     }
    100     else if(!strncmp("../", clone, 3)) {
    101       clone += 3;
    102       clen -= 3;
    103     }
    104 
    105     /*  B.  if the input buffer begins with a prefix of "/./" or "/.", where
    106         "."  is a complete path segment, then replace that prefix with "/" in
    107         the input buffer; otherwise, */
    108     else if(!strncmp("/./", clone, 3)) {
    109       clone += 2;
    110       clen -= 2;
    111     }
    112     else if(!strcmp("/.", clone)) {
    113       clone[1]='/';
    114       clone++;
    115       clen -= 1;
    116     }
    117 
    118     /*  C.  if the input buffer begins with a prefix of "/../" or "/..", where
    119         ".." is a complete path segment, then replace that prefix with "/" in
    120         the input buffer and remove the last segment and its preceding "/" (if
    121         any) from the output buffer; otherwise, */
    122 
    123     else if(!strncmp("/../", clone, 4)) {
    124       clone += 3;
    125       clen -= 3;
    126       /* remove the last segment from the output buffer */
    127       while(outptr > out) {
    128         outptr--;
    129         if(*outptr == '/')
    130           break;
    131       }
    132       *outptr = 0; /* zero-terminate where it stops */
    133     }
    134     else if(!strcmp("/..", clone)) {
    135       clone[2]='/';
    136       clone += 2;
    137       clen -= 2;
    138       /* remove the last segment from the output buffer */
    139       while(outptr > out) {
    140         outptr--;
    141         if(*outptr == '/')
    142           break;
    143       }
    144       *outptr = 0; /* zero-terminate where it stops */
    145     }
    146 
    147     /*  D.  if the input buffer consists only of "." or "..", then remove
    148         that from the input buffer; otherwise, */
    149 
    150     else if(!strcmp(".", clone) || !strcmp("..", clone)) {
    151       *clone = 0;
    152       *out = 0;
    153     }
    154 
    155     else {
    156       /*  E.  move the first path segment in the input buffer to the end of
    157           the output buffer, including the initial "/" character (if any) and
    158           any subsequent characters up to, but not including, the next "/"
    159           character or the end of the input buffer. */
    160 
    161       do {
    162         *outptr++ = *clone++;
    163         clen--;
    164       } while(*clone && (*clone != '/'));
    165       *outptr = 0;
    166     }
    167 
    168   } while(*clone);
    169 
    170   if(queryp) {
    171     size_t qlen;
    172     /* There was a query part, append that to the output. The 'clone' string
    173        may now have been altered so we copy from the original input string
    174        from the correct index. */
    175     size_t oindex = queryp - orgclone;
    176     qlen = strlen(&input[oindex]);
    177     memcpy(outptr, &input[oindex], qlen + 1); /* include the end zero byte */
    178   }
    179 
    180   free(orgclone);
    181   return out;
    182 }
    183