1
/* $Id: mwi.c 4172 2012-06-19 14:35:18Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
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>
31
#include <pj/string.h>
34
#define THIS_FILE "mwi.c"
37
* MWI module (mod-mdi)
39
static struct pjsip_module mod_mwi =
41
NULL, NULL, /* prev, next. */
42
{ "mod-mwi", 7 }, /* Name. */
44
PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
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() */
58
* This structure describe an mwi agent (both client and server)
60
typedef struct pjsip_mwi
62
pjsip_evsub *sub; /**< Event subscribtion record. */
63
pjsip_dialog *dlg; /**< The dialog. */
64
pjsip_evsub_user user_cb; /**< The user callback. */
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 */
74
* Forward decl for evsub callbacks.
76
static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
77
static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
79
static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub,
84
pjsip_msg_body **p_body);
85
static void mwi_on_evsub_rx_notify( pjsip_evsub *sub,
90
pjsip_msg_body **p_body);
91
static void mwi_on_evsub_client_refresh(pjsip_evsub *sub);
92
static void mwi_on_evsub_server_timeout(pjsip_evsub *sub);
96
* Event subscription callback for mwi.
98
static pjsip_evsub_user mwi_user =
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,
110
* Some static constants.
112
static const pj_str_t STR_EVENT = { "Event", 5 };
113
static const pj_str_t STR_MWI = { "message-summary", 15 };
114
static const pj_str_t STR_APP_SIMPLE_SMS = { "application/simple-message-summary", 34};
119
PJ_DEF(pj_status_t) pjsip_mwi_init_module( pjsip_endpoint *endpt,
120
pjsip_module *mod_evsub)
125
/* Check arguments. */
126
PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
128
/* Must have not been registered */
129
PJ_ASSERT_RETURN(mod_mwi.id == -1, PJ_EINVALIDOP);
131
/* Register to endpoint */
132
status = pjsip_endpt_register_module(endpt, &mod_mwi);
133
if (status != PJ_SUCCESS)
136
accept[0] = STR_APP_SIMPLE_SMS;
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);
152
* Get mwi module instance.
154
PJ_DEF(pjsip_module*) pjsip_mwi_instance(void)
161
* Create client subscription.
163
PJ_DEF(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg,
164
const pjsip_evsub_user *user_cb,
166
pjsip_evsub **p_evsub )
172
PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
174
PJ_UNUSED_ARG(options);
176
pjsip_dlg_inc_lock(dlg);
178
/* Create event subscription */
179
status = pjsip_evsub_create_uac( dlg, &mwi_user, &STR_MWI,
181
if (status != PJ_SUCCESS)
185
mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
189
pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
191
/* Attach to evsub */
192
pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
197
pjsip_dlg_dec_lock(dlg);
203
* Create server subscription.
205
PJ_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 )
210
pjsip_accept_hdr *accept;
211
pjsip_event_hdr *event;
214
char obj_name[PJ_MAX_OBJ_NAME];
217
/* Check arguments */
218
PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
220
/* Must be request message */
221
PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
222
PJSIP_ENOTREQUESTMSG);
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);
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);
233
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
235
if (pj_stricmp(&event->event_type, &STR_MWI) != 0) {
236
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
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);
244
for (i=0; i<accept->count; ++i) {
245
if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) {
250
if (i==accept->count) {
251
/* Nothing is acceptable */
252
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
257
* Assume client supports "application/simple-message-summary"
262
pjsip_dlg_inc_lock(dlg);
265
/* Create server subscription */
266
status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub);
267
if (status != PJ_SUCCESS)
270
/* Create server mwi subscription */
271
mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
275
pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
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,
281
/* Attach to evsub */
282
pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
288
pjsip_dlg_dec_lock(dlg);
294
* Forcefully terminate mwi.
296
PJ_DEF(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub,
299
return pjsip_evsub_terminate(sub, notify);
305
PJ_DEF(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub,
307
pjsip_tx_data **p_tdata)
309
return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
315
* Accept incoming subscription.
317
PJ_DEF(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub,
318
pjsip_rx_data *rdata,
320
const pjsip_hdr *hdr_list )
322
return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
326
* Create message body and attach it to the (NOTIFY) request.
328
static pj_status_t mwi_create_msg_body( pjsip_mwi *mwi,
329
pjsip_tx_data *tdata)
331
pjsip_msg_body *body;
334
PJ_ASSERT_RETURN(mwi->mime_type.type.slen && mwi->body.slen, PJ_EINVALIDOP);
336
/* Clone the message body and mime type */
337
pj_strdup(tdata->pool, &dup_text, &mwi->body);
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;
347
/* Attach to tdata */
348
tdata->msg->body = body;
357
PJ_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)
366
pjsip_tx_data *tdata;
369
/* Check arguments. */
370
PJ_ASSERT_RETURN(sub && mime_type && body && p_tdata, PJ_EINVAL);
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);
377
pjsip_dlg_inc_lock(mwi->dlg);
379
/* Create the NOTIFY request. */
380
status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
381
if (status != PJ_SUCCESS)
384
/* Update the cached message body */
385
if (mime_type || body)
386
pj_pool_reset(mwi->body_pool);
388
pjsip_media_type_cp(mwi->body_pool, &mwi->mime_type, mime_type);
390
pj_strdup(mwi->body_pool, &mwi->body, body);
392
/* Create message body */
393
status = mwi_create_msg_body( mwi, tdata );
394
if (status != PJ_SUCCESS)
401
pjsip_dlg_dec_lock(mwi->dlg);
407
* Create NOTIFY that reflect current state.
409
PJ_DEF(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub,
410
pjsip_tx_data **p_tdata )
413
pjsip_tx_data *tdata;
416
/* Check arguments. */
417
PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL);
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);
424
pjsip_dlg_inc_lock(mwi->dlg);
426
/* Create the NOTIFY request. */
427
status = pjsip_evsub_current_notify( sub, &tdata);
428
if (status != PJ_SUCCESS)
432
/* Create message body to reflect the mwi status. */
433
status = mwi_create_msg_body( mwi, tdata );
434
if (status != PJ_SUCCESS)
441
pjsip_dlg_dec_lock(mwi->dlg);
449
PJ_DEF(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub,
450
pjsip_tx_data *tdata )
452
return pjsip_evsub_send_request(sub, tdata);
456
* This callback is called by event subscription when subscription
459
static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
463
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
464
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
466
if (mwi->user_cb.on_evsub_state)
467
(*mwi->user_cb.on_evsub_state)(sub, event);
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;
478
* Called when transaction state has changed.
480
static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
485
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
486
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
488
if (mwi->user_cb.on_tsx_state)
489
(*mwi->user_cb.on_tsx_state)(sub, tsx, event);
494
* Called when SUBSCRIBE is received.
496
static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub,
497
pjsip_rx_data *rdata,
499
pj_str_t **p_st_text,
501
pjsip_msg_body **p_body)
505
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
506
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
508
if (mwi->user_cb.on_rx_refresh) {
509
(*mwi->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
513
/* Implementors MUST send NOTIFY if it implements on_rx_refresh */
514
pjsip_tx_data *tdata;
515
pj_str_t timeout = { "timeout", 7};
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);
522
status = pjsip_mwi_current_notify(sub, &tdata);
525
if (status == PJ_SUCCESS)
526
pjsip_mwi_send_request(sub, tdata);
532
* Called when NOTIFY is received.
534
static void mwi_on_evsub_rx_notify( pjsip_evsub *sub,
535
pjsip_rx_data *rdata,
537
pj_str_t **p_st_text,
539
pjsip_msg_body **p_body)
543
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
544
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
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,
554
* Called when it's time to send SUBSCRIBE.
556
static void mwi_on_evsub_client_refresh(pjsip_evsub *sub)
560
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
561
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
563
if (mwi->user_cb.on_client_refresh) {
564
(*mwi->user_cb.on_client_refresh)(sub);
567
pjsip_tx_data *tdata;
569
status = pjsip_mwi_initiate(sub, -1, &tdata);
570
if (status == PJ_SUCCESS)
571
pjsip_mwi_send_request(sub, tdata);
576
* Called when no refresh is received after the interval.
578
static void mwi_on_evsub_server_timeout(pjsip_evsub *sub)
582
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
583
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
585
if (mwi->user_cb.on_server_timeout) {
586
(*mwi->user_cb.on_server_timeout)(sub);
589
pjsip_tx_data *tdata;
590
pj_str_t reason = { "timeout", 7 };
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);