/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /**@CFILE http_extra.c * * Extra HTTP headers * * @author Pekka Pessi * * @date Created: Tue Jun 13 02:57:51 2000 ppessi */ #include "config.h" #include #include #include #include #include #include /* Avoid casting http_t to msg_pub_t and http_header_t to msg_header_t */ #define MSG_PUB_T struct http_s #define MSG_HDR_T union http_header_u #include "sofia-sip/http_parser.h" /* ========================================================================== */ /**@HTTP_HEADER http_proxy_connection Proxy-Connection extension header. */ #define http_proxy_connection_d msg_list_d #define http_proxy_connection_e msg_list_e msg_hclass_t http_proxy_connection_class[] = HTTP_HEADER_CLASS_LIST(proxy_connection, "Proxy-Connection", list); /* ====================================================================== */ /**@HTTP_HEADER http_cookie Cookie extension header. * * The Cookie header is used to transmit state information from server * back to the http client. Its syntax is defined in RFC 2109 section 4.3.4 * as follows: * * @code * cookie = "Cookie:" cookie-version * 1*((";" | ",") cookie-value) * cookie-value = NAME "=" VALUE [";" path] [";" domain] * cookie-version = "$Version" "=" value * NAME = attr * VALUE = value * path = "$Path" "=" value * domain = "$Domain" "=" value * @endcode * */ /**@ingroup http_cookie * * @typedef typedef struct http_cookie_s http_cookie_t; * * The structure http_cookie_t contains representation of @b Cookie * header. Please note that a single http_cookie_t can contain many * cookies. * * The http_cookie_t is defined as follows: * @code * typedef struct http_cookie_s * { * } http_cookie_t; * @endcode */ /**Update Cookie parameters. * * The function http_cookie_update() updates a @b Cookie parameter * shortcuts. * * @param sc pointer to a @c http_cookie_t object */ su_inline void http_cookie_update(http_cookie_t *c) { size_t i; c->c_name = NULL; c->c_version = NULL, c->c_domain = NULL, c->c_path = NULL; if (!c->c_params) return; if (!(MSG_PARAM_MATCH(c->c_version, c->c_params[0], "$Version"))) return; if (!c->c_params[1] || c->c_params[1][0] == '$') return; c->c_name = c->c_params[1]; for (i = 2; ; i++) { msg_param_t p = c->c_params[i]; if (!p || *p++ != '$') break; switch (p[0]) { case 'd': case 'D': MSG_PARAM_MATCH(c->c_domain, p, "Domain"); break; case 'p': case 'P': MSG_PARAM_MATCH(c->c_path, p, "Path"); break; } } } /* 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; } /** 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; } /** Encode (print) a Cookie header */ issize_t http_cookie_e(char b[], isize_t bsiz, msg_header_t const *h, int flags) { char *b0 = b, *end = b + bsiz; http_cookie_t const *c = (http_cookie_t *)h; size_t i; if (c->c_params) { for (i = 0; c->c_params[i]; i++) { if (i > 0) MSG_CHAR_E(b, end, ';'); MSG_STRING_E(b, end, c->c_params[i]); } } MSG_TERM_E(b, end); return b - b0; } /** Calculate extra storage used by Cookie header field */ isize_t http_cookie_dup_xtra(msg_header_t const *h, isize_t offset) { http_cookie_t const *c = (http_cookie_t *)h; MSG_PARAMS_SIZE(offset, c->c_params); return offset; } /** Duplicate a Cookie header field */ char *http_cookie_dup_one(msg_header_t *dst, msg_header_t const *src, char *b, isize_t xtra) { http_cookie_t *c = (http_cookie_t *)dst; http_cookie_t const *o = (http_cookie_t const *)src; char *end = b + xtra; b = msg_params_dup(&c->c_params, o->c_params, b, xtra); http_cookie_update(c); assert(b <= end); (void)end; return b; } msg_hclass_t http_cookie_class[] = HTTP_HEADER_CLASS(cookie, "Cookie", c_params, append, cookie); /* ====================================================================== */ /**@HTTP_HEADER http_set_cookie Set-Cookie extension header. * * The Set-Cookie header is used to transmit state information from server * back to the http client. Its syntax is defined in RFC 2109 section 4.2.2 * as follows: * * @code * set-cookie = "Set-Cookie:" cookies * cookies = 1#cookie * cookie = NAME "=" VALUE *(";" cookie-av) * NAME = attr * VALUE = value * cookie-av = "Comment" "=" value * | "Domain" "=" value * | "Max-Age" "=" value * | "Path" "=" value * | "Secure" * | "Version" "=" 1*DIGIT * * @endcode * */ /**@ingroup http_set_cookie * * @typedef typedef struct http_set_cookie_s http_set_cookie_t; * * The structure http_set_cookie_t contains representation of @b Set-Cookie * header. * * The http_set_cookie_t is defined as follows: * @code * typedef struct http_set_cookie_s * { * } http_set_cookie_t; * @endcode */ /**Update Set-Cookie parameters. * * The function http_set_cookie_update() updates a @b Set-Cookie parameter * shortcuts. * * @param sc pointer to a @c http_set_cookie_t object */ su_inline void http_set_cookie_update(http_set_cookie_t *sc) { size_t i; sc->sc_name = NULL; sc->sc_version = NULL, sc->sc_domain = NULL, sc->sc_path = NULL; sc->sc_comment = NULL, sc->sc_max_age = NULL, sc->sc_secure = 0; if (!sc->sc_params) return; sc->sc_name = sc->sc_params[0]; for (i = 1; sc->sc_params[i]; i++) { msg_param_t p = sc->sc_params[i]; switch (p[0]) { case 'c': case 'C': MSG_PARAM_MATCH(sc->sc_comment, p, "Comment"); break; case 'd': case 'D': MSG_PARAM_MATCH(sc->sc_domain, p, "Domain"); break; case 'm': case 'M': MSG_PARAM_MATCH(sc->sc_max_age, p, "Max-Age"); break; case 'p': case 'P': MSG_PARAM_MATCH(sc->sc_path, p, "Path"); break; case 's': case 'S': MSG_PARAM_MATCH_P(sc->sc_secure, p, "Secure"); break; case 'v': case 'V': MSG_PARAM_MATCH(sc->sc_version, p, "Version"); break; } } } #include /* Scan a cookie parameter */ static issize_t set_cookie_scanner(char *s) { char *rest; #define LOOKING_AT(s, what) \ (strncasecmp((s), what, strlen(what)) == 0 && (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) 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; } /** Encode (print) Set-Cookie header */ issize_t http_set_cookie_e(char b[], isize_t bsiz, msg_header_t const *h, int flags) { char *b0 = b, *end = b + bsiz; http_set_cookie_t const *sc = (http_set_cookie_t *)h; size_t i; if (sc->sc_params) { for (i = 0; sc->sc_params[i]; i++) { if (i > 0) MSG_CHAR_E(b, end, ';'); MSG_STRING_E(b, end, sc->sc_params[i]); } } MSG_TERM_E(b, end); return b - b0; } /** Calculate extra storage used by Set-Cookie header field */ isize_t http_set_cookie_dup_xtra(msg_header_t const *h, isize_t offset) { http_set_cookie_t const *sc = (http_set_cookie_t *)h; MSG_PARAMS_SIZE(offset, sc->sc_params); return offset; } /** Duplicate a Set-Cookie header field */ char *http_set_cookie_dup_one(msg_header_t *dst, msg_header_t const *src, char *b, isize_t xtra) { http_set_cookie_t *sc = (http_set_cookie_t *)dst; http_set_cookie_t const *o = (http_set_cookie_t const *)src; char *end = b + xtra; b = msg_params_dup(&sc->sc_params, o->sc_params, b, xtra); http_set_cookie_update(sc); assert(b <= end); (void)end; return b; } msg_hclass_t http_set_cookie_class[] = HTTP_HEADER_CLASS(set_cookie, "Set-Cookie", sc_params, append, set_cookie);