/*! * When looking up extensions, we can have different requests * identified by the 'action' argument, as follows. * Note that the coding is such that the low 4 bits are the * third argument to extension_match_core. */ enum ext_match_t { E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */ E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */ E_MATCH = 0x02, /* extension is an exact match */ E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */ E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */ E_EXEC = 0x22, /* want to exec an extension. Requires exact match */ E_FINDLABEL = 0x32 /* returns the priority for a given label. Requires exact match */ }; /* * Internal function for ast_extension_{match|close} * return 0 on no-match, 1 on match, 2 on early match. * mode is as follows: * E_MATCH success only on exact match * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern) * E_CANMATCH either of the above. */ static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode) { mode &= E_MATCH_MASK; /* only consider the relevant bits */ if (pattern[0] != '_') { /* not a pattern, try exact or partial match */ int ld = strlen(data), lp = strlen(pattern); if (lp < ld) /* pattern too short, cannot match */ return 0; /* depending on the mode, accept full or partial match or both */ if (mode == E_MATCH) return !strcmp(pattern, data); /* 1 on match, 0 on fail */ if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */ return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */ else return 0; } pattern++; /* skip leading _ */ while (*data && *pattern && *pattern != '/') { const char *end; if (*data == '-') { /* skip '-' in data (just a separator) */ data++; continue; } switch (toupper(*pattern)) { case '[': /* a range */ end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */ if (end == NULL) { ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n"); return 0; /* unconditional failure */ } for (pattern++; pattern != end; pattern++) { if (pattern+2 < end && pattern[1] == '-') { /* this is a range */ if (*data >= pattern[0] && *data <= pattern[2]) break; /* match found */ else { pattern += 2; /* skip a total of 3 chars */ continue; } } else if (*data == pattern[0]) break; /* match found */ } if (pattern == end) return 0; pattern = end; /* skip and continue */ break; case 'N': if (*data < '2' || *data > '9') return 0; break; case 'X': if (*data < '0' || *data > '9') return 0; break; case 'Z': if (*data < '1' || *data > '9') return 0; break; case '.': /* Must match, even with more digits */ return 1; case '!': /* Early match */ return 2; case ' ': case '-': /* Ignore these in patterns */ data--; /* compensate the final data++ */ break; default: if (*data != *pattern) return 0; } data++; pattern++; } if (*data) /* data longer than pattern, no match */ return 0; /* * match so far, but ran off the end of the data. * Depending on what is next, determine match or not. */ if (*pattern == '\0' || *pattern == '/') /* exact match */ return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */ else if (*pattern == '!') /* early match */ return 2; else /* partial match */ return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */ } int ast_extension_match(const char *pattern, const char *data) { return extension_match_core(pattern, data, E_MATCH); } int ast_extension_close(const char *pattern, const char *data, int needmore) { if (needmore != E_MATCHMORE && needmore != E_CANMATCH) ast_log(LOG_WARNING, "invalid argument %d\n", needmore); return extension_match_core(pattern, data, needmore); }