470 lines
11 KiB
C
470 lines
11 KiB
C
/*
|
|
* This file is part of the Sofia-SIP package
|
|
*
|
|
* Copyright (C) 2005 Nokia Corporation.
|
|
*
|
|
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
|
|
*
|
|
* 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 <Pekka.Pessi@nokia.com>
|
|
*
|
|
* @date Created: Tue Jun 13 02:57:51 2000 ppessi
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
|
|
/* 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
|
|
*/
|
|
static 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
|
|
*/
|
|
static 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 <sofia-sip/msg_date.h>
|
|
|
|
/* 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);
|