/* See section 4.2 of RFC 2616 for header format. */ int http_parse_header(struct http_header **result, const char *header) { const char *p, *q; size_t value_len, value_offset; struct http_header *node, **prev; *result = NULL; prev = result; p = header; while (*p != '\0' && !is_crlf(p)) { /* Get the field name. */ q = p; while (*q != '\0' && is_token_char(*q)) q++; if (*q != ':') { http_header_free(*result); return 400; } node = (struct http_header *) safe_malloc(sizeof(*node)); node->name = mkstr(p, q); node->value = NULL; node->next = NULL; value_len = 0; value_offset = 0; /* Copy the header field value until we hit a CRLF. */ p = q + 1; p = skip_lws(p); for (;;) { q = p; while (*q != '\0' && !is_space_char(*q) && !is_crlf(q)) { /* Section 2.2 of RFC 2616 disallows control characters. */ if (iscntrl((int) (unsigned char) *q)) { http_header_node_free(node); return 400; } q++; } strbuf_append(&node->value, &value_len, &value_offset, p, q - p); p = skip_lws(q); if (is_crlf(p)) break; /* Replace LWS with a single space. */ strbuf_append_str(&node->value, &value_len, &value_offset, " "); } *prev = node; prev = &node->next; p = skip_crlf(p); } return 0; }
/** * Parse a list of elements (#rule in [2.1]). */ bool parse_list(const char *s, regex_t *preg, unsigned int n, unsigned int m, void (*callback)(const char *s, regmatch_t pmatch[])) { int r; unsigned int items = 0; regmatch_t pmatch[20]; do { r = regexec(preg, s, 20, pmatch, 0); if (r) { if (html) printf("<li class='error'>"); printf(" Failed to match list item %i\n", items + 1); if (html) printf("</li>\n"); return false; } if (callback) callback(s, pmatch); items++; s += pmatch[0].rm_eo; s = skip_lws(s); if (*s == 0) break; if (*s != ',') { if (html) printf("<li class='error'>"); printf(" Expecting , after list item %i\n", items); if (html) printf("</li>\n"); return false; } while (*s == ',') s = skip_lws(s + 1); } while (*s != 0); if (items < n || m < items) { if (html) printf("<li class='error'>"); printf(" %i items in list, but there should be ", items); if (m == UINT_MAX) printf("at least %i\n", n); else printf("between %i and %i\n", n, m); if (html) printf("</li>\n"); return false; } return true; }
static void mangle_line (const char * line, const char * prefix) { const char * scan = (skip_name (line)); scan = (skip_lws (scan)); scan = (skip_fixed (scan, '(')); scan = (skip_lws (scan)); scan = (skip_fixed (scan, '"')); write_string (prefix); write_string (scan); write_char ('\n'); fflush (stdout); }
/* Scan a cookie parameter */ static issize_t set_cookie_scanner(char *s) { char *rest; #define LOOKING_AT(s, what) \ (su_casenmatch((s), what, strlen(what)) && (rest = s + strlen(what))) /* Special cases from Netscape spec */ if (LOOKING_AT(s, "expires=")) { msg_time_t value; msg_date_d((char const **)&rest, &value); } else if (LOOKING_AT(s, "path=/")) { for (;;) { rest += span_unreserved(rest); if (*rest != '/') break; rest++; } } else { return msg_attribute_value_scanner(s); } #undef LOOKING_AT if (IS_LWS(*rest)) { *rest++ = '\0'; skip_lws(&rest); } return rest - s; }
/** Decode (parse) a Cookie header */ issize_t http_cookie_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen) { http_cookie_t *c = (http_cookie_t *)h; assert(h); assert(sizeof(*h)); for (;*s;) { /* Ignore empty entries (comma-whitespace) */ if (*s == ',') { *s++ = '\0'; skip_lws(&s); continue; } if (msg_any_list_d(home, &s, (msg_param_t **)&c->c_params, cookie_scanner, ';') == -1) return -1; if (*s != '\0' && *s != ',') return -1; if (!c->c_params) return -1; } http_cookie_update(c); return 0; }
issize_t sip_refer_to_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { issize_t retval; sip_refer_to_t *r = (sip_refer_to_t *)h; retval = sip_name_addr_d(home, &s, &r->r_display, r->r_url, &r->r_params, NULL); if (retval < 0) return retval; if (*s == '?' && !r->r_display && !r->r_url->url_headers) { /* Missing <> around URL */ *s++ = '\0'; r->r_url->url_headers = s; s += strcspn(s, " \t;,"); if (IS_LWS(*s)) *s++ = '\0', skip_lws(&s); if (*s) return -1; r->r_display = s; /* Put empty string in display so that we encode using <> */ } else if (*s) return -1; return retval; }
/** Decode (parse) Set-Cookie header */ issize_t http_set_cookie_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen) { msg_header_t **hh = &h->sh_succ, *h0 = h; http_set_cookie_t *sc = (http_set_cookie_t *)h; msg_param_t *params; assert(h); assert(sizeof(*h)); for (;*s;) { /* Ignore empty entries (comma-whitespace) */ if (*s == ',') { *s++ = '\0'; skip_lws(&s); continue; } if (!h) { /* Allocate next header structure */ if (!(h = msg_header_alloc(home, h0->sh_class, 0))) return -1; *hh = h; h->sh_prev = hh; hh = &h->sh_succ; sc = sc->sc_next = (http_set_cookie_t *)h; } /* "Set-Cookie:" 1#(NAME "=" VALUE *(";" cookie-av))) */ params = su_zalloc(home, MSG_PARAMS_NUM(1) * sizeof(msg_param_t)); if (!params) return -1; params[0] = s, sc->sc_params = params; s += strcspn(s, ",;" LWS); if (*s) { *s++ = '\0'; skip_lws(&s); if (*s && msg_any_list_d(home, &s, (msg_param_t **)&sc->sc_params, set_cookie_scanner, ';') == -1) return -1; } if (*s != '\0' && *s != ',') return -1; if (sc->sc_params) http_set_cookie_update(sc); h = NULL; } return 0; }
/* Scan a cookie parameter */ static issize_t cookie_scanner(char *s) { char *p = s; size_t tlen; skip_token(&s); if (s == p) /* invalid parameter name */ return -1; tlen = s - p; if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } if (*s == '=') { char *v; s++; skip_lws(&s); v = s; /* get value */ if (*s == '"') { size_t qlen = span_quoted(s); if (!qlen) return -1; s += qlen; } else { s += strcspn(s, ",;" LWS); if (s == v) return -1; } if (p + tlen + 1 != v) { memmove(p + tlen + 1, v, s - v); p[tlen] = '='; p[tlen + 1 + (s - v)] = '\0'; } } if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } return s - p; }
/** * Callback for received header data. */ size_t header_callback(char *ptr, size_t msize, size_t nmemb, void *stream) { const size_t size = msize * nmemb; char s[400], *name, *value; UNUSED(stream); printf(html ? "<li><code>" : "* "); print(ptr, size); printf(html ? "</code><ul>" : "\n"); if (size < 2 || ptr[size - 2] != 13 || ptr[size - 1] != 10) { lookup("notcrlf"); if (html) printf("</ul></li>\n"); return size; } if (sizeof s <= size) { lookup("headertoolong"); if (html) printf("</ul></li>\n"); return size; } strncpy(s, ptr, size); s[size - 2] = 0; name = s; value = strchr(s, ':'); if (s[0] == 0) { /* empty header indicates end of headers */ lookup("endofheaders"); if (html) printf("</ul></li>\n"); return 0; } else if (start) { /* Status-Line [6.1] */ check_status_line(s); start = false; } else if (!value) { lookup("missingcolon"); } else { *value = 0; value++; check_header(name, skip_lws(value)); } if (html) printf("</ul></li>\n"); return size; }
issize_t sip_rack_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { sip_rack_t *ra = h->sh_rack; ra->ra_response = strtoul(s, &s, 10); if (IS_LWS(*s)) { skip_lws(&s); ra->ra_cseq = strtoul(s, &s, 10); if (IS_LWS(*s)) { skip_lws(&s); if ((ra->ra_method = sip_method_d(&s, &ra->ra_method_name)) >= 0) { return 0; } } } return -1; }
/** Parse SIP <word "@" word> construct used in @CallID. */ char *sip_word_at_word_d(char **ss) { char *rv = *ss, *s0 = *ss; skip_word(ss); if (s0 == *ss) return NULL; if (**ss == '@') { (*ss)++; s0 = *ss; skip_word(ss); if (s0 == *ss) return NULL; } if (IS_LWS(**ss)) (*ss)++; skip_lws(ss); return rv; }
static GHashTable * parse_param_list (const char *header, char delim) { GHashTable *params; GSList *list, *iter; char *item, *eq, *name_end, *value; gboolean override; params = g_hash_table_new_full (soup_str_case_hash, soup_str_case_equal, g_free, NULL); list = parse_list (header, delim); for (iter = list; iter; iter = iter->next) { item = iter->data; override = FALSE; eq = strchr (item, '='); if (eq) { name_end = (char *)unskip_lws (eq, item); if (name_end == item) { /* That's no good... */ g_free (item); continue; } *name_end = '\0'; value = (char *)skip_lws (eq + 1); if (name_end[-1] == '*' && name_end > item + 1) { name_end[-1] = '\0'; if (!decode_rfc5987 (value)) { g_free (item); continue; } override = TRUE; } else if (*value == '"')
/* Parse the `Content-Range' header and extract the information it contains. Returns 1 if successful, -1 otherwise. */ static int http_process_range (const char *hdr, void *arg) { struct http_process_range_closure *closure = (struct http_process_range_closure *)arg; long num; /* Certain versions of Nutscape proxy server send out `Content-Length' without "bytes" specifier, which is a breach of RFC2068 (as well as the HTTP/1.1 draft which was current at the time). But hell, I must support it... */ if (!strncasecmp (hdr, "bytes", 5)) { hdr += 5; hdr += skip_lws (hdr); if (!*hdr) return 0; } if (!ISDIGIT (*hdr)) return 0; for (num = 0; ISDIGIT (*hdr); hdr++) num = 10 * num + (*hdr - '0'); if (*hdr != '-' || !ISDIGIT (*(hdr + 1))) return 0; closure->first_byte_pos = num; ++hdr; for (num = 0; ISDIGIT (*hdr); hdr++) num = 10 * num + (*hdr - '0'); if (*hdr != '/' || !ISDIGIT (*(hdr + 1))) return 0; closure->last_byte_pos = num; ++hdr; for (num = 0; ISDIGIT (*hdr); hdr++) num = 10 * num + (*hdr - '0'); closure->entity_length = num; return 1; }
/** Decode transport */ issize_t sip_transport_d(char **ss, char const **ttransport) { char const *transport; char *pn, *pv, *pt; size_t pn_len, pv_len, pt_len; char *s = *ss; #define TRANSPORT_MATCH(t) \ (su_casenmatch(s + 7, t + 7, (sizeof t) - 8) && \ (!s[sizeof(t) - 1] || IS_LWS(s[sizeof(t) - 1])) \ && (transport = t, s += sizeof(t) - 1)) if (!su_casenmatch(s, "SIP/2.0", 7) || (!TRANSPORT_MATCH(sip_transport_udp) && !TRANSPORT_MATCH(sip_transport_tcp) && !TRANSPORT_MATCH(sip_transport_sctp) && !TRANSPORT_MATCH(sip_transport_ws) && !TRANSPORT_MATCH(sip_transport_wss) && !TRANSPORT_MATCH(sip_transport_tls))) { /* Protocol name */ transport = pn = s; skip_token(&s); pn_len = s - pn; skip_lws(&s); if (pn_len == 0 || *s++ != '/') return -1; skip_lws(&s); /* Protocol version */ pv = s; skip_token(&s); pv_len = s - pv; skip_lws(&s); if (pv_len == 0 || *s++ != '/') return -1; skip_lws(&s); /* Transport protocol */ pt = s; skip_token(&s); pt_len = s - pt; if (pt_len == 0) return -1; /* Remove whitespace between protocol name and version */ if (pn + pn_len + 1 != pv) { pn[pn_len] = '/'; pv = memmove(pn + pn_len + 1, pv, pv_len); } /* Remove whitespace between protocol version and transport */ if (pv + pv_len + 1 != pt) { pv[pv_len] = '/'; pt = memmove(pv + pv_len + 1, pt, pt_len); pt[pt_len] = '\0'; /* extra whitespace? */ if (su_casematch(transport, sip_transport_udp)) transport = sip_transport_udp; else if (su_casematch(transport, sip_transport_tcp)) transport = sip_transport_tcp; else if (su_casematch(transport, sip_transport_sctp)) transport = sip_transport_sctp; else if (su_casematch(transport, sip_transport_ws)) transport = sip_transport_ws; else if (su_casematch(transport, sip_transport_wss)) transport = sip_transport_wss; else if (su_casematch(transport, sip_transport_tls)) transport = sip_transport_tls; } } if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } *ss = s; *ttransport = transport; return 0; }
/* http://wp.netscape.com/newsref/std/cookie_spec.html */ void header_set_cookie(const char *s) { bool ok = true; int r; const char *semi = strchr(s, ';'); const char *s2; struct tm tm; double diff; time_t time0, time1; regmatch_t pmatch[20]; if (semi) s2 = strndup(s, semi - s); else s2 = s; r = regexec(&re_cookie_nameval, s2, 0, 0, 0); if (r) { lookup("cookiebadnameval"); ok = false; } if (!semi) return; s = skip_lws(semi + 1); while (*s) { semi = strchr(s, ';'); if (semi) s2 = strndup(s, semi - s); else s2 = s; if (strncasecmp(s2, "expires=", 8) == 0) { s2 += 8; r = regexec(&re_cookie_expires, s2, 20, pmatch, 0); if (r == 0) { tm.tm_mday = atoi(s2 + pmatch[2].rm_so); tm.tm_mon = month(s2 + pmatch[3].rm_so); tm.tm_year = atoi(s2 + pmatch[4].rm_so) - 1900; tm.tm_hour = atoi(s2 + pmatch[5].rm_so); tm.tm_min = atoi(s2 + pmatch[6].rm_so); tm.tm_sec = atoi(s2 + pmatch[7].rm_so); time0 = time(0); time1 = mktime_from_utc(&tm); diff = difftime(time0, time1); if (10 < diff) { lookup("cookiepastdate"); ok = false; } } else { lookup("cookiebaddate"); ok = false; } } else if (strncasecmp(s2, "domain=", 7) == 0) { } else if (strncasecmp(s2, "path=", 5) == 0) { if (s2[5] != '/') { lookup("cookiebadpath"); ok = false; } } else if (strcasecmp(s, "secure") == 0) { } else { if (html) printf("<li class='warning'>"); printf(" Set-Cookie field '%s':\n", s2); if (html) printf("</li>\n"); lookup("cookieunknownfield"); ok = false; } if (semi) s = skip_lws(semi + 1); else break; } if (ok) lookup("ok"); }
/** * soup_header_parse_quality_list: * @header: a header value * @unacceptable: (out) (allow-none) (transfer full) (element-type utf8): on * return, will contain a list of unacceptable values * * Parses a header whose content is a list of items with optional * "qvalue"s (eg, Accept, Accept-Charset, Accept-Encoding, * Accept-Language, TE). * * If @unacceptable is not %NULL, then on return, it will contain the * items with qvalue 0. Either way, those items will be removed from * the main list. * * Return value: (transfer full) (element-type utf8): a #GSList of * acceptable values (as allocated strings), highest-qvalue first. **/ GSList * soup_header_parse_quality_list (const char *header, GSList **unacceptable) { GSList *unsorted; QualityItem *array; GSList *sorted, *iter; char *item, *semi; const char *param, *equal, *value; double qval; int n; g_return_val_if_fail (header != NULL, NULL); if (unacceptable) *unacceptable = NULL; unsorted = soup_header_parse_list (header); array = g_new0 (QualityItem, g_slist_length (unsorted)); for (iter = unsorted, n = 0; iter; iter = iter->next) { item = iter->data; qval = 1.0; for (semi = strchr (item, ';'); semi; semi = strchr (semi + 1, ';')) { param = skip_lws (semi + 1); if (*param != 'q') continue; equal = skip_lws (param + 1); if (!equal || *equal != '=') continue; value = skip_lws (equal + 1); if (!value) continue; if (value[0] != '0' && value[0] != '1') continue; qval = (double)(value[0] - '0'); if (value[0] == '0' && value[1] == '.') { if (g_ascii_isdigit (value[2])) { qval += (double)(value[2] - '0') / 10; if (g_ascii_isdigit (value[3])) { qval += (double)(value[3] - '0') / 100; if (g_ascii_isdigit (value[4])) qval += (double)(value[4] - '0') / 1000; } } } *semi = '\0'; break; } if (qval == 0.0) { if (unacceptable) { *unacceptable = g_slist_prepend (*unacceptable, item); } } else { array[n].item = item; array[n].qval = qval; n++; } } g_slist_free (unsorted); qsort (array, n, sizeof (QualityItem), sort_by_qval); sorted = NULL; while (n--) sorted = g_slist_prepend (sorted, array[n].item); g_free (array); return sorted; }