Imported Upstream version 7.48.0
[platform/upstream/curl.git] / lib / dotdot.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@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 "dotdot.h"
26
27 #include "curl_memory.h"
28 /* The last #include file should be: */
29 #include "memdebug.h"
30
31 /*
32  * "Remove Dot Segments"
33  * https://tools.ietf.org/html/rfc3986#section-5.2.4
34  */
35
36 /*
37  * Curl_dedotdotify()
38  * @unittest: 1395
39  *
40  * This function gets a zero-terminated path with dot and dotdot sequences
41  * passed in and strips them off according to the rules in RFC 3986 section
42  * 5.2.4.
43  *
44  * The function handles a query part ('?' + stuff) appended but it expects
45  * that fragments ('#' + stuff) have already been cut off.
46  *
47  * RETURNS
48  *
49  * an allocated dedotdotified output string
50  */
51 char *Curl_dedotdotify(const char *input)
52 {
53   size_t inlen = strlen(input);
54   char *clone;
55   size_t clen = inlen; /* the length of the cloned input */
56   char *out = malloc(inlen+1);
57   char *outptr;
58   char *orgclone;
59   char *queryp;
60   if(!out)
61     return NULL; /* out of memory */
62
63   /* get a cloned copy of the input */
64   clone = strdup(input);
65   if(!clone) {
66     free(out);
67     return NULL;
68   }
69   orgclone = clone;
70   outptr = out;
71
72   if(!*clone) {
73     /* zero length string, return that */
74     free(out);
75     return clone;
76   }
77
78   /*
79    * To handle query-parts properly, we must find it and remove it during the
80    * dotdot-operation and then append it again at the end to the output
81    * string.
82    */
83   queryp = strchr(clone, '?');
84   if(queryp)
85     *queryp = 0;
86
87   do {
88
89     /*  A.  If the input buffer begins with a prefix of "../" or "./", then
90         remove that prefix from the input buffer; otherwise, */
91
92     if(!strncmp("./", clone, 2)) {
93       clone+=2;
94       clen-=2;
95     }
96     else if(!strncmp("../", clone, 3)) {
97       clone+=3;
98       clen-=3;
99     }
100
101     /*  B.  if the input buffer begins with a prefix of "/./" or "/.", where
102         "."  is a complete path segment, then replace that prefix with "/" in
103         the input buffer; otherwise, */
104     else if(!strncmp("/./", clone, 3)) {
105       clone+=2;
106       clen-=2;
107     }
108     else if(!strcmp("/.", clone)) {
109       clone[1]='/';
110       clone++;
111       clen-=1;
112     }
113
114     /*  C.  if the input buffer begins with a prefix of "/../" or "/..", where
115         ".." is a complete path segment, then replace that prefix with "/" in
116         the input buffer and remove the last segment and its preceding "/" (if
117         any) from the output buffer; otherwise, */
118
119     else if(!strncmp("/../", clone, 4)) {
120       clone+=3;
121       clen-=3;
122       /* remove the last segment from the output buffer */
123       while(outptr > out) {
124         outptr--;
125         if(*outptr == '/')
126           break;
127       }
128       *outptr = 0; /* zero-terminate where it stops */
129     }
130     else if(!strcmp("/..", clone)) {
131       clone[2]='/';
132       clone+=2;
133       clen-=2;
134       /* remove the last segment from the output buffer */
135       while(outptr > out) {
136         outptr--;
137         if(*outptr == '/')
138           break;
139       }
140       *outptr = 0; /* zero-terminate where it stops */
141     }
142
143     /*  D.  if the input buffer consists only of "." or "..", then remove
144         that from the input buffer; otherwise, */
145
146     else if(!strcmp(".", clone) || !strcmp("..", clone)) {
147       *clone=0;
148     }
149
150     else {
151       /*  E.  move the first path segment in the input buffer to the end of
152           the output buffer, including the initial "/" character (if any) and
153           any subsequent characters up to, but not including, the next "/"
154           character or the end of the input buffer. */
155
156       do {
157         *outptr++ = *clone++;
158         clen--;
159       } while(*clone && (*clone != '/'));
160       *outptr = 0;
161     }
162
163   } while(*clone);
164
165   if(queryp) {
166     size_t qlen;
167     /* There was a query part, append that to the output. The 'clone' string
168        may now have been altered so we copy from the original input string
169        from the correct index. */
170     size_t oindex = queryp - orgclone;
171     qlen = strlen(&input[oindex]);
172     memcpy(outptr, &input[oindex], qlen+1); /* include the ending zero byte */
173   }
174
175   free(orgclone);
176   return out;
177 }