1 /**
2  * @file sip/location.c Mock SIP server -- location handling
3  *
4  * Copyright (C) 2010 - 2016 Creytiv.com
5  */
6 #include <time.h>
7 #include <re.h>
8 #include "sipsrv.h"
9 
10 
11 struct loctmp {
12 	struct sa src;
13 	struct uri duri;
14 	char *uri;
15 	char *callid;
16 	uint32_t expires;
17 	uint32_t cseq;
18 	double q;
19 };
20 
21 
destructor_loctmp(void * arg)22 static void destructor_loctmp(void *arg)
23 {
24 	struct loctmp *tmp = arg;
25 
26 	mem_deref(tmp->uri);
27 	mem_deref(tmp->callid);
28 }
29 
30 
destructor_location(void * arg)31 static void destructor_location(void *arg)
32 {
33 	struct location *loc = arg;
34 
35 	list_unlink(&loc->le);
36 	mem_deref(loc->uri);
37 	mem_deref(loc->callid);
38 	mem_deref(loc->tmp);
39 }
40 
41 
cmp_handler(struct le * le,void * arg)42 static bool cmp_handler(struct le *le, void *arg)
43 {
44 	struct location *loc = le->data;
45 
46 	return uri_cmp(&loc->duri, arg);
47 }
48 
49 
location_update(struct list * locl,const struct sip_msg * msg,const struct sip_addr * contact,uint32_t expires)50 int location_update(struct list *locl, const struct sip_msg *msg,
51 		    const struct sip_addr *contact, uint32_t expires)
52 {
53 	struct location *loc, *loc_new = NULL;
54 	struct loctmp *tmp;
55 	struct pl pl;
56 	int err;
57 
58 	if (!locl || !msg || !contact)
59 		return EINVAL;
60 
61 	loc = list_ledata(list_apply(locl, true, cmp_handler,
62 				     (void *)&contact->uri));
63 	if (!loc) {
64 		if (expires == 0)
65 			return 0;
66 
67 		loc = loc_new = mem_zalloc(sizeof(*loc), destructor_location);
68 		if (!loc)
69 			return ENOMEM;
70 
71 		list_append(locl, &loc->le, loc);
72 	}
73 	else {
74 		if (!pl_strcmp(&msg->callid, loc->callid) &&
75 		    msg->cseq.num <= loc->cseq)
76 			return EPROTO;
77 
78 		if (expires == 0) {
79 			loc->rm = true;
80 			return 0;
81 		}
82 	}
83 
84 	tmp = mem_zalloc(sizeof(*tmp), destructor_loctmp);
85 	if (!tmp) {
86 		err = ENOMEM;
87 		goto out;
88 	}
89 
90 	err = pl_strdup(&tmp->uri, &contact->auri);
91 	if (err)
92 		goto out;
93 
94 	pl_set_str(&pl, tmp->uri);
95 
96 	if (uri_decode(&tmp->duri, &pl)) {
97 		err = EBADMSG;
98 		goto out;
99 	}
100 
101 	err = pl_strdup(&tmp->callid, &msg->callid);
102 	if (err)
103 		goto out;
104 
105 
106 	if (!msg_param_decode(&contact->params, "q", &pl))
107 		tmp->q = pl_float(&pl);
108 	else
109 		tmp->q = 1;
110 
111 	tmp->cseq    = msg->cseq.num;
112 	tmp->expires = expires;
113 	tmp->src     = msg->src;
114 
115  out:
116 	if (err) {
117 		mem_deref(loc_new);
118 		mem_deref(tmp);
119 	}
120 	else {
121 		mem_deref(loc->tmp);
122 		loc->tmp = tmp;
123 	}
124 
125 	return err;
126 }
127 
128 
location_commit(struct list * locl)129 void location_commit(struct list *locl)
130 {
131 	time_t now = time(NULL);
132 	struct le *le;
133 
134 	if (!locl)
135 		return;
136 
137 	for (le=locl->head; le; ) {
138 
139 		struct location *loc = le->data;
140 
141 		le = le->next;
142 
143 		if (loc->rm) {
144 			list_unlink(&loc->le);
145 			mem_deref(loc);
146 		}
147 		else if (loc->tmp) {
148 
149 			mem_deref(loc->uri);
150 			mem_deref(loc->callid);
151 
152 			loc->uri       = mem_ref(loc->tmp->uri);
153 			loc->callid    = mem_ref(loc->tmp->callid);
154 			loc->duri      = loc->tmp->duri;
155 			loc->cseq      = loc->tmp->cseq;
156 			loc->expires   = loc->tmp->expires + now;
157 			loc->src       = loc->tmp->src;
158 			loc->q         = loc->tmp->q;
159 
160 			loc->tmp = mem_deref(loc->tmp);
161 		}
162 	}
163 }
164 
165 
location_rollback(struct list * locl)166 void location_rollback(struct list *locl)
167 {
168 	struct le *le;
169 
170 	if (!locl)
171 		return;
172 
173 	for (le=locl->head; le; ) {
174 
175 		struct location *loc = le->data;
176 
177 		le = le->next;
178 
179 		if (!loc->uri) {
180 			list_unlink(&loc->le);
181 			mem_deref(loc);
182 		}
183 		else {
184 			loc->tmp = mem_deref(loc->tmp);
185 			loc->rm  = false;
186 		}
187 	}
188 }
189