|  | @@ -639,24 +639,56 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Skip the characters until one of the delimiters characters found.
 | 
	
		
			
				|  |  | -// 0-terminate resulting word. Skip the rest of the delimiters if any.
 | 
	
		
			
				|  |  | +// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
 | 
	
		
			
				|  |  |  // Advance pointer to buffer to the next word. Return found 0-terminated word.
 | 
	
		
			
				|  |  | -static char *skip(char **buf, const char *delimiters) {
 | 
	
		
			
				|  |  | -  char *p, *begin_word, *end_word, *end_delimiters;
 | 
	
		
			
				|  |  | +// 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);
 | 
	
		
			
				|  |  | -  end_delimiters = end_word + strspn(end_word, delimiters);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  for (p = end_word; p < end_delimiters; p++) {
 | 
	
		
			
				|  |  | -    *p = '\0';
 | 
	
		
			
				|  |  | +  /* 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';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  *buf = end_delimiters;
 | 
	
		
			
				|  |  | +  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) {
 | 
	
	
		
			
				|  | @@ -2058,26 +2090,26 @@ static int parse_auth_header(struct mg_connection *conn, char *buf,
 | 
	
		
			
				|  |  |    s = buf;
 | 
	
		
			
				|  |  |    (void) memset(ah, 0, sizeof(*ah));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Gobble initial spaces
 | 
	
		
			
				|  |  | -  while (isspace(* (unsigned char *) s)) {
 | 
	
		
			
				|  |  | -    s++;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    // Parse authorization header
 | 
	
		
			
				|  |  |    for (;;) {
 | 
	
		
			
				|  |  | -    name = skip(&s, "=");
 | 
	
		
			
				|  |  | -    value = skip(&s, ", ");  // IE uses commas, FF uses spaces
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Handle commas: Digest username="a", realm="b", ...
 | 
	
		
			
				|  |  | -    if (value[strlen(value) - 1] == ',') {
 | 
	
		
			
				|  |  | -      value[strlen(value) - 1] = '\0';
 | 
	
		
			
				|  |  | +    // Gobble initial spaces
 | 
	
		
			
				|  |  | +    while (isspace(* (unsigned char *) s)) {
 | 
	
		
			
				|  |  | +      s++;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // Trim double quotes around values
 | 
	
		
			
				|  |  | -    if (*value == '"') {
 | 
	
		
			
				|  |  | -      value++;
 | 
	
		
			
				|  |  | -      value[strlen(value) - 1] = '\0';
 | 
	
		
			
				|  |  | -    } else if (*value == '\0') {
 | 
	
		
			
				|  |  | +    name = skip_quoted(&s, "=", " ", 0);
 | 
	
		
			
				|  |  | +    /* Value is either quote-delimited, or ends at first comma or space. */
 | 
	
		
			
				|  |  | +    if (s[0] == '\"') {
 | 
	
		
			
				|  |  | +      s++;
 | 
	
		
			
				|  |  | +      value = skip_quoted(&s, "\"", " ", '\\');
 | 
	
		
			
				|  |  | +      if (s[0] == ',') {
 | 
	
		
			
				|  |  | +        s++;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    else
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +      value = skip_quoted(&s, ", ", " ", 0);  // IE uses commas, FF uses spaces
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (*name == '\0') {
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -2532,7 +2564,7 @@ static void parse_http_headers(char **buf, struct mg_request_info *ri) {
 | 
	
		
			
				|  |  |    int i;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
 | 
	
		
			
				|  |  | -    ri->http_headers[i].name = skip(buf, ": ");
 | 
	
		
			
				|  |  | +    ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
 | 
	
		
			
				|  |  |      ri->http_headers[i].value = skip(buf, "\r\n");
 | 
	
		
			
				|  |  |      if (ri->http_headers[i].name[0] == '\0')
 | 
	
		
			
				|  |  |        break;
 |