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