blob: 537ca9ec51cbd346fb351a196932b891995bb40e [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjsip-simple/mwi.h>
21#include <pjsip-simple/errno.h>
22#include <pjsip-simple/evsub_msg.h>
23#include <pjsip/sip_module.h>
24#include <pjsip/sip_endpoint.h>
25#include <pjsip/sip_dialog.h>
26#include <pj/assert.h>
27#include <pj/guid.h>
28#include <pj/log.h>
29#include <pj/os.h>
30#include <pj/pool.h>
31#include <pj/string.h>
32
33
34#define THIS_FILE "mwi.c"
35
36 /*
37 * MWI module (mod-mdi)
38 */
39static struct pjsip_module mod_mwi =
40{
41 NULL, NULL, /* prev, next. */
42 { "mod-mwi", 7 }, /* Name. */
43 -1, /* Id */
44 PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
45 NULL, /* load() */
46 NULL, /* start() */
47 NULL, /* stop() */
48 NULL, /* unload() */
49 NULL, /* on_rx_request() */
50 NULL, /* on_rx_response() */
51 NULL, /* on_tx_request. */
52 NULL, /* on_tx_response() */
53 NULL, /* on_tsx_state() */
54};
55
56
57/*
58 * This structure describe an mwi agent (both client and server)
59 */
60typedef struct pjsip_mwi
61{
62 pjsip_evsub *sub; /**< Event subscribtion record. */
63 pjsip_dialog *dlg; /**< The dialog. */
64 pjsip_evsub_user user_cb; /**< The user callback. */
65
66 /* These are for server subscriptions */
67 pj_pool_t *body_pool; /**< Pool to save message body */
68 pjsip_media_type mime_type; /**< MIME type of last msg body */
69 pj_str_t body; /**< Last sent message body */
70} pjsip_mwi;
71
72
73/*
74 * Forward decl for evsub callbacks.
75 */
76static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
77static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
78 pjsip_event *event);
79static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub,
80 pjsip_rx_data *rdata,
81 int *p_st_code,
82 pj_str_t **p_st_text,
83 pjsip_hdr *res_hdr,
84 pjsip_msg_body **p_body);
85static void mwi_on_evsub_rx_notify( pjsip_evsub *sub,
86 pjsip_rx_data *rdata,
87 int *p_st_code,
88 pj_str_t **p_st_text,
89 pjsip_hdr *res_hdr,
90 pjsip_msg_body **p_body);
91static void mwi_on_evsub_client_refresh(pjsip_evsub *sub);
92static void mwi_on_evsub_server_timeout(pjsip_evsub *sub);
93
94
95/*
96 * Event subscription callback for mwi.
97 */
98static pjsip_evsub_user mwi_user =
99{
100 &mwi_on_evsub_state,
101 &mwi_on_evsub_tsx_state,
102 &mwi_on_evsub_rx_refresh,
103 &mwi_on_evsub_rx_notify,
104 &mwi_on_evsub_client_refresh,
105 &mwi_on_evsub_server_timeout,
106};
107
108
109/*
110 * Some static constants.
111 */
112static const pj_str_t STR_EVENT = { "Event", 5 };
113static const pj_str_t STR_MWI = { "message-summary", 15 };
114static const pj_str_t STR_APP_SIMPLE_SMS = { "application/simple-message-summary", 34};
115
116/*
117 * Init mwi module.
118 */
119PJ_DEF(pj_status_t) pjsip_mwi_init_module( pjsip_endpoint *endpt,
120 pjsip_module *mod_evsub)
121{
122 pj_status_t status;
123 pj_str_t accept[1];
124
125 /* Check arguments. */
126 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
127
128 /* Must have not been registered */
129 PJ_ASSERT_RETURN(mod_mwi.id == -1, PJ_EINVALIDOP);
130
131 /* Register to endpoint */
132 status = pjsip_endpt_register_module(endpt, &mod_mwi);
133 if (status != PJ_SUCCESS)
134 return status;
135
136 accept[0] = STR_APP_SIMPLE_SMS;
137
138 /* Register event package to event module. */
139 status = pjsip_evsub_register_pkg( &mod_mwi, &STR_MWI,
140 PJSIP_MWI_DEFAULT_EXPIRES,
141 PJ_ARRAY_SIZE(accept), accept);
142 if (status != PJ_SUCCESS) {
143 pjsip_endpt_unregister_module(endpt, &mod_mwi);
144 return status;
145 }
146
147 return PJ_SUCCESS;
148}
149
150
151/*
152 * Get mwi module instance.
153 */
154PJ_DEF(pjsip_module*) pjsip_mwi_instance(void)
155{
156 return &mod_mwi;
157}
158
159
160/*
161 * Create client subscription.
162 */
163PJ_DEF(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg,
164 const pjsip_evsub_user *user_cb,
165 unsigned options,
166 pjsip_evsub **p_evsub )
167{
168 pj_status_t status;
169 pjsip_mwi *mwi;
170 pjsip_evsub *sub;
171
172 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
173
174 PJ_UNUSED_ARG(options);
175
176 pjsip_dlg_inc_lock(dlg);
177
178 /* Create event subscription */
179 status = pjsip_evsub_create_uac( dlg, &mwi_user, &STR_MWI,
180 options, &sub);
181 if (status != PJ_SUCCESS)
182 goto on_return;
183
184 /* Create mwi */
185 mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
186 mwi->dlg = dlg;
187 mwi->sub = sub;
188 if (user_cb)
189 pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
190
191 /* Attach to evsub */
192 pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
193
194 *p_evsub = sub;
195
196on_return:
197 pjsip_dlg_dec_lock(dlg);
198 return status;
199}
200
201
202/*
203 * Create server subscription.
204 */
205PJ_DEF(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg,
206 const pjsip_evsub_user *user_cb,
207 pjsip_rx_data *rdata,
208 pjsip_evsub **p_evsub )
209{
210 pjsip_accept_hdr *accept;
211 pjsip_event_hdr *event;
212 pjsip_evsub *sub;
213 pjsip_mwi *mwi;
214 char obj_name[PJ_MAX_OBJ_NAME];
215 pj_status_t status;
216
217 /* Check arguments */
218 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
219
220 /* Must be request message */
221 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
222 PJSIP_ENOTREQUESTMSG);
223
224 /* Check that request is SUBSCRIBE */
225 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
226 &pjsip_subscribe_method)==0,
227 PJSIP_SIMPLE_ENOTSUBSCRIBE);
228
229 /* Check that Event header contains "mwi" */
230 event = (pjsip_event_hdr*)
231 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
232 if (!event) {
233 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
234 }
235 if (pj_stricmp(&event->event_type, &STR_MWI) != 0) {
236 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
237 }
238
239 /* Check that request contains compatible Accept header. */
240 accept = (pjsip_accept_hdr*)
241 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
242 if (accept) {
243 unsigned i;
244 for (i=0; i<accept->count; ++i) {
245 if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) {
246 break;
247 }
248 }
249
250 if (i==accept->count) {
251 /* Nothing is acceptable */
252 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
253 }
254
255 } else {
256 /* No Accept header.
257 * Assume client supports "application/simple-message-summary"
258 */
259 }
260
261 /* Lock dialog */
262 pjsip_dlg_inc_lock(dlg);
263
264
265 /* Create server subscription */
266 status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub);
267 if (status != PJ_SUCCESS)
268 goto on_return;
269
270 /* Create server mwi subscription */
271 mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
272 mwi->dlg = dlg;
273 mwi->sub = sub;
274 if (user_cb)
275 pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
276
277 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "mwibd%p", dlg->pool);
278 mwi->body_pool = pj_pool_create(dlg->pool->factory, obj_name,
279 512, 512, NULL);
280
281 /* Attach to evsub */
282 pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
283
284 /* Done: */
285 *p_evsub = sub;
286
287on_return:
288 pjsip_dlg_dec_lock(dlg);
289 return status;
290}
291
292
293/*
294 * Forcefully terminate mwi.
295 */
296PJ_DEF(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub,
297 pj_bool_t notify )
298{
299 return pjsip_evsub_terminate(sub, notify);
300}
301
302/*
303 * Create SUBSCRIBE
304 */
305PJ_DEF(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub,
306 pj_int32_t expires,
307 pjsip_tx_data **p_tdata)
308{
309 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
310 p_tdata);
311}
312
313
314/*
315 * Accept incoming subscription.
316 */
317PJ_DEF(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub,
318 pjsip_rx_data *rdata,
319 int st_code,
320 const pjsip_hdr *hdr_list )
321{
322 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
323}
324
325/*
326 * Create message body and attach it to the (NOTIFY) request.
327 */
328static pj_status_t mwi_create_msg_body( pjsip_mwi *mwi,
329 pjsip_tx_data *tdata)
330{
331 pjsip_msg_body *body;
332 pj_str_t dup_text;
333
334 PJ_ASSERT_RETURN(mwi->mime_type.type.slen && mwi->body.slen, PJ_EINVALIDOP);
335
336 /* Clone the message body and mime type */
337 pj_strdup(tdata->pool, &dup_text, &mwi->body);
338
339 /* Create the message body */
340 body = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_msg_body);
341 pjsip_media_type_cp(tdata->pool, &body->content_type, &mwi->mime_type);
342 body->data = dup_text.ptr;
343 body->len = (unsigned)dup_text.slen;
344 body->print_body = &pjsip_print_text_body;
345 body->clone_data = &pjsip_clone_text_data;
346
347 /* Attach to tdata */
348 tdata->msg->body = body;
349
350 return PJ_SUCCESS;
351}
352
353
354/*
355 * Create NOTIFY
356 */
357PJ_DEF(pj_status_t) pjsip_mwi_notify( pjsip_evsub *sub,
358 pjsip_evsub_state state,
359 const pj_str_t *state_str,
360 const pj_str_t *reason,
361 const pjsip_media_type *mime_type,
362 const pj_str_t *body,
363 pjsip_tx_data **p_tdata)
364{
365 pjsip_mwi *mwi;
366 pjsip_tx_data *tdata;
367 pj_status_t status;
368
369 /* Check arguments. */
370 PJ_ASSERT_RETURN(sub && mime_type && body && p_tdata, PJ_EINVAL);
371
372 /* Get the mwi object. */
373 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
374 PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP);
375
376 /* Lock object. */
377 pjsip_dlg_inc_lock(mwi->dlg);
378
379 /* Create the NOTIFY request. */
380 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
381 if (status != PJ_SUCCESS)
382 goto on_return;
383
384 /* Update the cached message body */
385 if (mime_type || body)
386 pj_pool_reset(mwi->body_pool);
387 if (mime_type)
388 pjsip_media_type_cp(mwi->body_pool, &mwi->mime_type, mime_type);
389 if (body)
390 pj_strdup(mwi->body_pool, &mwi->body, body);
391
392 /* Create message body */
393 status = mwi_create_msg_body( mwi, tdata );
394 if (status != PJ_SUCCESS)
395 goto on_return;
396
397 /* Done. */
398 *p_tdata = tdata;
399
400on_return:
401 pjsip_dlg_dec_lock(mwi->dlg);
402 return status;
403}
404
405
406/*
407 * Create NOTIFY that reflect current state.
408 */
409PJ_DEF(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub,
410 pjsip_tx_data **p_tdata )
411{
412 pjsip_mwi *mwi;
413 pjsip_tx_data *tdata;
414 pj_status_t status;
415
416 /* Check arguments. */
417 PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL);
418
419 /* Get the mwi object. */
420 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
421 PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP);
422
423 /* Lock object. */
424 pjsip_dlg_inc_lock(mwi->dlg);
425
426 /* Create the NOTIFY request. */
427 status = pjsip_evsub_current_notify( sub, &tdata);
428 if (status != PJ_SUCCESS)
429 goto on_return;
430
431
432 /* Create message body to reflect the mwi status. */
433 status = mwi_create_msg_body( mwi, tdata );
434 if (status != PJ_SUCCESS)
435 goto on_return;
436
437 /* Done. */
438 *p_tdata = tdata;
439
440on_return:
441 pjsip_dlg_dec_lock(mwi->dlg);
442 return status;
443}
444
445
446/*
447 * Send request.
448 */
449PJ_DEF(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub,
450 pjsip_tx_data *tdata )
451{
452 return pjsip_evsub_send_request(sub, tdata);
453}
454
455/*
456 * This callback is called by event subscription when subscription
457 * state has changed.
458 */
459static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
460{
461 pjsip_mwi *mwi;
462
463 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
464 PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
465
466 if (mwi->user_cb.on_evsub_state)
467 (*mwi->user_cb.on_evsub_state)(sub, event);
468
469 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
470 if (mwi->body_pool) {
471 pj_pool_release(mwi->body_pool);
472 mwi->body_pool = NULL;
473 }
474 }
475}
476
477/*
478 * Called when transaction state has changed.
479 */
480static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
481 pjsip_event *event)
482{
483 pjsip_mwi *mwi;
484
485 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
486 PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
487
488 if (mwi->user_cb.on_tsx_state)
489 (*mwi->user_cb.on_tsx_state)(sub, tsx, event);
490}
491
492
493/*
494 * Called when SUBSCRIBE is received.
495 */
496static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub,
497 pjsip_rx_data *rdata,
498 int *p_st_code,
499 pj_str_t **p_st_text,
500 pjsip_hdr *res_hdr,
501 pjsip_msg_body **p_body)
502{
503 pjsip_mwi *mwi;
504
505 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
506 PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
507
508 if (mwi->user_cb.on_rx_refresh) {
509 (*mwi->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
510 res_hdr, p_body);
511
512 } else {
513 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
514 pjsip_tx_data *tdata;
515 pj_str_t timeout = { "timeout", 7};
516 pj_status_t status;
517
518 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
519 status = pjsip_mwi_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
520 NULL, &timeout, NULL, NULL, &tdata);
521 } else {
522 status = pjsip_mwi_current_notify(sub, &tdata);
523 }
524
525 if (status == PJ_SUCCESS)
526 pjsip_mwi_send_request(sub, tdata);
527 }
528}
529
530
531/*
532 * Called when NOTIFY is received.
533 */
534static void mwi_on_evsub_rx_notify( pjsip_evsub *sub,
535 pjsip_rx_data *rdata,
536 int *p_st_code,
537 pj_str_t **p_st_text,
538 pjsip_hdr *res_hdr,
539 pjsip_msg_body **p_body)
540{
541 pjsip_mwi *mwi;
542
543 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
544 PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
545
546 /* Just notify application. */
547 if (mwi->user_cb.on_rx_notify) {
548 (*mwi->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
549 res_hdr, p_body);
550 }
551}
552
553/*
554 * Called when it's time to send SUBSCRIBE.
555 */
556static void mwi_on_evsub_client_refresh(pjsip_evsub *sub)
557{
558 pjsip_mwi *mwi;
559
560 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
561 PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
562
563 if (mwi->user_cb.on_client_refresh) {
564 (*mwi->user_cb.on_client_refresh)(sub);
565 } else {
566 pj_status_t status;
567 pjsip_tx_data *tdata;
568
569 status = pjsip_mwi_initiate(sub, -1, &tdata);
570 if (status == PJ_SUCCESS)
571 pjsip_mwi_send_request(sub, tdata);
572 }
573}
574
575/*
576 * Called when no refresh is received after the interval.
577 */
578static void mwi_on_evsub_server_timeout(pjsip_evsub *sub)
579{
580 pjsip_mwi *mwi;
581
582 mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
583 PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
584
585 if (mwi->user_cb.on_server_timeout) {
586 (*mwi->user_cb.on_server_timeout)(sub);
587 } else {
588 pj_status_t status;
589 pjsip_tx_data *tdata;
590 pj_str_t reason = { "timeout", 7 };
591
592 status = pjsip_mwi_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
593 NULL, &reason, NULL, NULL, &tdata);
594 if (status == PJ_SUCCESS)
595 pjsip_mwi_send_request(sub, tdata);
596 }
597}
598