src/core/parser/contact/contact.c
0489d6a2
 /*
  * Parses one Contact in Contact HF body
7dd0b342
  *
c32feee5
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * This file is part of ser, a free SIP server.
  *
  * ser is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser 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 General Public License for more details.
  *
4447dc86
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
37b37a99
  *
0489d6a2
  */
 
37b37a99
 #include <string.h>        /* memset */
0489d6a2
 #include "../../mem/mem.h" /* pkg_malloc, pkg_free */
 #include "../../dprint.h"
 #include "../../trim.h"    /* trim_leading, trim_trailing */
37b37a99
 #include "contact.h"
0489d6a2
 
 
 #define ST1 1 /* Basic state */
 #define ST2 2 /* Quoted */
 #define ST3 3 /* Angle quoted */
 #define ST4 4 /* Angle quoted and quoted */
 #define ST5 5 /* Escape in quoted */
 #define ST6 6 /* Escape in angle quoted and quoted */
 
 
 /*
  * Skip URI, stops when , (next contact)
  * or ; (parameter) is found
  */
 static inline int skip_uri(str* _s)
 {
 	register int st = ST1;
 
 	while(_s->len) {
 		switch(*(_s->s)) {
4447dc86
 			case ',':
 			case ';':
 				if (st == ST1) return 0;
 				break;
 
 			case '\"':
 				switch(st) {
 					case ST1: st = ST2; break;
 					case ST2: st = ST1; break;
 					case ST3: st = ST4; break;
 					case ST4: st = ST3; break;
 					case ST5: st = ST2; break;
 					case ST6: st = ST4; break;
 				}
 				break;
 
 			case '<':
 				switch(st) {
 					case ST1: st = ST3; break;
 					case ST3:
 							  LM_ERR("second bracket < found\n");
 							  return -1;
 					case ST5: st = ST2; break;
 					case ST6: st = ST4; break;
 				}
 				break;
 
 			case '>':
 				switch(st) {
 					case ST1:
 						LM_ERR("bracket > is first\n");
 						return -2;
0489d6a2
 
4447dc86
 					case ST3: st = ST1; break;
 					case ST5: st = ST2; break;
 					case ST6: st = ST4; break;
 				}
 				break;
 
 			case '\\':
 				switch(st) {
 					case ST2: st = ST5; break;
 					case ST4: st = ST6; break;
 					case ST5: st = ST2; break;
 					case ST6: st = ST4; break;
 				}
 				break;
 
 			default: break;
0489d6a2
 
 		}
 
 		_s->s++;
 		_s->len--;
 	}
 
 	if (st != ST1) {
4447dc86
 		LM_ERR("bracket < or \" not closed\n");
0489d6a2
 		return -3;
 	}
 
 	return 0;
 }
 
 
b8ac3cda
 /*
  * Skip name part
  *
  * _s will be adjusted to point at the beginning
  * of URI
  */
 static inline int skip_name(str* _s)
 {
 	char* last_wsp, *p;
 	int i, quoted = 0;
 
 	if (!_s) {
4447dc86
 		LM_ERR("invalid parameter value\n");
b8ac3cda
 		return -1;
 	}
 
 	p = _s->s;
 
 	last_wsp = 0;
 
 	for(i = 0; i < _s->len; i++) {
 		if (!quoted) {
 			if ((*p == ' ') || (*p == '\t')) {
 				last_wsp = p;
 			} else {
 				if (*p == '<') {
 					_s->s = p;
 					_s->len -= i;
 					return 0;
 				}
4447dc86
 
a0d7837c
 				if (*p == ':' || *p == ';') {
b8ac3cda
 					if (last_wsp) {
 						_s->len -= last_wsp - _s->s + 1;
a0d7837c
 						_s->s = last_wsp;
b8ac3cda
 					}
 					return 0;
 				}
 
 				if (*p == '\"') {
 					quoted = 1;
 				}
 			}
 		} else {
 			if ((*p == '\"') && (*(p-1) != '\\')) quoted = 0;
 		}
 		p++;
 	}
 
 	if (quoted) {
4447dc86
 		LM_ERR("closing quote missing in name part of Contact\n");
b8ac3cda
 	} else {
4447dc86
 		LM_ERR("error in contact, scheme separator not found\n");
b8ac3cda
 	}
 
 	return -1;
 }
 
