blob: 7a7a87d253e0e9ee58132828c78bd7f41abf150f [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
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
22static int notify_request(struct sipnot *not, bool reset_ls);
23
24
25static 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
34static 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
54static 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
81static 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
98static 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
109void 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
117static 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
177static 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
190static 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
199static 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
226static 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
245static 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
269int 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
284int 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
304int 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
420int 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
451int 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}