xpcom/io/nsWildCard.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim:set ts=4 sts=4 sw=4 cin et: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* *
michael@0 8 *
michael@0 9 *
michael@0 10 * nsWildCard.cpp: shell-like wildcard match routines
michael@0 11 *
michael@0 12 * See nsIZipReader.findEntries documentation in nsIZipReader.idl for
michael@0 13 * a description of the syntax supported by the routines in this file.
michael@0 14 *
michael@0 15 * Rob McCool
michael@0 16 *
michael@0 17 */
michael@0 18
michael@0 19 #include "nsWildCard.h"
michael@0 20 #include "nsXPCOM.h"
michael@0 21 #include "nsCRTGlue.h"
michael@0 22 #include "nsCharTraits.h"
michael@0 23
michael@0 24 /* -------------------- ASCII-specific character methods ------------------- */
michael@0 25
michael@0 26 typedef int static_assert_character_code_arrangement['a' > 'A' ? 1 : -1];
michael@0 27
michael@0 28 template<class T>
michael@0 29 static int
michael@0 30 alpha(T c)
michael@0 31 {
michael@0 32 return ('a' <= c && c <= 'z') ||
michael@0 33 ('A' <= c && c <= 'Z');
michael@0 34 }
michael@0 35
michael@0 36 template<class T>
michael@0 37 static int
michael@0 38 alphanumeric(T c)
michael@0 39 {
michael@0 40 return ('0' <= c && c <= '9') || ::alpha(c);
michael@0 41 }
michael@0 42
michael@0 43 template<class T>
michael@0 44 static int
michael@0 45 lower(T c)
michael@0 46 {
michael@0 47 return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c;
michael@0 48 }
michael@0 49
michael@0 50 template<class T>
michael@0 51 static int
michael@0 52 upper(T c)
michael@0 53 {
michael@0 54 return ('a' <= c && c <= 'z') ? c - ('a' - 'A') : c;
michael@0 55 }
michael@0 56
michael@0 57 /* ----------------------------- _valid_subexp ---------------------------- */
michael@0 58
michael@0 59 template<class T>
michael@0 60 static int
michael@0 61 _valid_subexp(const T *expr, T stop1, T stop2)
michael@0 62 {
michael@0 63 int x;
michael@0 64 int nsc = 0; /* Number of special characters */
michael@0 65 int np; /* Number of pipe characters in union */
michael@0 66 int tld = 0; /* Number of tilde characters */
michael@0 67
michael@0 68 for (x = 0; expr[x] && (expr[x] != stop1) && (expr[x] != stop2); ++x) {
michael@0 69 switch(expr[x]) {
michael@0 70 case '~':
michael@0 71 if(tld) /* at most one exclusion */
michael@0 72 return INVALID_SXP;
michael@0 73 if (stop1) /* no exclusions within unions */
michael@0 74 return INVALID_SXP;
michael@0 75 if (!expr[x+1]) /* exclusion cannot be last character */
michael@0 76 return INVALID_SXP;
michael@0 77 if (!x) /* exclusion cannot be first character */
michael@0 78 return INVALID_SXP;
michael@0 79 ++tld;
michael@0 80 /* fall through */
michael@0 81 case '*':
michael@0 82 case '?':
michael@0 83 case '$':
michael@0 84 ++nsc;
michael@0 85 break;
michael@0 86 case '[':
michael@0 87 ++nsc;
michael@0 88 if((!expr[++x]) || (expr[x] == ']'))
michael@0 89 return INVALID_SXP;
michael@0 90 for(; expr[x] && (expr[x] != ']'); ++x) {
michael@0 91 if(expr[x] == '\\' && !expr[++x])
michael@0 92 return INVALID_SXP;
michael@0 93 }
michael@0 94 if(!expr[x])
michael@0 95 return INVALID_SXP;
michael@0 96 break;
michael@0 97 case '(':
michael@0 98 ++nsc;
michael@0 99 if (stop1) /* no nested unions */
michael@0 100 return INVALID_SXP;
michael@0 101 np = -1;
michael@0 102 do {
michael@0 103 int t = ::_valid_subexp(&expr[++x], T(')'), T('|'));
michael@0 104 if(t == 0 || t == INVALID_SXP)
michael@0 105 return INVALID_SXP;
michael@0 106 x+=t;
michael@0 107 if(!expr[x])
michael@0 108 return INVALID_SXP;
michael@0 109 ++np;
michael@0 110 } while (expr[x] == '|' );
michael@0 111 if(np < 1) /* must be at least one pipe */
michael@0 112 return INVALID_SXP;
michael@0 113 break;
michael@0 114 case ')':
michael@0 115 case ']':
michael@0 116 case '|':
michael@0 117 return INVALID_SXP;
michael@0 118 case '\\':
michael@0 119 ++nsc;
michael@0 120 if(!expr[++x])
michael@0 121 return INVALID_SXP;
michael@0 122 break;
michael@0 123 default:
michael@0 124 break;
michael@0 125 }
michael@0 126 }
michael@0 127 if((!stop1) && (!nsc)) /* must be at least one special character */
michael@0 128 return NON_SXP;
michael@0 129 return ((expr[x] == stop1 || expr[x] == stop2) ? x : INVALID_SXP);
michael@0 130 }
michael@0 131
michael@0 132
michael@0 133 template<class T>
michael@0 134 int
michael@0 135 NS_WildCardValid_(const T *expr)
michael@0 136 {
michael@0 137 int x = ::_valid_subexp(expr, T('\0'), T('\0'));
michael@0 138 return (x < 0 ? x : VALID_SXP);
michael@0 139 }
michael@0 140
michael@0 141 int
michael@0 142 NS_WildCardValid(const char *expr)
michael@0 143 {
michael@0 144 return NS_WildCardValid_(expr);
michael@0 145 }
michael@0 146
michael@0 147 int
michael@0 148 NS_WildCardValid(const char16_t *expr)
michael@0 149 {
michael@0 150 return NS_WildCardValid_(expr);
michael@0 151 }
michael@0 152
michael@0 153 /* ----------------------------- _shexp_match ----------------------------- */
michael@0 154
michael@0 155
michael@0 156 #define MATCH 0
michael@0 157 #define NOMATCH 1
michael@0 158 #define ABORTED -1
michael@0 159
michael@0 160 template<class T>
michael@0 161 static int
michael@0 162 _shexp_match(const T *str, const T *expr, bool case_insensitive, unsigned int level);
michael@0 163
michael@0 164 /**
michael@0 165 * Count characters until we reach a NUL character or either of the
michael@0 166 * two delimiter characters, stop1 or stop2. If we encounter a bracketed
michael@0 167 * expression, look only for NUL or ']' inside it. Do not look for stop1
michael@0 168 * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
michael@0 169 * Handle all escaping.
michael@0 170 * Return index in input string of first stop found, or ABORTED if not found.
michael@0 171 * If "dest" is non-nullptr, copy counted characters to it and null terminate.
michael@0 172 */
michael@0 173 template<class T>
michael@0 174 static int
michael@0 175 _scan_and_copy(const T *expr, T stop1, T stop2, T *dest)
michael@0 176 {
michael@0 177 int sx; /* source index */
michael@0 178 T cc;
michael@0 179
michael@0 180 for (sx = 0; (cc = expr[sx]) && cc != stop1 && cc != stop2; sx++) {
michael@0 181 if (cc == '\\') {
michael@0 182 if (!expr[++sx])
michael@0 183 return ABORTED; /* should be impossible */
michael@0 184 }
michael@0 185 else if (cc == '[') {
michael@0 186 while ((cc = expr[++sx]) && cc != ']') {
michael@0 187 if(cc == '\\' && !expr[++sx])
michael@0 188 return ABORTED;
michael@0 189 }
michael@0 190 if (!cc)
michael@0 191 return ABORTED; /* should be impossible */
michael@0 192 }
michael@0 193 }
michael@0 194 if (dest && sx) {
michael@0 195 /* Copy all but the closing delimiter. */
michael@0 196 memcpy(dest, expr, sx * sizeof(T));
michael@0 197 dest[sx] = 0;
michael@0 198 }
michael@0 199 return cc ? sx : ABORTED; /* index of closing delimiter */
michael@0 200 }
michael@0 201
michael@0 202 /* On input, expr[0] is the opening parenthesis of a union.
michael@0 203 * See if any of the alternatives in the union matches as a pattern.
michael@0 204 * The strategy is to take each of the alternatives, in turn, and append
michael@0 205 * the rest of the expression (after the closing ')' that marks the end of
michael@0 206 * this union) to that alternative, and then see if the resultant expression
michael@0 207 * matches the input string. Repeat this until some alternative matches,
michael@0 208 * or we have an abort.
michael@0 209 */
michael@0 210 template<class T>
michael@0 211 static int
michael@0 212 _handle_union(const T *str, const T *expr, bool case_insensitive,
michael@0 213 unsigned int level)
michael@0 214 {
michael@0 215 int sx; /* source index */
michael@0 216 int cp; /* source index of closing parenthesis */
michael@0 217 int count;
michael@0 218 int ret = NOMATCH;
michael@0 219 T *e2;
michael@0 220
michael@0 221 /* Find the closing parenthesis that ends this union in the expression */
michael@0 222 cp = ::_scan_and_copy(expr, T(')'), T('\0'), static_cast<T*>(nullptr));
michael@0 223 if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */
michael@0 224 return ABORTED;
michael@0 225 ++cp; /* now index of char after closing parenthesis */
michael@0 226 e2 = (T *) NS_Alloc((1 + nsCharTraits<T>::length(expr)) * sizeof(T));
michael@0 227 if (!e2)
michael@0 228 return ABORTED;
michael@0 229 for (sx = 1; ; ++sx) {
michael@0 230 /* Here, expr[sx] is one character past the preceding '(' or '|'. */
michael@0 231 /* Copy everything up to the next delimiter to e2 */
michael@0 232 count = ::_scan_and_copy(expr + sx, T(')'), T('|'), e2);
michael@0 233 if (count == ABORTED || !count) {
michael@0 234 ret = ABORTED;
michael@0 235 break;
michael@0 236 }
michael@0 237 sx += count;
michael@0 238 /* Append everything after closing parenthesis to e2. This is safe. */
michael@0 239 nsCharTraits<T>::copy(e2 + count, expr + cp, nsCharTraits<T>::length(expr + cp) + 1);
michael@0 240 ret = ::_shexp_match(str, e2, case_insensitive, level + 1);
michael@0 241 if (ret != NOMATCH || !expr[sx] || expr[sx] == ')')
michael@0 242 break;
michael@0 243 }
michael@0 244 NS_Free(e2);
michael@0 245 if (sx < 2)
michael@0 246 ret = ABORTED;
michael@0 247 return ret;
michael@0 248 }
michael@0 249
michael@0 250 /* returns 1 if val is in range from start..end, case insensitive. */
michael@0 251 static int
michael@0 252 _is_char_in_range(unsigned char start, unsigned char end, unsigned char val)
michael@0 253 {
michael@0 254 char map[256];
michael@0 255 memset(map, 0, sizeof map);
michael@0 256 while (start <= end)
michael@0 257 map[lower(start++)] = 1;
michael@0 258 return map[lower(val)];
michael@0 259 }
michael@0 260
michael@0 261 template<class T>
michael@0 262 static int
michael@0 263 _shexp_match(const T *str, const T *expr, bool case_insensitive,
michael@0 264 unsigned int level)
michael@0 265 {
michael@0 266 int x; /* input string index */
michael@0 267 int y; /* expression index */
michael@0 268 int ret,neg;
michael@0 269
michael@0 270 if (level > 20) /* Don't let the stack get too deep. */
michael@0 271 return ABORTED;
michael@0 272 for(x = 0, y = 0; expr[y]; ++y, ++x) {
michael@0 273 if((!str[x]) && (expr[y] != '$') && (expr[y] != '*')) {
michael@0 274 return NOMATCH;
michael@0 275 }
michael@0 276 switch(expr[y]) {
michael@0 277 case '$':
michael@0 278 if(str[x])
michael@0 279 return NOMATCH;
michael@0 280 --x; /* we don't want loop to increment x */
michael@0 281 break;
michael@0 282 case '*':
michael@0 283 while(expr[++y] == '*'){}
michael@0 284 if(!expr[y])
michael@0 285 return MATCH;
michael@0 286 while(str[x]) {
michael@0 287 ret = ::_shexp_match(&str[x++], &expr[y], case_insensitive,
michael@0 288 level + 1);
michael@0 289 switch(ret) {
michael@0 290 case NOMATCH:
michael@0 291 continue;
michael@0 292 case ABORTED:
michael@0 293 return ABORTED;
michael@0 294 default:
michael@0 295 return MATCH;
michael@0 296 }
michael@0 297 }
michael@0 298 if((expr[y] == '$') && (expr[y+1] == '\0') && (!str[x]))
michael@0 299 return MATCH;
michael@0 300 else
michael@0 301 return NOMATCH;
michael@0 302 case '[': {
michael@0 303 T start, end = 0;
michael@0 304 int i;
michael@0 305 neg = ((expr[++y] == '^') && (expr[y+1] != ']'));
michael@0 306 if (neg)
michael@0 307 ++y;
michael@0 308 i = y;
michael@0 309 start = expr[i++];
michael@0 310 if (start == '\\')
michael@0 311 start = expr[i++];
michael@0 312 if (::alphanumeric(start) && expr[i++] == '-') {
michael@0 313 end = expr[i++];
michael@0 314 if (end == '\\')
michael@0 315 end = expr[i++];
michael@0 316 }
michael@0 317 if (::alphanumeric(end) && expr[i] == ']') {
michael@0 318 /* This is a range form: a-b */
michael@0 319 T val = str[x];
michael@0 320 if (end < start) { /* swap them */
michael@0 321 T tmp = end;
michael@0 322 end = start;
michael@0 323 start = tmp;
michael@0 324 }
michael@0 325 if (case_insensitive && ::alpha(val)) {
michael@0 326 val = ::_is_char_in_range((unsigned char) start,
michael@0 327 (unsigned char) end,
michael@0 328 (unsigned char) val);
michael@0 329 if (neg == val)
michael@0 330 return NOMATCH;
michael@0 331 }
michael@0 332 else if (neg != ((val < start) || (val > end))) {
michael@0 333 return NOMATCH;
michael@0 334 }
michael@0 335 y = i;
michael@0 336 }
michael@0 337 else {
michael@0 338 /* Not range form */
michael@0 339 int matched = 0;
michael@0 340 for (; expr[y] != ']'; y++) {
michael@0 341 if (expr[y] == '\\')
michael@0 342 ++y;
michael@0 343 if(case_insensitive)
michael@0 344 matched |= (::upper(str[x]) == ::upper(expr[y]));
michael@0 345 else
michael@0 346 matched |= (str[x] == expr[y]);
michael@0 347 }
michael@0 348 if (neg == matched)
michael@0 349 return NOMATCH;
michael@0 350 }
michael@0 351 }
michael@0 352 break;
michael@0 353 case '(':
michael@0 354 if (!expr[y+1])
michael@0 355 return ABORTED;
michael@0 356 return ::_handle_union(&str[x], &expr[y], case_insensitive, level + 1);
michael@0 357 case '?':
michael@0 358 break;
michael@0 359 case ')':
michael@0 360 case ']':
michael@0 361 case '|':
michael@0 362 return ABORTED;
michael@0 363 case '\\':
michael@0 364 ++y;
michael@0 365 /* fall through */
michael@0 366 default:
michael@0 367 if(case_insensitive) {
michael@0 368 if(::upper(str[x]) != ::upper(expr[y]))
michael@0 369 return NOMATCH;
michael@0 370 }
michael@0 371 else {
michael@0 372 if(str[x] != expr[y])
michael@0 373 return NOMATCH;
michael@0 374 }
michael@0 375 break;
michael@0 376 }
michael@0 377 }
michael@0 378 return (str[x] ? NOMATCH : MATCH);
michael@0 379 }
michael@0 380
michael@0 381
michael@0 382 template<class T>
michael@0 383 static int
michael@0 384 ns_WildCardMatch(const T *str, const T *xp, bool case_insensitive)
michael@0 385 {
michael@0 386 T *expr = nullptr;
michael@0 387 int x, ret = MATCH;
michael@0 388
michael@0 389 if (!nsCharTraits<T>::find(xp, nsCharTraits<T>::length(xp), T('~')))
michael@0 390 return ::_shexp_match(str, xp, case_insensitive, 0);
michael@0 391
michael@0 392 expr = (T *) NS_Alloc((nsCharTraits<T>::length(xp) + 1) * sizeof(T));
michael@0 393 if(!expr)
michael@0 394 return NOMATCH;
michael@0 395 memcpy(expr, xp, (nsCharTraits<T>::length(xp) + 1) * sizeof(T));
michael@0 396
michael@0 397 x = ::_scan_and_copy(expr, T('~'), T('\0'), static_cast<T*>(nullptr));
michael@0 398 if (x != ABORTED && expr[x] == '~') {
michael@0 399 expr[x++] = '\0';
michael@0 400 ret = ::_shexp_match(str, &expr[x], case_insensitive, 0);
michael@0 401 switch (ret) {
michael@0 402 case NOMATCH: ret = MATCH; break;
michael@0 403 case MATCH: ret = NOMATCH; break;
michael@0 404 default: break;
michael@0 405 }
michael@0 406 }
michael@0 407 if (ret == MATCH)
michael@0 408 ret = ::_shexp_match(str, expr, case_insensitive, 0);
michael@0 409
michael@0 410 NS_Free(expr);
michael@0 411 return ret;
michael@0 412 }
michael@0 413
michael@0 414 template<class T>
michael@0 415 int
michael@0 416 NS_WildCardMatch_(const T *str, const T *expr, bool case_insensitive)
michael@0 417 {
michael@0 418 int is_valid = NS_WildCardValid(expr);
michael@0 419 switch(is_valid) {
michael@0 420 case INVALID_SXP:
michael@0 421 return -1;
michael@0 422 default:
michael@0 423 return ::ns_WildCardMatch(str, expr, case_insensitive);
michael@0 424 }
michael@0 425 }
michael@0 426
michael@0 427 int
michael@0 428 NS_WildCardMatch(const char *str, const char *xp,
michael@0 429 bool case_insensitive)
michael@0 430 {
michael@0 431 return NS_WildCardMatch_(str, xp, case_insensitive);
michael@0 432 }
michael@0 433
michael@0 434 int
michael@0 435 NS_WildCardMatch(const char16_t *str, const char16_t *xp,
michael@0 436 bool case_insensitive)
michael@0 437 {
michael@0 438 return NS_WildCardMatch_(str, xp, case_insensitive);
michael@0 439 }

mercurial