00001 /*************************************************************************** 00002 * _ _ ____ _ 00003 * Project ___| | | | _ \| | 00004 * / __| | | | |_) | | 00005 * | (__| |_| | _ <| |___ 00006 * \___|\___/|_| \_\_____| 00007 * 00008 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. 00009 * 00010 * This software is licensed as described in the file COPYING, which 00011 * you should have received as part of this distribution. The terms 00012 * are also available at https://curl.haxx.se/docs/copyright.html. 00013 * 00014 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 00015 * copies of the Software, and permit persons to whom the Software is 00016 * furnished to do so, under the terms of the COPYING file. 00017 * 00018 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 00019 * KIND, either express or implied. 00020 * 00021 ***************************************************************************/ 00022 00023 #include "curl_setup.h" 00024 00025 #include <curl/curl.h> 00026 00027 #include "dotdot.h" 00028 #include "curl_memory.h" 00029 00030 /* The last #include file should be: */ 00031 #include "memdebug.h" 00032 00033 /* 00034 * "Remove Dot Segments" 00035 * https://tools.ietf.org/html/rfc3986#section-5.2.4 00036 */ 00037 00038 /* 00039 * Curl_dedotdotify() 00040 * @unittest: 1395 00041 * 00042 * This function gets a zero-terminated path with dot and dotdot sequences 00043 * passed in and strips them off according to the rules in RFC 3986 section 00044 * 5.2.4. 00045 * 00046 * The function handles a query part ('?' + stuff) appended but it expects 00047 * that fragments ('#' + stuff) have already been cut off. 00048 * 00049 * RETURNS 00050 * 00051 * an allocated dedotdotified output string 00052 */ 00053 char *Curl_dedotdotify(const char *input) 00054 { 00055 size_t inlen = strlen(input); 00056 char *clone; 00057 size_t clen = inlen; /* the length of the cloned input */ 00058 char *out = malloc(inlen+1); 00059 char *outptr; 00060 char *orgclone; 00061 char *queryp; 00062 if(!out) 00063 return NULL; /* out of memory */ 00064 00065 /* get a cloned copy of the input */ 00066 clone = strdup(input); 00067 if(!clone) { 00068 free(out); 00069 return NULL; 00070 } 00071 orgclone = clone; 00072 outptr = out; 00073 00074 if(!*clone) { 00075 /* zero length string, return that */ 00076 free(out); 00077 return clone; 00078 } 00079 00080 /* 00081 * To handle query-parts properly, we must find it and remove it during the 00082 * dotdot-operation and then append it again at the end to the output 00083 * string. 00084 */ 00085 queryp = strchr(clone, '?'); 00086 if(queryp) 00087 *queryp = 0; 00088 00089 do { 00090 00091 /* A. If the input buffer begins with a prefix of "../" or "./", then 00092 remove that prefix from the input buffer; otherwise, */ 00093 00094 if(!strncmp("./", clone, 2)) { 00095 clone+=2; 00096 clen-=2; 00097 } 00098 else if(!strncmp("../", clone, 3)) { 00099 clone+=3; 00100 clen-=3; 00101 } 00102 00103 /* B. if the input buffer begins with a prefix of "/./" or "/.", where 00104 "." is a complete path segment, then replace that prefix with "/" in 00105 the input buffer; otherwise, */ 00106 else if(!strncmp("/./", clone, 3)) { 00107 clone+=2; 00108 clen-=2; 00109 } 00110 else if(!strcmp("/.", clone)) { 00111 clone[1]='/'; 00112 clone++; 00113 clen-=1; 00114 } 00115 00116 /* C. if the input buffer begins with a prefix of "/../" or "/..", where 00117 ".." is a complete path segment, then replace that prefix with "/" in 00118 the input buffer and remove the last segment and its preceding "/" (if 00119 any) from the output buffer; otherwise, */ 00120 00121 else if(!strncmp("/../", clone, 4)) { 00122 clone+=3; 00123 clen-=3; 00124 /* remove the last segment from the output buffer */ 00125 while(outptr > out) { 00126 outptr--; 00127 if(*outptr == '/') 00128 break; 00129 } 00130 *outptr = 0; /* zero-terminate where it stops */ 00131 } 00132 else if(!strcmp("/..", clone)) { 00133 clone[2]='/'; 00134 clone+=2; 00135 clen-=2; 00136 /* remove the last segment from the output buffer */ 00137 while(outptr > out) { 00138 outptr--; 00139 if(*outptr == '/') 00140 break; 00141 } 00142 *outptr = 0; /* zero-terminate where it stops */ 00143 } 00144 00145 /* D. if the input buffer consists only of "." or "..", then remove 00146 that from the input buffer; otherwise, */ 00147 00148 else if(!strcmp(".", clone) || !strcmp("..", clone)) { 00149 *clone=0; 00150 } 00151 00152 else { 00153 /* E. move the first path segment in the input buffer to the end of 00154 the output buffer, including the initial "/" character (if any) and 00155 any subsequent characters up to, but not including, the next "/" 00156 character or the end of the input buffer. */ 00157 00158 do { 00159 *outptr++ = *clone++; 00160 clen--; 00161 } while(*clone && (*clone != '/')); 00162 *outptr = 0; 00163 } 00164 00165 } while(*clone); 00166 00167 if(queryp) { 00168 size_t qlen; 00169 /* There was a query part, append that to the output. The 'clone' string 00170 may now have been altered so we copy from the original input string 00171 from the correct index. */ 00172 size_t oindex = queryp - orgclone; 00173 qlen = strlen(&input[oindex]); 00174 memcpy(outptr, &input[oindex], qlen+1); /* include the ending zero byte */ 00175 } 00176 00177 free(orgclone); 00178 return out; 00179 }