summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip/sip_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/pjsip/sip_parser.c')
-rw-r--r--pjsip/src/pjsip/sip_parser.c1551
1 files changed, 1551 insertions, 0 deletions
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
new file mode 100644
index 00000000..34e6d4d9
--- /dev/null
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -0,0 +1,1551 @@
+/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.c 17 9/11/05 9:28a Bennylp $ */
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pj/scanner.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <ctype.h> /* tolower() */
+
+#define RESERVED ";/?:@&=+$,"
+#define MARK "-_.!~*'()"
+#define ESCAPED "%"
+#define USER "&=+$,;?/"
+#define PASS "&=+$,"
+#define TOKEN "-.!%*_=`'~+" /* '+' is because of application/pidf+xml in Content-Type! */
+#define HOST "_-."
+#define HEX_DIGIT "abcdefABCDEF"
+#define PARAM_CHAR "[]/:&+$" MARK "%"
+
+#define PJSIP_VERSION "SIP/2.0"
+#define PJSIP_SYN_ERR_EXCEPTION 1
+
+#define UNREACHED(expr)
+
+typedef struct handler_rec
+{
+ char hname[PJSIP_MAX_HNAME_LEN+1];
+ pj_size_t hname_len;
+ pj_uint32_t hname_hash;
+ pjsip_parse_hdr_func *handler;
+} handler_rec;
+
+static handler_rec handler[127];
+static unsigned handler_count;
+static int parser_is_initialized;
+
+
+/*
+ * Global vars (also extern).
+ */
+const pj_str_t pjsip_USER_STR = { "user", 4};
+const pj_str_t pjsip_METHOD_STR = { "method", 6};
+const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9};
+const pj_str_t pjsip_MADDR_STR = { "maddr", 5 };
+const pj_str_t pjsip_LR_STR = { "lr", 2 };
+const pj_str_t pjsip_SIP_STR = { "sip", 3 };
+const pj_str_t pjsip_SIPS_STR = { "sips", 4 };
+const pj_str_t pjsip_TEL_STR = { "tel", 3 };
+const pj_str_t pjsip_BRANCH_STR = { "branch", 6 };
+const pj_str_t pjsip_TTL_STR = { "ttl", 3 };
+const pj_str_t pjsip_PNAME_STR = { "received", 8 };
+const pj_str_t pjsip_Q_STR = { "q", 1 };
+const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 };
+const pj_str_t pjsip_TAG_STR = { "tag", 3 };
+const pj_str_t pjsip_RPORT_STR = { "rport", 5};
+
+pj_char_spec pjsip_HOST_SPEC, /* For scanning host part. */
+ pjsip_DIGIT_SPEC, /* Decimal digits */
+ pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */
+ pjsip_ALNUM_SPEC, /* Decimal + Alpha. */
+ pjsip_TOKEN_SPEC, /* Token. */
+ pjsip_HEX_SPEC, /* Hexadecimal digits. */
+ pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when it's not quoted.) */
+ pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
+ pjsip_PASSWD_SPEC, /* Password. */
+ pjsip_USER_SPEC, /* User */
+ pjsip_ARRAY_ELEMENTS, /* Array separator. */
+ pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/
+ pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name in URL. */
+
+
+/*
+ * Forward decl.
+ */
+static pjsip_msg * int_parse_msg( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_parser_err_report *err_list);
+static void int_parse_param( pj_scanner *scanner,
+ pj_str_t *pname,
+ pj_str_t *pvalue);
+static void int_parse_req_line( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_request_line *req_line);
+static int int_is_next_user( pj_scanner *scanner);
+static void int_parse_status_line( pj_scanner *scanner,
+ pjsip_status_line *line);
+static void int_parse_user_pass( pj_scanner *scanner,
+ pj_str_t *user,
+ pj_str_t *pass);
+static void int_parse_uri_host_port( pj_scanner *scanner,
+ pj_str_t *p_host,
+ int *p_port);
+static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool, unsigned option);
+static pjsip_url * int_parse_sip_url( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_bool_t parse_params);
+static pjsip_name_addr* int_parse_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool );
+static void parse_hdr_end( pj_scanner *scanner );
+static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_allow_hdr* parse_hdr_allow( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_contact_hdr* parse_hdr_contact( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_clen_hdr* parse_hdr_content_length( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_ctype_hdr* parse_hdr_content_type( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_cseq_hdr* parse_hdr_cseq( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_expires_hdr* parse_hdr_expires( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_from_hdr* parse_hdr_from( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_min_expires_hdr* parse_hdr_min_expires( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_rr_hdr* parse_hdr_rr( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_route_hdr* parse_hdr_route( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_retry_after_hdr* parse_hdr_retry_after( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_supported_hdr* parse_hdr_supported( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_to_hdr* parse_hdr_to( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_unsupported_hdr* parse_hdr_unsupported( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_via_hdr* parse_hdr_via( pj_scanner *scanner,
+ pj_pool_t *pool);
+static pjsip_generic_string_hdr* parse_hdr_generic_string( pj_scanner *scanner,
+ pj_pool_t *pool);
+
+/* Convert non NULL terminated string to integer. */
+static unsigned long pj_strtoul_mindigit(const pj_str_t *str, unsigned mindig)
+{
+ unsigned long value;
+ unsigned i;
+
+ value = 0;
+ for (i=0; i<(unsigned)str->slen; ++i) {
+ value = value * 10 + (str->ptr[i] - '0');
+ }
+ for (; i<mindig; ++i) {
+ value = value * 10;
+ }
+ return value;
+}
+
+/* Case insensitive comparison */
+#define parser_stricmp(str1, str2) \
+ (str1.slen != str2.slen ? -1 : \
+ !(memcmp(str1.ptr, str2.ptr, str1.slen)==0 || stricmp(str1.ptr, str2.ptr)==0))
+
+/* Syntax error handler for parser. */
+static void on_syntax_error(pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+/* Concatenate unrecognized params into single string. */
+void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
+ const pj_str_t *pname, const pj_str_t *pvalue, int sepchar)
+{
+ char *new_param, *p;
+ int len;
+
+ len = param->slen + pname->slen + pvalue->slen + 3;
+ p = new_param = pj_pool_alloc(pool, len);
+
+ if (param->slen) {
+ int old_len = param->slen;
+ pj_memcpy(p, param->ptr, old_len);
+ p += old_len;
+ }
+ *p++ = (char)sepchar;
+ pj_memcpy(p, pname->ptr, pname->slen);
+ p += pname->slen;
+
+ if (pvalue->slen) {
+ *p++ = '=';
+ pj_memcpy(p, pvalue->ptr, pvalue->slen);
+ p += pvalue->slen;
+ }
+
+ *p = '\0';
+
+ param->ptr = new_param;
+ param->slen = p - new_param;
+}
+
+/* Concatenate unrecognized params into single string. */
+static void concat_param( pj_str_t *param, pj_pool_t *pool,
+ const pj_str_t *pname, const pj_str_t *pvalue )
+{
+ pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
+}
+
+/* Initialize static properties of the parser. */
+static void init_parser()
+{
+ static int initialized;
+
+ if (initialized)
+ return;
+
+ initialized = 1;
+
+ pj_cs_add_num( pjsip_DIGIT_SPEC );
+ pj_cs_add_alpha( pjsip_ALPHA_SPEC );
+
+ pj_cs_add_alpha( pjsip_ALNUM_SPEC );
+ pj_cs_add_num( pjsip_ALNUM_SPEC );
+
+ pj_cs_add_str(pjsip_NEWLINE_OR_EOF_SPEC, "\r\n");
+ //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0);
+
+ pj_cs_add_str( pjsip_ARRAY_ELEMENTS, ",\r\n");
+
+ pj_memcpy(pjsip_TOKEN_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+ pj_cs_add_str( pjsip_TOKEN_SPEC, TOKEN);
+
+ pj_memcpy(pjsip_HOST_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+ pj_cs_add_str( pjsip_HOST_SPEC, HOST);
+
+ pj_memcpy(pjsip_HEX_SPEC, pjsip_DIGIT_SPEC, sizeof(pj_char_spec));
+ pj_cs_add_str( pjsip_HEX_SPEC, HEX_DIGIT);
+
+ pj_memcpy(pjsip_PARAM_CHAR_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+ pj_cs_add_str( pjsip_PARAM_CHAR_SPEC, PARAM_CHAR);
+
+ pj_memcpy(pjsip_USER_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+ pj_cs_add_str( pjsip_USER_SPEC, MARK ESCAPED USER );
+
+ pj_memcpy(pjsip_PASSWD_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec));
+ pj_cs_add_str( pjsip_PASSWD_SPEC, MARK ESCAPED PASS);
+
+ pj_cs_add_str( pjsip_PROBE_USER_HOST_SPEC, "@ \n>");
+ pj_cs_invert( pjsip_PROBE_USER_HOST_SPEC );
+
+ pj_cs_add_str( pjsip_DISPLAY_SCAN_SPEC, ":\r\n<");
+
+ pjsip_register_hdr_parser( "Accept", NULL, (pjsip_parse_hdr_func*) &parse_hdr_accept);
+ pjsip_register_hdr_parser( "Allow", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow);
+ pjsip_register_hdr_parser( "Call-ID", NULL, (pjsip_parse_hdr_func*) &parse_hdr_call_id);
+ pjsip_register_hdr_parser( "Contact", "m", (pjsip_parse_hdr_func*) &parse_hdr_contact);
+ pjsip_register_hdr_parser( "Content-Length", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_length);
+ pjsip_register_hdr_parser( "Content-Type", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_type);
+ pjsip_register_hdr_parser( "CSeq", NULL, (pjsip_parse_hdr_func*) &parse_hdr_cseq);
+ pjsip_register_hdr_parser( "Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_expires);
+ pjsip_register_hdr_parser( "From", "f", (pjsip_parse_hdr_func*) &parse_hdr_from);
+ pjsip_register_hdr_parser( "Max-Forwards", NULL, (pjsip_parse_hdr_func*) &parse_hdr_max_forwards);
+ pjsip_register_hdr_parser( "Min-Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_min_expires);
+ pjsip_register_hdr_parser( "Record-Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_rr);
+ pjsip_register_hdr_parser( "Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_route);
+ pjsip_register_hdr_parser( "Require", NULL, (pjsip_parse_hdr_func*) &parse_hdr_require);
+ pjsip_register_hdr_parser( "Retry-After", NULL, (pjsip_parse_hdr_func*) &parse_hdr_retry_after);
+ pjsip_register_hdr_parser( "Supported", "k", (pjsip_parse_hdr_func*) &parse_hdr_supported);
+ pjsip_register_hdr_parser( "To", "t", (pjsip_parse_hdr_func*) &parse_hdr_to);
+ pjsip_register_hdr_parser( "Unsupported", NULL, (pjsip_parse_hdr_func*) &parse_hdr_unsupported);
+ pjsip_register_hdr_parser( "Via", NULL, (pjsip_parse_hdr_func*) &parse_hdr_via);
+
+ /* Register auth parser. */
+ pjsip_auth_init_parser();
+
+}
+
+static void init_sip_parser()
+{
+ if (!parser_is_initialized) {
+ /* Prevent race cond. */
+ pj_enter_critical_section();
+ if (!parser_is_initialized) {
+ init_parser();
+ parser_is_initialized = 1;
+ }
+ pj_leave_critical_section();
+ }
+}
+
+/* Compare the handler record with header name, and return:
+ * - 0 if handler match.
+ * - <0 if handler is 'less' than the header name.
+ * - >0 if handler is 'greater' than header name.
+ */
+static int compare_handler( const handler_rec *r1,
+ const char *name,
+ pj_size_t name_len,
+ pj_uint32_t hash )
+{
+ /* Compare length. */
+ if (r1->hname_len < name_len)
+ return -1;
+ if (r1->hname_len > name_len)
+ return 1;
+
+ /* Length is equal, compare hashed value. */
+ if (r1->hname_hash < hash)
+ return -1;
+ if (r1->hname_hash > hash)
+ return 1;
+
+ /* Equal length and equal hash. compare the strings. */
+ return strcmp(r1->hname, name);
+}
+
+/* Register one handler for one header name. */
+static int int_register_parser( const char *name, pjsip_parse_hdr_func *fptr )
+{
+ unsigned pos;
+ handler_rec rec;
+ unsigned i;
+
+ if (handler_count >= PJ_ARRAY_SIZE(handler)) {
+ return -1;
+ }
+
+ /* Initialize temporary handler. */
+ rec.handler = fptr;
+ rec.hname_len = strlen(name);
+ if (rec.hname_len >= sizeof(rec.hname)) {
+ return -1;
+ }
+ /* Name is copied in lowercase. */
+ for (i=0; i<rec.hname_len; ++i) {
+ rec.hname[i] = (char)tolower(name[i]);
+ }
+ rec.hname[i] = '\0';
+ /* Hash value is calculated from the lowercase name. */
+ rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING);
+
+ /* Get the pos to insert the new handler. */
+ for (pos=0; pos < handler_count; ++pos) {
+ int d;
+ d = compare_handler(&handler[pos], rec.hname, rec.hname_len, rec.hname_hash);
+ if (d == 0) {
+ pj_assert(0);
+ return -1;
+ }
+ if (d > 0) {
+ break;
+ }
+ }
+
+ /* Shift handlers. */
+ if (pos != handler_count) {
+ pj_memmove( &handler[pos+1], &handler[pos], (handler_count-pos)*sizeof(handler_rec));
+ }
+ /* Add new handler. */
+ pj_memcpy( &handler[pos], &rec, sizeof(handler_rec));
+ ++handler_count;
+
+ return 0;
+}
+
+/* Register parser handler. If both header name and short name are valid,
+ * then two instances of handler will be registered.
+ */
+PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname,
+ const char *hshortname,
+ pjsip_parse_hdr_func *fptr)
+{
+ if (int_register_parser(hname, fptr)) {
+ return -1;
+ }
+ if (hshortname && int_register_parser(hshortname, fptr)) {
+ return -1;
+ }
+ return 0;
+}
+
+/* Find handler to parse the header name. */
+static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname)
+{
+ handler_rec *first;
+ char hname_copy[PJSIP_MAX_HNAME_LEN];
+ pj_uint32_t hash;
+ int comp;
+ unsigned i, n;
+
+ /* Calculate hash value while converting the header to lowercase.
+ * Don't assume that 'hname' is NULL terminated.
+ */
+ hash = 0;
+ for (i=0; i<(unsigned)hname->slen; ++i) {
+ hname_copy[i] = (char)tolower(hname->ptr[i]);
+ hash = hash * PJ_HASH_MULTIPLIER + hname_copy[i];
+ }
+ hname_copy[i] = '\0';
+
+ /* Binary search for the handler. */
+ comp = -1;
+ first = &handler[0];
+ n = handler_count;
+ for (; n > 0; ) {
+ unsigned half = n / 2;
+ handler_rec *mid = first + half;
+
+ comp = compare_handler(mid, hname_copy, hname->slen, hash);
+ if (comp < 0) {
+ first = ++mid;
+ n -= half + 1;
+ } else if (comp==0) {
+ first = mid;
+ break;
+ } else {
+ n = half;
+ }
+ }
+
+ return comp==0 ? first->handler : NULL;
+}
+
+/* Public function to parse SIP message. */
+PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size,
+ pjsip_parser_err_report *err_list)
+{
+ pjsip_msg *msg = NULL;
+ pj_scanner scanner;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+ PJ_TRY {
+ msg = int_parse_msg(&scanner, pool, err_list);
+ }
+ PJ_DEFAULT {
+ msg = NULL;
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ return msg;
+}
+
+/* Determine if a message has been received. */
+PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,
+ pj_bool_t is_datagram, pj_size_t *msg_size)
+{
+#if PJ_HAS_TCP
+ const char *hdr_end;
+ const char *body_start;
+ const char *pos;
+ const char *line;
+ int content_length = -1;
+
+ *msg_size = size;
+
+ /* For datagram, the whole datagram IS the message. */
+ if (is_datagram) {
+ return PJ_TRUE;
+ }
+
+
+ /* Find the end of header area by finding an empty line. */
+ if ((pos = strstr(buf, "\n\r\n")) == NULL) {
+ return PJ_FALSE;
+ }
+
+ hdr_end = pos+1;
+ body_start = pos+3;
+
+ /* Find "Content-Length" header the hard way. */
+ line = strchr(buf, '\n');
+ while (line && line < hdr_end-14) {
+ ++line;
+ if ( ((*line=='C' || *line=='c') && strncasecmp(line, "Content-Length", 14) == 0) ||
+ ((*line=='l' || *line=='L') && (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':')))
+ {
+ /* Try to parse the header. */
+ pj_scanner scanner;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, (char*)line, hdr_end-line,
+ PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+ PJ_TRY {
+ pj_str_t str_clen;
+
+ /* Get "Content-Length" or "L" name */
+ if (*line=='C' || *line=='c')
+ pj_scan_advance_n(&scanner, 14, PJ_TRUE);
+ else if (*line=='l' || *line=='L')
+ pj_scan_advance_n(&scanner, 1, PJ_TRUE);
+
+ /* Get colon */
+ if (pj_scan_get_char(&scanner) != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ /* Get number */
+ pj_scan_get(&scanner, pjsip_DIGIT_SPEC, &str_clen);
+
+ /* Get newline. */
+ pj_scan_get_newline(&scanner);
+
+ /* Found a valid Content-Length header. */
+ content_length = pj_strtoul(&str_clen);
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ }
+
+ /* Found valid Content-Length? */
+ if (content_length != -1)
+ break;
+
+ /* Go to next line. */
+ line = strchr(line, '\n');
+ }
+
+ /* Found Content-Length? */
+ if (content_length == -1) {
+ PJ_LOG(4, ("sipparser", "pjsip_find_msg: incoming TCP packet has missing "
+ "Content-Length header, treated as incomplete."));
+ return PJ_FALSE;
+ }
+
+ /* Enough packet received? */
+ *msg_size = (body_start - buf) + content_length;
+ return (*msg_size) <= size;
+#else
+ PJ_UNUSED_ARG(buf)
+ PJ_UNUSED_ARG(is_datagram)
+ *msg_size = size;
+ return 1;
+#endif
+}
+
+/* Public function to parse URI */
+PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
+ char *buf, pj_size_t size,
+ unsigned option)
+{
+ PJ_USE_EXCEPTION;
+ pj_scanner scanner;
+ pjsip_uri *uri = NULL;
+
+ if (!parser_is_initialized) {
+ init_parser();
+ parser_is_initialized = 1;
+ }
+
+ pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
+
+
+ PJ_TRY {
+ uri = int_parse_uri_or_name_addr(&scanner, pool, option);
+ }
+ PJ_END;
+
+ /* Must have exhausted all inputs. */
+ if (pj_scan_is_eof(&scanner) || *scanner.current=='\r' || *scanner.current=='\n') {
+ /* Success. */
+ pj_scan_fini(&scanner);
+ return uri;
+ }
+
+ /* Still have some characters unparsed. */
+ pj_scan_fini(&scanner);
+ return NULL;
+}
+
+/* Generic function to print message body.
+ * This assumes that the 'data' member points to a contigous memory where the
+ * actual body is laid.
+ */
+static int generic_print_body (pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+ pjsip_msg_body *body = msg_body;
+ if (size < body->len)
+ return 0;
+
+ pj_memcpy (buf, body->data, body->len);
+ return body->len;
+}
+
+/* Internal function to parse SIP message */
+static pjsip_msg *int_parse_msg( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_parser_err_report *err_list)
+{
+ PJ_USE_EXCEPTION;
+ int ch;
+ pjsip_msg *msg;
+ pjsip_ctype_hdr *ctype_hdr = NULL;
+
+ /* Skip leading newlines. */
+ ch = *scanner->current;
+ while (ch=='\r' || ch=='\n') {
+ pj_scan_get_char(scanner);
+ ch = *scanner->current;
+ }
+
+ msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+ /* Parse request or status line */
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) {
+ msg->type = PJSIP_RESPONSE_MSG;
+ int_parse_status_line( scanner, &msg->line.status );
+ } else {
+ msg->type = PJSIP_REQUEST_MSG;
+ int_parse_req_line(scanner, pool, &msg->line.req );
+ }
+
+ /* Parse headers. */
+ do {
+ pj_str_t hname;
+ pjsip_parse_hdr_func * handler;
+ pjsip_hdr *hdr = NULL;
+
+ /* Get hname. */
+ pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hname);
+ ch = pj_scan_get_char( scanner );
+ if (ch != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ /* Find handler. */
+ handler = find_handler(&hname);
+
+ PJ_TRY {
+ /* Call the handler if found.
+ * If no handler is found, then treat the header as generic
+ * hname/hvalue pair.
+ */
+ if (handler) {
+ hdr = (*handler)(scanner, pool);
+ } else {
+ pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(scanner, pool);
+ ghdr->type = PJSIP_H_OTHER;
+ ghdr->name = ghdr->sname = hname;
+ hdr = (pjsip_hdr*)ghdr;
+ }
+
+ /* Check if we've just parsed a Content-Type header.
+ * We will check for a message body if we've got Content-Type header.
+ */
+ if (hdr->type == PJSIP_H_CONTENT_TYPE) {
+ ctype_hdr = (pjsip_ctype_hdr*)hdr;
+ }
+
+ }
+ PJ_DEFAULT {
+ /* Exception was thrown during parsing.
+ * Skip until newline, and parse next header.
+ */
+ pj_str_t token;
+ hdr = NULL;
+
+ PJ_LOG(4,("sipparser", "Syntax error in line %d col %d (hname=%.*s)",
+ scanner->line, scanner->col, hname.slen, hname.ptr));
+
+ if (err_list) {
+ pjsip_parser_err_report *err_info;
+
+ err_info = pj_pool_alloc(pool, sizeof(*err_info));
+ err_info->exception_code = PJ_GET_EXCEPTION();
+ err_info->line = scanner->line;
+ err_info->col = scanner->col;
+ err_info->hname = hname;
+
+ pj_list_insert_before(err_list, err_info);
+ }
+
+ if (!pj_scan_is_eof(scanner)) {
+ pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &token);
+ parse_hdr_end(scanner);
+ }
+ }
+ PJ_END;
+
+ if (hdr) {
+ /* Single parse of header line can produce multiple headers.
+ * For example, if one Contact: header contains Contact list
+ * separated by comma, then these Contacts will be split into
+ * different Contact headers.
+ * So here we must insert list instead of just insert one header.
+ */
+ pj_list_insert_nodes_before(&msg->hdr, hdr);
+ }
+
+ /* Parse until EOF or an empty line is found. */
+ } while (!pj_scan_is_eof(scanner) &&
+ *scanner->current != '\r' && *scanner->current != '\n');
+
+ /* If empty line is found, eat it. */
+ if (!pj_scan_is_eof(scanner)) {
+ if (*scanner->current=='\r' || *scanner->current=='\n') {
+ pj_scan_get_newline(scanner);
+ }
+ }
+
+ /* If we have Content-Type header, treat the rest of the message as body. */
+ if (ctype_hdr) {
+ pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+ pj_strdup (pool, &body->content_type.type, &ctype_hdr->media.type);
+ pj_strdup (pool, &body->content_type.subtype, &ctype_hdr->media.subtype);
+ pj_strdup (pool, &body->content_type.param, &ctype_hdr->media.param);
+ body->data = scanner->current;
+ body->len = scanner->end - scanner->current;
+ body->print_body = &generic_print_body;
+
+ msg->body = body;
+ }
+
+ return msg;
+}
+
+/* Parse parameter (pname ["=" pvalue]). */
+void pjsip_parse_param_imp( pj_scanner *scanner,
+ pj_str_t *pname, pj_str_t *pvalue,
+ unsigned option)
+{
+ /* pname */
+ pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pname);
+
+ /* pvalue, if any */
+ if (*scanner->current == '=') {
+ pj_scan_get_char(scanner);
+ /* pvalue can be a quoted string. */
+ if (*scanner->current == '"') {
+ pj_scan_get_quote( scanner, '"', '"', pvalue);
+ if (option & PJSIP_PARSE_REMOVE_QUOTE) {
+ pvalue->ptr++;
+ pvalue->slen -= 2;
+ }
+ } else {
+ pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pvalue);
+ }
+ } else {
+ pvalue->ptr = NULL;
+ pvalue->slen = 0;
+ }
+}
+
+/* Parse parameter (";" pname ["=" pvalue]). */
+static void int_parse_param( pj_scanner *scanner,
+ pj_str_t *pname, pj_str_t *pvalue)
+{
+ /* Get ';' character */
+ pj_scan_get_char(scanner);
+
+ /* Get pname and optionally pvalue */
+ pjsip_parse_param_imp(scanner, pname, pvalue, 0);
+}
+
+/* Parse host:port in URI. */
+static void int_parse_uri_host_port( pj_scanner *scanner,
+ pj_str_t *host, int *p_port)
+{
+ pj_scan_get( scanner, pjsip_HOST_SPEC, host);
+ if (*scanner->current == ':') {
+ pj_str_t port;
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, pjsip_DIGIT_SPEC, &port);
+ *p_port = pj_strtoul(&port);
+ } else {
+ *p_port = 0;
+ }
+}
+
+/* Determine if the next token in an URI is a user specification. */
+static int int_is_next_user(pj_scanner *scanner)
+{
+ pj_str_t dummy;
+ int is_user;
+
+ /* Find character '@'. If this character exist, then the token
+ * must be a username.
+ */
+ if (pj_scan_peek( scanner, pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@')
+ is_user = 1;
+ else
+ is_user = 0;
+
+ return is_user;
+}
+
+/* Parse user:pass tokens in an URI. */
+static void int_parse_user_pass( pj_scanner *scanner,
+ pj_str_t *user, pj_str_t *pass)
+{
+ pj_scan_get( scanner, pjsip_USER_SPEC, user);
+ if ( *scanner->current == ':') {
+ pj_scan_get_char( scanner );
+ pj_scan_get( scanner, pjsip_PASSWD_SPEC, pass);
+ } else {
+ pass->ptr = NULL;
+ pass->slen = 0;
+ }
+
+ /* Get the '@' */
+ pj_scan_get_char( scanner );
+}
+
+/* Parse all types of URI. */
+static pjsip_uri *int_parse_uri_or_name_addr(pj_scanner *scanner, pj_pool_t *pool,
+ unsigned option)
+{
+ pjsip_uri *uri;
+ int is_name_addr = 0;
+
+ if (*scanner->current=='"' || *scanner->current=='<') {
+ uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ is_name_addr = 1;
+ } else {
+ pj_scan_state backtrack;
+ pj_str_t scheme;
+ int colon;
+
+ pj_scan_save_state( scanner, &backtrack);
+ pj_scan_get( scanner, pjsip_TOKEN_SPEC, &scheme);
+ colon = pj_scan_get_char( scanner );
+ pj_scan_restore_state( scanner, &backtrack);
+
+ if (colon==':' && (parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0))
+ {
+ uri = (pjsip_uri*)int_parse_sip_url( scanner, pool,
+ (option & PJSIP_PARSE_URI_IN_FROM_TO_HDR) == 0 );
+
+ } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) {
+
+ /* Not supported. */
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({return NULL; /* Not reached. */});
+
+ } else {
+ uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ is_name_addr = 1;
+ }
+ }
+
+ /* Should we return the URI object as name address? */
+ if (option & PJSIP_PARSE_URI_AS_NAMEADDR) {
+ if (is_name_addr == 0) {
+ pjsip_name_addr *name_addr;
+
+ name_addr = pjsip_name_addr_create(pool);
+ name_addr->uri = uri;
+
+ uri = (pjsip_uri*)name_addr;
+ }
+ }
+
+ return uri;
+}
+
+/* Parse URI. */
+static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool,
+ pj_bool_t parse_params)
+{
+ if (*scanner->current=='"' || *scanner->current=='<') {
+ return (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ } else {
+ pj_str_t scheme;
+ int colon;
+
+ /* Get scheme. */
+ colon = pj_scan_peek(scanner, pjsip_TOKEN_SPEC, &scheme);
+ if (colon != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0))
+ {
+ return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params );
+
+ } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({ return NULL; /* Not reached. */ })
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({ return NULL; /* Not reached. */ })
+ }
+ }
+}
+
+/* Parse "sip:" and "sips:" URI. */
+static pjsip_url *int_parse_sip_url( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_bool_t parse_params)
+{
+ pj_str_t scheme;
+ pjsip_url *url;
+ int colon;
+ int skip_ws = scanner->skip_ws;
+ scanner->skip_ws = 0;
+
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &scheme);
+ colon = pj_scan_get_char(scanner);
+ if (colon != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ if (parser_stricmp(scheme, pjsip_SIP_STR)==0) {
+ url = pjsip_url_create(pool, 0);
+
+ } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) {
+ url = pjsip_url_create(pool, 1);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ /* should not reach here */
+ UNREACHED({
+ pj_assert(0);
+ return 0;
+ })
+ }
+
+ if (int_is_next_user(scanner)) {
+ int_parse_user_pass(scanner, &url->user, &url->passwd);
+ }
+
+ /* Get host:port */
+ int_parse_uri_host_port(scanner, &url->host, &url->port);
+
+ /* Get URL parameters. */
+ while ( parse_params && *scanner->current == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) {
+ url->user_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) {
+ url->method_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TRANSPORT_STR) && pvalue.slen) {
+ url->transport_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+ url->ttl_param = pj_strtoul(&pvalue);
+
+ } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+ url->maddr_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_LR_STR)) {
+ url->lr_param = 1;
+
+ } else {
+ concat_param(&url->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+ /* Get header params. */
+ if (parse_params && *scanner->current == '?') {
+ pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &url->header_param);
+ }
+
+ scanner->skip_ws = skip_ws;
+ pj_scan_skip_whitespace(scanner);
+ return url;
+}
+
+/* Parse nameaddr. */
+static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool )
+{
+ int has_bracket;
+ pjsip_name_addr *name_addr;
+
+ name_addr = pjsip_name_addr_create(pool);
+
+ if (*scanner->current == '"') {
+ pj_scan_get_quote( scanner, '"', '"', &name_addr->display);
+
+ } else if (*scanner->current != '<') {
+ int next;
+ pj_str_t dummy;
+
+ /* This can be either the start of display name,
+ * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char.
+ * We're only interested in display name, because SIP URL
+ * will be parser later.
+ */
+ next = pj_scan_peek_until(scanner, pjsip_DISPLAY_SCAN_SPEC, &dummy);
+ if (next == '<') {
+ /* Ok, this is what we're looking for, a display name. */
+ pj_scan_get_until_ch( scanner, '<', &name_addr->display);
+ pj_strtrim(&name_addr->display);
+ }
+ }
+
+ /* Manually skip whitespace. */
+ pj_scan_skip_whitespace(scanner);
+
+ /* Get the SIP-URL */
+ has_bracket = (*scanner->current == '<');
+ if (has_bracket)
+ pj_scan_get_char(scanner);
+ name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
+ if (has_bracket)
+ pj_scan_get_char(scanner);
+
+ return name_addr;
+}
+
+
+/* Parse SIP request line. */
+static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_request_line *req_line)
+{
+ pj_str_t token;
+
+ pj_scan_get( scanner, pjsip_TOKEN_SPEC, &token);
+ pjsip_method_init_np( &req_line->method, &token);
+
+ req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0)
+ PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
+ pj_scan_advance_n (scanner, 7, 1);
+ pj_scan_get_newline( scanner );
+}
+
+/* Parse status line. */
+static void int_parse_status_line( pj_scanner *scanner,
+ pjsip_status_line *status_line)
+{
+ pj_str_t token;
+
+ if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ pj_scan_advance_n( scanner, 7, 1);
+
+ pj_scan_get( scanner, pjsip_DIGIT_SPEC, &token);
+ status_line->code = pj_strtoul(&token);
+ pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &status_line->reason);
+ pj_scan_get_newline( scanner );
+}
+
+/* Parse ending of header. */
+static void parse_hdr_end( pj_scanner *scanner )
+{
+ if (pj_scan_is_eof(scanner)) {
+ ; /* Do nothing. */
+ } else if (*scanner->current == '&') {
+ pj_scan_get_char(scanner);
+ } else {
+ pj_scan_get_newline(scanner);
+ }
+}
+
+/* Parse ending of header. */
+void pjsip_parse_end_hdr_imp( pj_scanner *scanner )
+{
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic array header. */
+static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr,
+ pj_scanner *scanner)
+{
+ pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[0]);
+ hdr->count++;
+
+ while (*scanner->current == ',') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[hdr->count]);
+ hdr->count++;
+ }
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic string header. */
+static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
+ pj_scanner *scanner )
+{
+ pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue);
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic integer header. */
+static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr,
+ pj_scanner *scanner )
+{
+ pj_str_t tmp;
+ pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &tmp);
+ hdr->ivalue = pj_strtoul(&tmp);
+ parse_hdr_end(scanner);
+}
+
+
+/* Parse Accept header. */
+static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_accept_hdr *accept = pjsip_accept_hdr_create(pool);
+ parse_generic_array_hdr(accept, scanner);
+ return accept;
+}
+
+/* Parse Allow header. */
+static pjsip_allow_hdr* parse_hdr_allow( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_allow_hdr *allow = pjsip_allow_hdr_create(pool);
+ parse_generic_array_hdr(allow, scanner);
+ return allow;
+}
+
+/* Parse Call-ID header. */
+static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(pool);
+ pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id);
+ parse_hdr_end(scanner);
+ return hdr;
+}
+
+/* Parse and interpret Contact param. */
+static void int_parse_contact_param( pjsip_contact_hdr *hdr,
+ pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ while ( *scanner->current == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+ if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) {
+ char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen);
+ if (!dot_pos) {
+ hdr->q1000 = pj_strtoul(&pvalue);
+ } else {
+ pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
+ pvalue.ptr = dot_pos + 1;
+ hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3);
+ }
+ } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) {
+ hdr->expires = pj_strtoul(&pvalue);
+
+ } else {
+ concat_param(&hdr->other_param, pool, &pname, &pvalue);
+ }
+ }
+}
+
+/* Parse Contact header. */
+PJ_DEF(pjsip_contact_hdr*) parse_hdr_contact( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_contact_hdr *first = NULL;
+
+ do {
+ pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
+ if (first == NULL)
+ first = hdr;
+ else
+ pj_list_insert_before(first, hdr);
+
+ if (*scanner->current == '*') {
+ pj_scan_get_char(scanner);
+ hdr->star = 1;
+
+ } else {
+ hdr->star = 0;
+ hdr->uri = int_parse_uri_or_name_addr(scanner, pool, PJSIP_PARSE_URI_AS_NAMEADDR);
+
+ int_parse_contact_param(hdr, scanner, pool);
+ }
+
+ if (*scanner->current != ',')
+ break;
+
+ pj_scan_get_char(scanner);
+
+ } while (1);
+
+ parse_hdr_end(scanner);
+ return first;
+}
+
+/* Parse Content-Length header. */
+PJ_DEF(pjsip_clen_hdr*) parse_hdr_content_length( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pj_str_t digit;
+ pjsip_clen_hdr *hdr;
+
+ hdr = pjsip_clen_hdr_create(pool);
+ pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit);
+ hdr->len = pj_strtoul(&digit);
+ parse_hdr_end(scanner);
+ return hdr;
+}
+
+/* Parse Content-Type header. */
+PJ_DEF(pjsip_ctype_hdr*) parse_hdr_content_type( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_ctype_hdr *hdr;
+
+ hdr = pjsip_ctype_hdr_create(pool);
+
+ /* Parse media type and subtype. */
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.type);
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.subtype);
+
+ /* Parse media parameters */
+ while (*scanner->current == ';') {
+ pj_str_t pname, pvalue;
+ int_parse_param(scanner, &pname, &pvalue);
+ concat_param(&hdr->media.param, pool, &pname, &pvalue);
+ }
+
+ parse_hdr_end(scanner);
+ return hdr;
+}
+
+/* Parse CSeq header. */
+PJ_DEF(pjsip_cseq_hdr*) parse_hdr_cseq( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pj_str_t cseq, method;
+ pjsip_cseq_hdr *hdr;
+
+ hdr = pjsip_cseq_hdr_create(pool);
+ pj_scan_get( scanner, pjsip_DIGIT_SPEC, &cseq);
+ hdr->cseq = pj_strtoul(&cseq);
+
+ pj_scan_get( scanner, pjsip_TOKEN_SPEC, &method);
+ pjsip_method_init_np(&hdr->method, &method);
+
+ parse_hdr_end( scanner );
+ return hdr;
+}
+
+/* Parse Expires header. */
+static pjsip_expires_hdr* parse_hdr_expires(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(pool);
+ parse_generic_int_hdr(hdr, scanner);
+ return hdr;
+}
+
+/* Parse From: or To: header. */
+static void parse_hdr_fromto( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_from_hdr *hdr)
+{
+ hdr->uri = int_parse_uri_or_name_addr(scanner, pool,
+ PJSIP_PARSE_URI_AS_NAMEADDR |
+ PJSIP_PARSE_URI_IN_FROM_TO_HDR);
+
+ while ( *scanner->current == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_TAG_STR)) {
+ hdr->tag = pvalue;
+
+ } else {
+ concat_param(&hdr->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+ parse_hdr_end(scanner);
+}
+
+/* Parse From: header. */
+PJ_DEF(pjsip_from_hdr*) parse_hdr_from( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_from_hdr *hdr = pjsip_from_hdr_create(pool);
+ parse_hdr_fromto(scanner, pool, hdr);
+ return hdr;
+}
+
+/* Parse Require: header. */
+static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_require_hdr *hdr = pjsip_require_hdr_create(pool);
+ parse_generic_array_hdr(hdr, scanner);
+ return hdr;
+}
+
+/* Parse Retry-After: header. */
+static pjsip_retry_after_hdr* parse_hdr_retry_after(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_retry_after_hdr *hdr;
+ hdr = pjsip_retry_after_hdr_create(pool);
+ parse_generic_int_hdr(hdr, scanner);
+ return hdr;
+}
+
+/* Parse Supported: header. */
+static pjsip_supported_hdr* parse_hdr_supported(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(pool);
+ parse_generic_array_hdr(hdr, scanner);
+ return hdr;
+}
+
+
+/* Parse To: header. */
+PJ_DEF(pjsip_to_hdr*) parse_hdr_to( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_to_hdr *hdr = pjsip_to_hdr_create(pool);
+ parse_hdr_fromto(scanner, pool, hdr);
+ return hdr;
+}
+
+/* Parse Unsupported: header. */
+static pjsip_unsupported_hdr* parse_hdr_unsupported(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(pool);
+ parse_generic_array_hdr(hdr, scanner);
+ return hdr;
+}
+
+/* Parse and interpret Via parameters. */
+static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ while ( *scanner->current == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) {
+ hdr->branch_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+ hdr->ttl_param = pj_strtoul(&pvalue);
+
+ } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+ hdr->maddr_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) {
+ hdr->recvd_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) {
+ if (pvalue.slen)
+ hdr->rport_param = pj_strtoul(&pvalue);
+ else
+ hdr->rport_param = 0;
+ } else {
+ concat_param( &hdr->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+}
+
+/* Parse Max-Forwards header. */
+static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_max_forwards_hdr *hdr;
+ hdr = pjsip_max_forwards_hdr_create(pool);
+ parse_generic_int_hdr(hdr, scanner);
+ return hdr;
+}
+
+/* Parse Min-Expires header. */
+static pjsip_min_expires_hdr* parse_hdr_min_expires(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_min_expires_hdr *hdr;
+ hdr = pjsip_min_expires_hdr_create(pool);
+ parse_generic_int_hdr(hdr, scanner);
+ return hdr;
+}
+
+
+/* Parse Route: or Record-Route: header. */
+static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_routing_hdr *hdr )
+{
+ pjsip_name_addr *temp=int_parse_name_addr(scanner, pool);
+
+ pj_memcpy(&hdr->name_addr, temp, sizeof(*temp));
+ if (*scanner->current == ';') {
+ pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->other_param);
+ }
+}
+
+/* Parse Record-Route header. */
+PJ_DEF(pjsip_rr_hdr*) parse_hdr_rr( pj_scanner *scanner, pj_pool_t *pool)
+{
+ pjsip_rr_hdr *first = NULL;
+
+ do {
+ pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(pool);
+ if (!first) {
+ first = hdr;
+ } else {
+ pj_list_insert_before(first, hdr);
+ }
+ parse_hdr_rr_route(scanner, pool, hdr);
+ if (*scanner->current == ',') {
+ pj_scan_get_char(scanner);
+ } else {
+ break;
+ }
+ } while (1);
+ parse_hdr_end(scanner);
+ return first;
+}
+
+/* Parse Route: header. */
+PJ_DEF(pjsip_route_hdr*) parse_hdr_route( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_route_hdr *first = NULL;
+
+ do {
+ pjsip_route_hdr *hdr = pjsip_route_hdr_create(pool);
+ if (!first) {
+ first = hdr;
+ } else {
+ pj_list_insert_before(first, hdr);
+ }
+ parse_hdr_rr_route(scanner, pool, hdr);
+ if (*scanner->current == ',') {
+ pj_scan_get_char(scanner);
+ } else {
+ break;
+ }
+ } while (1);
+ parse_hdr_end(scanner);
+ return first;
+}
+
+/* Parse Via: header. */
+PJ_DEF(pjsip_via_hdr*) parse_hdr_via( pj_scanner *scanner, pj_pool_t *pool)
+{
+ pjsip_via_hdr *first = NULL;
+
+ do {
+ pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
+ if (!first)
+ first = hdr;
+ else
+ pj_list_insert_before(first, hdr);
+
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+
+ pj_scan_advance_n( scanner, 8, 1);
+
+ pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hdr->transport);
+ pj_scan_get( scanner, pjsip_HOST_SPEC, &hdr->sent_by.host);
+
+ if (*scanner->current==':') {
+ pj_str_t digit;
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit);
+ hdr->sent_by.port = pj_strtoul(&digit);
+ } else {
+ hdr->sent_by.port = 5060;
+ }
+
+ int_parse_via_param(hdr, scanner, pool);
+
+ if (*scanner->current == '(') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch( scanner, ')', &hdr->comment);
+ pj_scan_get_char( scanner );
+ }
+
+ if (*scanner->current != ',')
+ break;
+
+ pj_scan_get_char(scanner);
+
+ } while (1);
+
+ parse_hdr_end(scanner);
+ return first;
+}
+
+/* Parse generic header. */
+PJ_DEF(pjsip_generic_string_hdr*) parse_hdr_generic_string( pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_generic_string_hdr *hdr;
+
+ hdr = pjsip_generic_string_hdr_create(pool, NULL);
+ parse_generic_string_hdr(hdr, scanner);
+ return hdr;
+
+}
+
+/* Public function to parse a header value. */
+PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
+ char *buf, pj_size_t size, int *parsed_len )
+{
+ pj_scanner scanner;
+ pjsip_hdr *hdr = NULL;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+ PJ_TRY {
+ pjsip_parse_hdr_func *handler = find_handler(hname);
+ if (handler) {
+ hdr = (*handler)(&scanner, pool);
+ } else {
+ pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(&scanner, pool);
+ ghdr->type = PJSIP_H_OTHER;
+ pj_strdup(pool, &ghdr->name, hname);
+ ghdr->sname = ghdr->name;
+ hdr = (pjsip_hdr*)ghdr;
+ }
+
+ }
+ PJ_DEFAULT {
+ hdr = NULL;
+ }
+ PJ_END
+
+ if (parsed_len) {
+ *parsed_len = (scanner.current - scanner.begin);
+ }
+
+ pj_scan_fini(&scanner);
+
+ return hdr;
+}
+