1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, 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 /* get a cloned copy of the input */ 66 clone = strdup(input); 67 if(!clone) { 68 free(out); 69 return NULL; 70 } 71 orgclone = clone; 72 outptr = out; 73 74 if(!*clone) { 75 /* zero length string, return that */ 76 free(out); 77 return clone; 78 } 79 80 /* 81 * To handle query-parts properly, we must find it and remove it during the 82 * dotdot-operation and then append it again at the end to the output 83 * string. 84 */ 85 queryp = strchr(clone, '?'); 86 if(queryp) 87 *queryp = 0; 88 89 do { 90 91 /* A. If the input buffer begins with a prefix of "../" or "./", then 92 remove that prefix from the input buffer; otherwise, */ 93 94 if(!strncmp("./", clone, 2)) { 95 clone+=2; 96 clen-=2; 97 } 98 else if(!strncmp("../", clone, 3)) { 99 clone+=3; 100 clen-=3; 101 } 102 103 /* B. if the input buffer begins with a prefix of "/./" or "/.", where 104 "." is a complete path segment, then replace that prefix with "/" in 105 the input buffer; otherwise, */ 106 else if(!strncmp("/./", clone, 3)) { 107 clone+=2; 108 clen-=2; 109 } 110 else if(!strcmp("/.", clone)) { 111 clone[1]='/'; 112 clone++; 113 clen-=1; 114 } 115 116 /* C. if the input buffer begins with a prefix of "/../" or "/..", where 117 ".." is a complete path segment, then replace that prefix with "/" in 118 the input buffer and remove the last segment and its preceding "/" (if 119 any) from the output buffer; otherwise, */ 120 121 else if(!strncmp("/../", clone, 4)) { 122 clone+=3; 123 clen-=3; 124 /* remove the last segment from the output buffer */ 125 while(outptr > out) { 126 outptr--; 127 if(*outptr == '/') 128 break; 129 } 130 *outptr = 0; /* zero-terminate where it stops */ 131 } 132 else if(!strcmp("/..", clone)) { 133 clone[2]='/'; 134 clone+=2; 135 clen-=2; 136 /* remove the last segment from the output buffer */ 137 while(outptr > out) { 138 outptr--; 139 if(*outptr == '/') 140 break; 141 } 142 *outptr = 0; /* zero-terminate where it stops */ 143 } 144 145 /* D. if the input buffer consists only of "." or "..", then remove 146 that from the input buffer; otherwise, */ 147 148 else if(!strcmp(".", clone) || !strcmp("..", clone)) { 149 *clone=0; 150 } 151 152 else { 153 /* E. move the first path segment in the input buffer to the end of 154 the output buffer, including the initial "/" character (if any) and 155 any subsequent characters up to, but not including, the next "/" 156 character or the end of the input buffer. */ 157 158 do { 159 *outptr++ = *clone++; 160 clen--; 161 } while(*clone && (*clone != '/')); 162 *outptr = 0; 163 } 164 165 } while(*clone); 166 167 if(queryp) { 168 size_t qlen; 169 /* There was a query part, append that to the output. The 'clone' string 170 may now have been altered so we copy from the original input string 171 from the correct index. */ 172 size_t oindex = queryp - orgclone; 173 qlen = strlen(&input[oindex]); 174 memcpy(outptr, &input[oindex], qlen+1); /* include the ending zero byte */ 175 } 176 177 free(orgclone); 178 return out; 179 } 180