#include "internal.h" static void mg_strlcpy(register char *dst, register const char *src, size_t n) { for (; *src != '\0' && n > 1; n--) { *dst++ = *src++; } *dst = '\0'; } static int lowercase(const char *s) { return tolower(* (const unsigned char *) s); } static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { int diff = 0; if (len > 0) do { diff = lowercase(s1++) - lowercase(s2++); } while (diff == 0 && s1[-1] != '\0' && --len > 0); return diff; } static int mg_strcasecmp(const char *s1, const char *s2) { int diff; do { diff = lowercase(s1++) - lowercase(s2++); } while (diff == 0 && s1[-1] != '\0'); return diff; } static char * mg_strndup(const char *ptr, size_t len) { char *p; if ((p = (char *) malloc(len + 1)) != NULL) { mg_strlcpy(p, ptr, len + 1); } return p; } static char * mg_strdup(const char *str) { return mg_strndup(str, strlen(str)); } static const char *mg_strcasestr(const char *big_str, const char *small_str) { int i, big_len = strlen(big_str), small_len = strlen(small_str); for (i = 0; i <= big_len - small_len; i++) { if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { return big_str + i; } } return NULL; } // Like snprintf(), but never returns negative value, or a value // that is larger than a supplied buffer. // Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability // in his audit report. static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) { int n; if (buflen == 0) { return 0; } n = vsnprintf(buf, buflen, fmt, ap); if (n < 0) { n = 0; } else if (n >= (int) buflen) { n = (int) buflen - 1; } buf[n] = '\0'; return n; } static int mg_snprintf(char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(3, 4); static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = mg_vsnprintf(buf, buflen, fmt, ap); va_end(ap); return n; } // Skip the characters until one of the delimiters characters found. // 0-terminate resulting word. Skip the delimiter and following whitespaces. // Advance pointer to buffer to the next word. Return found 0-terminated word. // Delimiters can be quoted with quotechar. static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) { char *p, *begin_word, *end_word, *end_whitespace; begin_word = *buf; end_word = begin_word + strcspn(begin_word, delimiters); // Check for quotechar if (end_word > begin_word) { p = end_word - 1; while (*p == quotechar) { // If there is anything beyond end_word, copy it if (*end_word == '\0') { *p = '\0'; break; } else { size_t end_off = strcspn(end_word + 1, delimiters); memmove (p, end_word, end_off + 1); p += end_off; // p must correspond to end_word - 1 end_word += end_off + 1; } } for (p++; p < end_word; p++) { *p = '\0'; } } if (*end_word == '\0') { *buf = end_word; } else { end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); for (p = end_word; p < end_whitespace; p++) { *p = '\0'; } *buf = end_whitespace; } return begin_word; } // Simplified version of skip_quoted without quote char // and whitespace == delimiters static char *skip(char **buf, const char *delimiters) { return skip_quoted(buf, delimiters, delimiters, 0); } // Return HTTP header value, or NULL if not found. static const char *get_header(const struct mg_request_info *ri, const char *name) { int i; for (i = 0; i < ri->num_headers; i++) if (!mg_strcasecmp(name, ri->http_headers[i].name)) return ri->http_headers[i].value; return NULL; } const char *mg_get_header(const struct mg_connection *conn, const char *name) { return get_header(&conn->request_info, name); } // A helper function for traversing a comma separated list of values. // It returns a list pointer shifted to the next value, or NULL if the end // of the list found. // Value is stored in val vector. If value has form "x=y", then eq_val // vector is initialized to point to the "y" part, and val vector length // is adjusted to point only to "x". static const char *next_option(const char *list, struct vec *val, struct vec *eq_val) { if (list == NULL || *list == '\0') { // End of the list list = NULL; } else { val->ptr = list; if ((list = strchr(val->ptr, ',')) != NULL) { // Comma found. Store length and shift the list ptr val->len = list - val->ptr; list++; } else { // This value is the last one list = val->ptr + strlen(val->ptr); val->len = list - val->ptr; } if (eq_val != NULL) { // Value has form "x=y", adjust pointers and lengths // so that val points to "x", and eq_val points to "y". eq_val->len = 0; eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); if (eq_val->ptr != NULL) { eq_val->ptr++; // Skip over '=' character eq_val->len = val->ptr + val->len - eq_val->ptr; val->len = (eq_val->ptr - val->ptr) - 1; } } } return list; } // Perform case-insensitive match of string against pattern static int match_prefix(const char *pattern, int pattern_len, const char *str) { const char *or_str; int i, j, len, res; if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) { res = match_prefix(pattern, or_str - pattern, str); return res > 0 ? res : match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str); } i = j = 0; res = -1; for (; i < pattern_len; i++, j++) { if (pattern[i] == '?' && str[j] != '\0') { continue; } else if (pattern[i] == '$') { return str[j] == '\0' ? j : -1; } else if (pattern[i] == '*') { i++; if (pattern[i] == '*') { i++; len = (int) strlen(str + j); } else { len = (int) strcspn(str + j, "/"); } if (i == pattern_len) { return j + len; } do { res = match_prefix(pattern + i, pattern_len - i, str + j + len); } while (res == -1 && len-- > 0); return res == -1 ? -1 : j + res + len; } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { return -1; } } return j; }