0489d6a2
 
 /*
  * Parse contacts in a Contact HF
  */
 int parse_contacts(str* _s, contact_t** _c)
 {
 	contact_t* c;
37b37a99
 	param_hooks_t hooks;
69f9b0a4
 	str sv;
 
 	sv = *_s;
0489d6a2
 
 	while(1) {
4447dc86
 		/* Allocate and clear contact structure */
0489d6a2
 		c = (contact_t*)pkg_malloc(sizeof(contact_t));
 		if (c == 0) {
e3ecad34
 			PKG_MEM_ERROR;
0489d6a2
 			goto error;
 		}
 		memset(c, 0, sizeof(contact_t));
4447dc86
 
b8ac3cda
 		c->name.s = _s->s;
 
 		if (skip_name(_s) < 0) {
4447dc86
 			LM_ERR("error while skipping name part\n");
b8ac3cda
 			goto error;
 		}
 
0489d6a2
 		c->uri.s = _s->s;
b8ac3cda
 		c->name.len = _s->s - c->name.s;
 		trim_trailing(&c->name);
4447dc86
 
 		/* Find the end of the URI */
0489d6a2
 		if (skip_uri(_s) < 0) {
4447dc86
 			LM_ERR("error while skipping URI\n");
0489d6a2
 			goto error;
 		}
4447dc86
 
0489d6a2
 		c->uri.len = _s->s - c->uri.s; /* Calculate URI length */
 		trim_trailing(&(c->uri));      /* Remove any trailing spaces from URI */
 
4447dc86
 		/* Remove <> if any */
 		if ((c->uri.len >= 2) && (c->uri.s[0] == '<')
 				&& (c->uri.s[c->uri.len - 1] == '>')) {
b8ac3cda
 			c->uri.s++;
 			c->uri.len -= 2;
 		}
 
 		trim(&c->uri);
69f9b0a4
 		if((c->uri.len <= 0) || (c->uri.s + c->uri.len > sv.s + sv.len)) {
a0d7837c
 			LM_ERR("invalid contact uri\n");
69f9b0a4
 			goto error;
 		}
4447dc86
 
0489d6a2
 		if (_s->len == 0) goto ok;
4447dc86
 
0489d6a2
 		if (_s->s[0] == ';') {         /* Contact parameter found */
 			_s->s++;
 			_s->len--;
 			trim_leading(_s);
4447dc86
 
0489d6a2
 			if (_s->len == 0) {
4447dc86
 				LM_ERR("error while parsing params\n");
0489d6a2
 				goto error;
 			}
 
37b37a99
 			if (parse_params(_s, CLASS_CONTACT, &hooks, &c->params) < 0) {
4447dc86
 				LM_ERR("error while parsing parameters\n");
0489d6a2
 				goto error;
 			}
 
37b37a99
 			c->q = hooks.contact.q;
 			c->expires = hooks.contact.expires;
3294f904
 			c->received = hooks.contact.received;
524b4677
 			c->methods = hooks.contact.methods;
9cda3be9
 			c->instance = hooks.contact.instance;
506be363
 			c->reg_id = hooks.contact.reg_id;
5af1a114
 			c->flags = hooks.contact.flags;
37b37a99
 
0489d6a2
 			if (_s->len == 0) goto ok;
 		}
 
4447dc86
 		/* Next character is comma */
d6fcabe8
 		c->len = _s->s - c->name.s;
0489d6a2
 		_s->s++;
 		_s->len--;
 		trim_leading(_s);
 
9ed8820a
 		c->next = *_c;
 		*_c = c;
 		c = NULL;
 
0489d6a2
 		if (_s->len == 0) {
4447dc86
 			LM_ERR("text after comma missing\n");
0489d6a2
 			goto error;
 		}
 	}
 
4447dc86
 error:
69f9b0a4
 	LM_ERR("failure parsing '%.*s' (%d) [%p/%p/%d]\n", sv.len, sv.s, sv.len,
 			sv.s, _s->s, (int)(_s->s - sv.s));
0489d6a2
 	if (c) pkg_free(c);
 	free_contacts(_c); /* Free any contacts created so far */
 	return -1;
 
4447dc86
 ok:
d6fcabe8
 	c->len = _s->s - c->name.s;
0489d6a2
 	c->next = *_c;
 	*_c = c;
 	return 0;
 }
 
 
 /*
  * Free list of contacts
  * _c is head of the list
  */
 void free_contacts(contact_t** _c)
 {
 	contact_t* ptr;
 
 	while(*_c) {
 		ptr = *_c;
 		*_c = (*_c)->next;
 		if (ptr->params) {
37b37a99
 			free_params(ptr->params);
0489d6a2
 		}
 		pkg_free(ptr);
 	}
 }
 
 
 /*
  * Print list of contacts, just for debugging
  */
1c6135b6
 void print_contacts(FILE* _o, contact_t* _c)
0489d6a2
 {
 	contact_t* ptr;
 
 	ptr = _c;
 
 	while(ptr) {
1c6135b6
 		fprintf(_o, "---Contact---\n");
3294f904
 		fprintf(_o, "name    : '%.*s'\n", ptr->name.len, ptr->name.s);
 		fprintf(_o, "URI     : '%.*s'\n", ptr->uri.len, ptr->uri.s);
 		fprintf(_o, "q       : %p\n", ptr->q);
 		fprintf(_o, "expires : %p\n", ptr->expires);
 		fprintf(_o, "received: %p\n", ptr->received);
506be363
 		fprintf(_o, "methods : %p\n", ptr->methods);
9cda3be9
 		fprintf(_o, "instance: %p\n", ptr->instance);
506be363
 		fprintf(_o, "reg-id  : %p\n", ptr->reg_id);
5af1a114
 		fprintf(_o, "flags   : %p\n", ptr->flags);
d6fcabe8
 		fprintf(_o, "len     : %d\n", ptr->len);
0489d6a2
 		if (ptr->params) {
1c6135b6
 			print_params(_o, ptr->params);
0489d6a2
 		}
1c6135b6
 		fprintf(_o, "---/Contact---\n");
0489d6a2
 		ptr = ptr->next;
 	}
 }