1 /**
2  * @file notify.c  SIP Event Notify
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <re_types.h>
7 #include <re_mem.h>
8 #include <re_mbuf.h>
9 #include <re_sa.h>
10 #include <re_list.h>
11 #include <re_hash.h>
12 #include <re_fmt.h>
13 #include <re_uri.h>
14 #include <re_sys.h>
15 #include <re_tmr.h>
16 #include <re_msg.h>
17 #include <re_sip.h>
18 #include <re_sipevent.h>
19 #include "sipevent.h"
20 
21 
22 static int notify_request(struct sipnot *not, bool reset_ls);
23 
24 
internal_close_handler(int err,const struct sip_msg * msg,void * arg)25 static void internal_close_handler(int err, const struct sip_msg *msg,
26 				   void *arg)
27 {
28 	(void)err;
29 	(void)msg;
30 	(void)arg;
31 }
32 
33 
terminate(struct sipnot * not,enum sipevent_reason reason)34 static bool terminate(struct sipnot *not, enum sipevent_reason reason)
35 {
36 	not->terminated = true;
37 	not->reason     = reason;
38 	not->closeh     = internal_close_handler;
39 
40 	if (not->req) {
41 		mem_ref(not);
42 		return true;
43 	}
44 
45 	if (not->subscribed && !notify_request(not, true)) {
46 		mem_ref(not);
47 		return true;
48 	}
49 
50 	return false;
51 }
52 
53 
destructor(void * arg)54 static void destructor(void *arg)
55 {
56 	struct sipnot *not = arg;
57 
58 	tmr_cancel(&not->tmr);
59 
60 	if (!not->terminated) {
61 
62 		if (terminate(not, SIPEVENT_DEACTIVATED))
63 			return;
64 	}
65 
66 	hash_unlink(&not->he);
67 	mem_deref(not->req);
68 	mem_deref(not->dlg);
69 	mem_deref(not->auth);
70 	mem_deref(not->mb);
71 	mem_deref(not->event);
72 	mem_deref(not->id);
73 	mem_deref(not->cuser);
74 	mem_deref(not->hdrs);
75 	mem_deref(not->ctype);
76 	mem_deref(not->sock);
77 	mem_deref(not->sip);
78 }
79 
80 
sipnot_terminate(struct sipnot * not,int err,const struct sip_msg * msg,enum sipevent_reason reason)81 static void sipnot_terminate(struct sipnot *not, int err,
82 			     const struct sip_msg *msg,
83 			     enum sipevent_reason reason)
84 {
85 	sipnot_close_h *closeh;
86 	void *arg;
87 
88 	closeh = not->closeh;
89 	arg    = not->arg;
90 
91 	tmr_cancel(&not->tmr);
92 	(void)terminate(not, reason);
93 
94 	closeh(err, msg, arg);
95 }
96 
97 
tmr_handler(void * arg)98 static void tmr_handler(void *arg)
99 {
100 	struct sipnot *not = arg;
101 
102 	if (not->terminated)
103 		return;
104 
105 	sipnot_terminate(not, ETIMEDOUT, NULL, SIPEVENT_TIMEOUT);
106 }
107 
108 
sipnot_refresh(struct sipnot * not,uint32_t expires)109 void sipnot_refresh(struct sipnot *not, uint32_t expires)
110 {
111 	not->expires = min(expires, not->expires_max);
112 
113 	tmr_start(&not->tmr, not->expires * 1000, tmr_handler, not);
114 }
115 
116 
response_handler(int err,const struct sip_msg * msg,void * arg)117 static void response_handler(int err, const struct sip_msg *msg, void *arg)
118 {
119 	struct sipnot *not = arg;
120 
121 	if (err) {
122 		if (err == ETIMEDOUT)
123 			not->subscribed = false;
124 		goto out;
125 	}
126 
127 	if (sip_request_loops(&not->ls, msg->scode)) {
128 		not->subscribed = false;
129 		goto out;
130 	}
131 
132 	if (msg->scode < 200) {
133 		return;
134 	}
135 	else if (msg->scode < 300) {
136 
137 		(void)sip_dialog_update(not->dlg, msg);
138 	}
139 	else {
140 		switch (msg->scode) {
141 
142 		case 401:
143 		case 407:
144 			err = sip_auth_authenticate(not->auth, msg);
145 			if (err) {
146 				err = (err == EAUTH) ? 0 : err;
147 				break;
148 			}
149 
150 			err = notify_request(not, false);
151 			if (err)
152 				break;
153 
154 			return;
155 		}
156 
157 		not->subscribed = false;
158 	}
159 
160  out:
161 	if (not->termsent) {
162 		mem_deref(not);
163 	}
164 	else if (not->terminated) {
165 		if (!not->subscribed || notify_request(not, true))
166 			mem_deref(not);
167 	}
168 	else if (!not->subscribed) {
169 		sipnot_terminate(not, err, msg, -1);
170 	}
171 	else if (not->notify_pending) {
172 		(void)notify_request(not, true);
173 	}
174 }
175 
176 
send_handler(enum sip_transp tp,const struct sa * src,const struct sa * dst,struct mbuf * mb,void * arg)177 static int send_handler(enum sip_transp tp, const struct sa *src,
178 			const struct sa *dst, struct mbuf *mb, void *arg)
179 {
180 	struct sip_contact contact;
181 	struct sipnot *not = arg;
182 	(void)dst;
183 
184 	sip_contact_set(&contact, not->cuser, src, tp);
185 
186 	return mbuf_printf(mb, "%H", sip_contact_print, &contact);
187 }
188 
189 
print_event(struct re_printf * pf,const struct sipnot * not)190 static int print_event(struct re_printf *pf, const struct sipnot *not)
191 {
192 	if (not->id)
193 		return re_hprintf(pf, "%s;id=%s", not->event, not->id);
194 	else
195 		return re_hprintf(pf, "%s", not->event);
196 }
197 
198 
print_substate(struct re_printf * pf,const struct sipnot * not)199 static int print_substate(struct re_printf *pf, const struct sipnot *not)
200 {
201 	int err;
202 
203 	if (not->terminated) {
204 
205 		err = re_hprintf(pf, "terminated;reason=%s",
206 				 sipevent_reason_name(not->reason));
207 
208 		if (not->retry_after)
209 			err |= re_hprintf(pf, ";retry-after=%u",
210 					  not->retry_after);
211 	}
212 	else {
213 		uint32_t expires;
214 
215 		expires = (uint32_t)(tmr_get_expire(&not->tmr) / 1000);
216 
217 		err = re_hprintf(pf, "%s;expires=%u",
218 				 sipevent_substate_name(not->substate),
219 				 expires);
220 	}
221 
222 	return err;
223 }
224 
225 
print_content(struct re_printf * pf,const struct sipnot * not)226 static int print_content(struct re_printf *pf, const struct sipnot *not)
227 {
228 	if (!not->mb)
229 		return re_hprintf(pf,
230 				  "Content-Length: 0\r\n"
231 				  "\r\n");
232 	else
233 		return re_hprintf(pf,
234 				  "Content-Type: %s\r\n"
235 				  "Content-Length: %zu\r\n"
236 				  "\r\n"
237 				  "%b",
238 				  not->ctype,
239 				  mbuf_get_left(not->mb),
240 				  mbuf_buf(not->mb),
241 				  mbuf_get_left(not->mb));
242 }
243 
244 
notify_request(struct sipnot * not,bool reset_ls)245 static int notify_request(struct sipnot *not, bool reset_ls)
246 {
247 	if (reset_ls)
248 		sip_loopstate_reset(&not->ls);
249 
250 	if (not->terminated)
251 		not->termsent = true;
252 
253 	not->notify_pending = false;
254 
255 	return sip_drequestf(&not->req, not->sip, true, "NOTIFY",
256 			     not->dlg, 0, not->auth,
257 			     send_handler, response_handler, not,
258 			     "Event: %H\r\n"
259 			     "Subscription-State: %H\r\n"
260 			     "%s"
261 			     "%H",
262 			     print_event, not,
263 			     print_substate, not,
264 			     not->hdrs,
265 			     print_content, not);
266 }
267 
268 
sipnot_notify(struct sipnot * not)269 int sipnot_notify(struct sipnot *not)
270 {
271 	if (not->expires == 0) {
272 		return 0;
273 	}
274 
275 	if (not->req) {
276 		not->notify_pending = true;
277 		return 0;
278 	}
279 
280 	return notify_request(not, true);
281 }
282 
283 
sipnot_reply(struct sipnot * not,const struct sip_msg * msg,uint16_t scode,const char * reason)284 int sipnot_reply(struct sipnot *not, const struct sip_msg *msg,
285 		 uint16_t scode, const char *reason)
286 {
287 	struct sip_contact contact;
288 	uint32_t expires;
289 
290 	expires = (uint32_t)(tmr_get_expire(&not->tmr) / 1000);
291 
292 	sip_contact_set(&contact, not->cuser, &msg->dst, msg->tp);
293 
294 	return sip_treplyf(NULL, NULL, not->sip, msg, true, scode, reason,
295 			   "%H"
296 			   "Expires: %u\r\n"
297 			   "Content-Length: 0\r\n"
298 			   "\r\n",
299 			   sip_contact_print, &contact,
300 			   expires);
301 }
302 
303 
sipevent_accept(struct sipnot ** notp,struct sipevent_sock * sock,const struct sip_msg * msg,struct sip_dialog * dlg,const struct sipevent_event * event,uint16_t scode,const char * reason,uint32_t expires_min,uint32_t expires_dfl,uint32_t expires_max,const char * cuser,const char * ctype,sip_auth_h * authh,void * aarg,bool aref,sipnot_close_h * closeh,void * arg,const char * fmt,...)304 int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock,
305 		    const struct sip_msg *msg, struct sip_dialog *dlg,
306 		    const struct sipevent_event *event,
307 		    uint16_t scode, const char *reason, uint32_t expires_min,
308 		    uint32_t expires_dfl, uint32_t expires_max,
309 		    const char *cuser, const char *ctype,
310 		    sip_auth_h *authh, void *aarg, bool aref,
311 		    sipnot_close_h *closeh, void *arg, const char *fmt, ...)
312 {
313 	struct sipnot *not;
314 	uint32_t expires;
315 	int err;
316 
317 	if (!notp || !sock || !msg || !scode || !reason || !expires_dfl ||
318 	    !expires_max || !cuser || !ctype || expires_dfl < expires_min)
319 		return EINVAL;
320 
321 	not = mem_zalloc(sizeof(*not), destructor);
322 	if (!not)
323 		return ENOMEM;
324 
325 	if (!pl_strcmp(&msg->met, "REFER")) {
326 
327 		err = str_dup(&not->event, "refer");
328 		if (err)
329 			goto out;
330 
331 		err = re_sdprintf(&not->id, "%u", msg->cseq.num);
332 		if (err)
333 			goto out;
334 	}
335 	else {
336 		if (!event) {
337 			err = EINVAL;
338 			goto out;
339 		}
340 
341 		err = pl_strdup(&not->event, &event->event);
342 		if (err)
343 			goto out;
344 
345 		if (pl_isset(&event->id)) {
346 
347 			err = pl_strdup(&not->id, &event->id);
348 			if (err)
349 				goto out;
350 		}
351 	}
352 
353 	if (dlg) {
354 		not->dlg = mem_ref(dlg);
355 	}
356 	else {
357 		err = sip_dialog_accept(&not->dlg, msg);
358 		if (err)
359 			goto out;
360 	}
361 
362 	hash_append(sock->ht_not,
363 		    hash_joaat_str(sip_dialog_callid(not->dlg)),
364 		    &not->he, not);
365 
366 	err = sip_auth_alloc(&not->auth, authh, aarg, aref);
367 	if (err)
368 		goto out;
369 
370 	err = str_dup(&not->cuser, cuser);
371 	if (err)
372 		goto out;
373 
374 	err = str_dup(&not->ctype, ctype);
375 	if (err)
376 		goto out;
377 
378 	if (fmt) {
379 		va_list ap;
380 
381 		va_start(ap, fmt);
382 		err = re_vsdprintf(&not->hdrs, fmt, ap);
383 		va_end(ap);
384 		if (err)
385 			goto out;
386 	}
387 
388 	not->expires_min = expires_min;
389 	not->expires_dfl = expires_dfl;
390 	not->expires_max = expires_max;
391 	not->substate = SIPEVENT_PENDING;
392 	not->sock   = mem_ref(sock);
393 	not->sip    = mem_ref(sock->sip);
394 	not->closeh = closeh ? closeh : internal_close_handler;
395 	not->arg    = arg;
396 
397 	if (pl_isset(&msg->expires))
398 		expires = pl_u32(&msg->expires);
399 	else
400 		expires = not->expires_dfl;
401 
402 	sipnot_refresh(not, expires);
403 
404 	err = sipnot_reply(not, msg, scode, reason);
405 	if (err)
406 		goto out;
407 
408 	not->subscribed = true;
409 
410  out:
411 	if (err)
412 		mem_deref(not);
413 	else
414 		*notp = not;
415 
416 	return err;
417 }
418 
419 
sipevent_notify(struct sipnot * not,struct mbuf * mb,enum sipevent_subst state,enum sipevent_reason reason,uint32_t retry_after)420 int sipevent_notify(struct sipnot *not, struct mbuf *mb,
421 		    enum sipevent_subst state, enum sipevent_reason reason,
422 		    uint32_t retry_after)
423 {
424 	if (!not || not->terminated)
425 		return EINVAL;
426 
427 	if (mb || state != SIPEVENT_TERMINATED) {
428 		mem_deref(not->mb);
429 		not->mb = mem_ref(mb);
430 	}
431 
432 	switch (state) {
433 
434 	case SIPEVENT_ACTIVE:
435 	case SIPEVENT_PENDING:
436 		not->substate = state;
437 		return sipnot_notify(not);
438 
439 	case SIPEVENT_TERMINATED:
440 		tmr_cancel(&not->tmr);
441 		not->retry_after = retry_after;
442 		(void)terminate(not, reason);
443 		return 0;
444 
445 	default:
446 		return EINVAL;
447 	}
448 }
449 
450 
sipevent_notifyf(struct sipnot * not,struct mbuf ** mbp,enum sipevent_subst state,enum sipevent_reason reason,uint32_t retry_after,const char * fmt,...)451 int sipevent_notifyf(struct sipnot *not, struct mbuf **mbp,
452 		     enum sipevent_subst state, enum sipevent_reason reason,
453 		     uint32_t retry_after, const char *fmt, ...)
454 {
455 	struct mbuf *mb;
456 	va_list ap;
457 	int err;
458 
459 	if (!not || not->terminated || !fmt)
460 		return EINVAL;
461 
462 	if (mbp && *mbp)
463 		return sipevent_notify(not, *mbp, state, reason, retry_after);
464 
465 	mb = mbuf_alloc(1024);
466 	if (!mb)
467 		return ENOMEM;
468 
469 	va_start(ap, fmt);
470 	err = mbuf_vprintf(mb, fmt, ap);
471 	va_end(ap);
472 	if (err)
473 		goto out;
474 
475 	mb->pos = 0;
476 
477 	err = sipevent_notify(not, mb, state, reason, retry_after);
478 	if (err)
479 		goto out;
480 
481  out:
482 	if (err || !mbp)
483 		mem_deref(mb);
484 	else
485 		*mbp = mb;
486 
487 	return err;
488 }
489