• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, 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 #ifndef CURL_DISABLE_FTP
25 #include <curl/curl.h>
26 
27 #include "curl_fnmatch.h"
28 #include "curl_memory.h"
29 
30 /* The last #include file should be: */
31 #include "memdebug.h"
32 
33 #ifndef HAVE_FNMATCH
34 
35 #define CURLFNM_CHARSET_LEN (sizeof(char) * 256)
36 #define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15)
37 
38 #define CURLFNM_NEGATE  CURLFNM_CHARSET_LEN
39 
40 #define CURLFNM_ALNUM   (CURLFNM_CHARSET_LEN + 1)
41 #define CURLFNM_DIGIT   (CURLFNM_CHARSET_LEN + 2)
42 #define CURLFNM_XDIGIT  (CURLFNM_CHARSET_LEN + 3)
43 #define CURLFNM_ALPHA   (CURLFNM_CHARSET_LEN + 4)
44 #define CURLFNM_PRINT   (CURLFNM_CHARSET_LEN + 5)
45 #define CURLFNM_BLANK   (CURLFNM_CHARSET_LEN + 6)
46 #define CURLFNM_LOWER   (CURLFNM_CHARSET_LEN + 7)
47 #define CURLFNM_GRAPH   (CURLFNM_CHARSET_LEN + 8)
48 #define CURLFNM_SPACE   (CURLFNM_CHARSET_LEN + 9)
49 #define CURLFNM_UPPER   (CURLFNM_CHARSET_LEN + 10)
50 
51 typedef enum {
52   CURLFNM_SCHS_DEFAULT = 0,
53   CURLFNM_SCHS_RIGHTBR,
54   CURLFNM_SCHS_RIGHTBRLEFTBR
55 } setcharset_state;
56 
57 typedef enum {
58   CURLFNM_PKW_INIT = 0,
59   CURLFNM_PKW_DDOT
60 } parsekey_state;
61 
62 typedef enum {
63   CCLASS_OTHER = 0,
64   CCLASS_DIGIT,
65   CCLASS_UPPER,
66   CCLASS_LOWER
67 } char_class;
68 
69 #define SETCHARSET_OK     1
70 #define SETCHARSET_FAIL   0
71 
parsekeyword(unsigned char ** pattern,unsigned char * charset)72 static int parsekeyword(unsigned char **pattern, unsigned char *charset)
73 {
74   parsekey_state state = CURLFNM_PKW_INIT;
75 #define KEYLEN 10
76   char keyword[KEYLEN] = { 0 };
77   int found = FALSE;
78   int i;
79   unsigned char *p = *pattern;
80   for(i = 0; !found; i++) {
81     char c = *p++;
82     if(i >= KEYLEN)
83       return SETCHARSET_FAIL;
84     switch(state) {
85     case CURLFNM_PKW_INIT:
86       if(ISLOWER(c))
87         keyword[i] = c;
88       else if(c == ':')
89         state = CURLFNM_PKW_DDOT;
90       else
91         return SETCHARSET_FAIL;
92       break;
93     case CURLFNM_PKW_DDOT:
94       if(c == ']')
95         found = TRUE;
96       else
97         return SETCHARSET_FAIL;
98     }
99   }
100 #undef KEYLEN
101 
102   *pattern = p; /* move caller's pattern pointer */
103   if(strcmp(keyword, "digit") == 0)
104     charset[CURLFNM_DIGIT] = 1;
105   else if(strcmp(keyword, "alnum") == 0)
106     charset[CURLFNM_ALNUM] = 1;
107   else if(strcmp(keyword, "alpha") == 0)
108     charset[CURLFNM_ALPHA] = 1;
109   else if(strcmp(keyword, "xdigit") == 0)
110     charset[CURLFNM_XDIGIT] = 1;
111   else if(strcmp(keyword, "print") == 0)
112     charset[CURLFNM_PRINT] = 1;
113   else if(strcmp(keyword, "graph") == 0)
114     charset[CURLFNM_GRAPH] = 1;
115   else if(strcmp(keyword, "space") == 0)
116     charset[CURLFNM_SPACE] = 1;
117   else if(strcmp(keyword, "blank") == 0)
118     charset[CURLFNM_BLANK] = 1;
119   else if(strcmp(keyword, "upper") == 0)
120     charset[CURLFNM_UPPER] = 1;
121   else if(strcmp(keyword, "lower") == 0)
122     charset[CURLFNM_LOWER] = 1;
123   else
124     return SETCHARSET_FAIL;
125   return SETCHARSET_OK;
126 }
127 
128 /* Return the character class. */
charclass(unsigned char c)129 static char_class charclass(unsigned char c)
130 {
131   if(ISUPPER(c))
132     return CCLASS_UPPER;
133   if(ISLOWER(c))
134     return CCLASS_LOWER;
135   if(ISDIGIT(c))
136     return CCLASS_DIGIT;
137   return CCLASS_OTHER;
138 }
139 
140 /* Include a character or a range in set. */
setcharorrange(unsigned char ** pp,unsigned char * charset)141 static void setcharorrange(unsigned char **pp, unsigned char *charset)
142 {
143   unsigned char *p = (*pp)++;
144   unsigned char c = *p++;
145 
146   charset[c] = 1;
147   if(ISALNUM(c) && *p++ == '-') {
148     char_class cc = charclass(c);
149     unsigned char endrange = *p++;
150 
151     if(endrange == '\\')
152       endrange = *p++;
153     if(endrange >= c && charclass(endrange) == cc) {
154       while(c++ != endrange)
155         if(charclass(c) == cc)  /* Chars in class may be not consecutive. */
156           charset[c] = 1;
157       *pp = p;
158     }
159   }
160 }
161 
162 /* returns 1 (true) if pattern is OK, 0 if is bad ("p" is pattern pointer) */
setcharset(unsigned char ** p,unsigned char * charset)163 static int setcharset(unsigned char **p, unsigned char *charset)
164 {
165   setcharset_state state = CURLFNM_SCHS_DEFAULT;
166   bool something_found = FALSE;
167   unsigned char c;
168 
169   memset(charset, 0, CURLFNM_CHSET_SIZE);
170   for(;;) {
171     c = **p;
172     if(!c)
173       return SETCHARSET_FAIL;
174 
175     switch(state) {
176     case CURLFNM_SCHS_DEFAULT:
177       if(c == ']') {
178         if(something_found)
179           return SETCHARSET_OK;
180         something_found = TRUE;
181         state = CURLFNM_SCHS_RIGHTBR;
182         charset[c] = 1;
183         (*p)++;
184       }
185       else if(c == '[') {
186         unsigned char *pp = *p + 1;
187 
188         if(*pp++ == ':' && parsekeyword(&pp, charset))
189           *p = pp;
190         else {
191           charset[c] = 1;
192           (*p)++;
193         }
194         something_found = TRUE;
195       }
196       else if(c == '^' || c == '!') {
197         if(!something_found) {
198           if(charset[CURLFNM_NEGATE]) {
199             charset[c] = 1;
200             something_found = TRUE;
201           }
202           else
203             charset[CURLFNM_NEGATE] = 1; /* negate charset */
204         }
205         else
206           charset[c] = 1;
207         (*p)++;
208       }
209       else if(c == '\\') {
210         c = *(++(*p));
211         if(c)
212           setcharorrange(p, charset);
213         else
214           charset['\\'] = 1;
215         something_found = TRUE;
216       }
217       else {
218         setcharorrange(p, charset);
219         something_found = TRUE;
220       }
221       break;
222     case CURLFNM_SCHS_RIGHTBR:
223       if(c == '[') {
224         state = CURLFNM_SCHS_RIGHTBRLEFTBR;
225         charset[c] = 1;
226         (*p)++;
227       }
228       else if(c == ']') {
229         return SETCHARSET_OK;
230       }
231       else if(ISPRINT(c)) {
232         charset[c] = 1;
233         (*p)++;
234         state = CURLFNM_SCHS_DEFAULT;
235       }
236       else
237         /* used 'goto fail' instead of 'return SETCHARSET_FAIL' to avoid a
238          * nonsense warning 'statement not reached' at end of the fnc when
239          * compiling on Solaris */
240         goto fail;
241       break;
242     case CURLFNM_SCHS_RIGHTBRLEFTBR:
243       if(c == ']')
244         return SETCHARSET_OK;
245       state  = CURLFNM_SCHS_DEFAULT;
246       charset[c] = 1;
247       (*p)++;
248       break;
249     }
250   }
251 fail:
252   return SETCHARSET_FAIL;
253 }
254 
loop(const unsigned char * pattern,const unsigned char * string,int maxstars)255 static int loop(const unsigned char *pattern, const unsigned char *string,
256                 int maxstars)
257 {
258   unsigned char *p = (unsigned char *)pattern;
259   unsigned char *s = (unsigned char *)string;
260   unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
261 
262   for(;;) {
263     unsigned char *pp;
264 
265     switch(*p) {
266     case '*':
267       if(!maxstars)
268         return CURL_FNMATCH_NOMATCH;
269       /* Regroup consecutive stars and question marks. This can be done because
270          '*?*?*' can be expressed as '??*'. */
271       for(;;) {
272         if(*++p == '\0')
273           return CURL_FNMATCH_MATCH;
274         if(*p == '?') {
275           if(!*s++)
276             return CURL_FNMATCH_NOMATCH;
277         }
278         else if(*p != '*')
279           break;
280       }
281       /* Skip string characters until we find a match with pattern suffix. */
282       for(maxstars--; *s; s++) {
283         if(loop(p, s, maxstars) == CURL_FNMATCH_MATCH)
284           return CURL_FNMATCH_MATCH;
285       }
286       return CURL_FNMATCH_NOMATCH;
287     case '?':
288       if(!*s)
289         return CURL_FNMATCH_NOMATCH;
290       s++;
291       p++;
292       break;
293     case '\0':
294       return *s? CURL_FNMATCH_NOMATCH: CURL_FNMATCH_MATCH;
295     case '\\':
296       if(p[1])
297         p++;
298       if(*s++ != *p++)
299         return CURL_FNMATCH_NOMATCH;
300       break;
301     case '[':
302       pp = p + 1; /* Copy in case of syntax error in set. */
303       if(setcharset(&pp, charset)) {
304         int found = FALSE;
305         if(!*s)
306           return CURL_FNMATCH_NOMATCH;
307         if(charset[(unsigned int)*s])
308           found = TRUE;
309         else if(charset[CURLFNM_ALNUM])
310           found = ISALNUM(*s);
311         else if(charset[CURLFNM_ALPHA])
312           found = ISALPHA(*s);
313         else if(charset[CURLFNM_DIGIT])
314           found = ISDIGIT(*s);
315         else if(charset[CURLFNM_XDIGIT])
316           found = ISXDIGIT(*s);
317         else if(charset[CURLFNM_PRINT])
318           found = ISPRINT(*s);
319         else if(charset[CURLFNM_SPACE])
320           found = ISSPACE(*s);
321         else if(charset[CURLFNM_UPPER])
322           found = ISUPPER(*s);
323         else if(charset[CURLFNM_LOWER])
324           found = ISLOWER(*s);
325         else if(charset[CURLFNM_BLANK])
326           found = ISBLANK(*s);
327         else if(charset[CURLFNM_GRAPH])
328           found = ISGRAPH(*s);
329 
330         if(charset[CURLFNM_NEGATE])
331           found = !found;
332 
333         if(!found)
334           return CURL_FNMATCH_NOMATCH;
335         p = pp + 1;
336         s++;
337         break;
338       }
339       /* Syntax error in set; mismatch! */
340       return CURL_FNMATCH_NOMATCH;
341 
342     default:
343       if(*p++ != *s++)
344         return CURL_FNMATCH_NOMATCH;
345       break;
346     }
347   }
348 }
349 
350 /*
351  * @unittest: 1307
352  */
Curl_fnmatch(void * ptr,const char * pattern,const char * string)353 int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
354 {
355   (void)ptr; /* the argument is specified by the curl_fnmatch_callback
356                 prototype, but not used by Curl_fnmatch() */
357   if(!pattern || !string) {
358     return CURL_FNMATCH_FAIL;
359   }
360   return loop((unsigned char *)pattern, (unsigned char *)string, 2);
361 }
362 #else
363 #include <fnmatch.h>
364 /*
365  * @unittest: 1307
366  */
Curl_fnmatch(void * ptr,const char * pattern,const char * string)367 int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
368 {
369   int rc;
370   (void)ptr; /* the argument is specified by the curl_fnmatch_callback
371                 prototype, but not used by Curl_fnmatch() */
372   if(!pattern || !string) {
373     return CURL_FNMATCH_FAIL;
374   }
375   rc = fnmatch(pattern, string, 0);
376   switch(rc) {
377   case 0:
378     return CURL_FNMATCH_MATCH;
379   case FNM_NOMATCH:
380     return CURL_FNMATCH_NOMATCH;
381   default:
382     return CURL_FNMATCH_FAIL;
383   }
384   /* not reached */
385 }
386 
387 #endif
388 
389 #endif /* if FTP is disabled */
390