blob: bb5dcd52af7dc42f17fe3318c908b74bbb22fa73 [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono5dcb38d2005-11-21 01:55:47 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono834aee32006-02-19 01:38:06 +000019#include <pjsip-simple/presence.h>
20#include <pjsip-simple/errno.h>
21#include <pjsip-simple/evsub_msg.h>
22#include <pjsip/sip_module.h>
23#include <pjsip/sip_endpoint.h>
24#include <pjsip/sip_dialog.h>
25#include <pj/assert.h>
26#include <pj/guid.h>
27#include <pj/log.h>
28#include <pj/os.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000029#include <pj/pool.h>
30#include <pj/string.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000031
Benny Prijono5dcb38d2005-11-21 01:55:47 +000032
Benny Prijono834aee32006-02-19 01:38:06 +000033#define THIS_FILE "presence.c"
34#define PRES_DEFAULT_EXPIRES 600
Benny Prijono5dcb38d2005-11-21 01:55:47 +000035
36/*
Benny Prijono834aee32006-02-19 01:38:06 +000037 * Presence module (mod-presence)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000038 */
Benny Prijono834aee32006-02-19 01:38:06 +000039static struct pjsip_module mod_presence =
Benny Prijono5dcb38d2005-11-21 01:55:47 +000040{
Benny Prijono834aee32006-02-19 01:38:06 +000041 NULL, NULL, /* prev, next. */
42 { "mod-presence", 12 }, /* Name. */
43 -1, /* Id */
Benny Prijono2f8992b2006-02-25 21:16:36 +000044 PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +000045 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 * Presence message body type.
59 */
Benny Prijonoa1e69682007-05-11 15:14:34 +000060typedef enum content_type_e
Benny Prijono834aee32006-02-19 01:38:06 +000061{
62 CONTENT_TYPE_NONE,
63 CONTENT_TYPE_PIDF,
64 CONTENT_TYPE_XPIDF,
Benny Prijonoa1e69682007-05-11 15:14:34 +000065} content_type_e;
Benny Prijono834aee32006-02-19 01:38:06 +000066
67/*
68 * This structure describe a presentity, for both subscriber and notifier.
69 */
70struct pjsip_pres
71{
72 pjsip_evsub *sub; /**< Event subscribtion record. */
73 pjsip_dialog *dlg; /**< The dialog. */
Benny Prijonoa1e69682007-05-11 15:14:34 +000074 content_type_e content_type; /**< Content-Type. */
Benny Prijono834aee32006-02-19 01:38:06 +000075 pjsip_pres_status status; /**< Presence status. */
76 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
77 pjsip_evsub_user user_cb; /**< The user callback. */
78};
79
80
81typedef struct pjsip_pres pjsip_pres;
82
83
84/*
85 * Forward decl for evsub callback.
86 */
87static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
88static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
89 pjsip_event *event);
90static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
91 pjsip_rx_data *rdata,
92 int *p_st_code,
93 pj_str_t **p_st_text,
94 pjsip_hdr *res_hdr,
95 pjsip_msg_body **p_body);
96static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
97 pjsip_rx_data *rdata,
98 int *p_st_code,
99 pj_str_t **p_st_text,
100 pjsip_hdr *res_hdr,
101 pjsip_msg_body **p_body);
102static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
103static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
104
105
106/*
107 * Event subscription callback for presence.
108 */
109static pjsip_evsub_user pres_user =
110{
111 &pres_on_evsub_state,
112 &pres_on_evsub_tsx_state,
113 &pres_on_evsub_rx_refresh,
114 &pres_on_evsub_rx_notify,
115 &pres_on_evsub_client_refresh,
116 &pres_on_evsub_server_timeout,
117};
118
119
120/*
121 * Some static constants.
122 */
123const pj_str_t STR_EVENT = { "Event", 5 };
124const pj_str_t STR_PRESENCE = { "presence", 8 };
125const pj_str_t STR_APPLICATION = { "application", 11 };
126const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
127const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
128const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
129const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
130
131
132/*
133 * Init presence module.
134 */
135PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
136 pjsip_module *mod_evsub)
137{
138 pj_status_t status;
139 pj_str_t accept[2];
140
141 /* Check arguments. */
142 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
143
144 /* Must have not been registered */
145 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
146
147 /* Register to endpoint */
148 status = pjsip_endpt_register_module(endpt, &mod_presence);
149 if (status != PJ_SUCCESS)
150 return status;
151
152 accept[0] = STR_APP_PIDF_XML;
153 accept[1] = STR_APP_XPIDF_XML;
154
155 /* Register event package to event module. */
156 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
157 PRES_DEFAULT_EXPIRES,
158 PJ_ARRAY_SIZE(accept), accept);
159 if (status != PJ_SUCCESS) {
160 pjsip_endpt_unregister_module(endpt, &mod_presence);
161 return status;
162 }
163
164 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000165}
166
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000167
Benny Prijono834aee32006-02-19 01:38:06 +0000168/*
169 * Get presence module instance.
170 */
171PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
172{
173 return &mod_presence;
174}
175
176
177/*
178 * Create client subscription.
179 */
180PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
181 const pjsip_evsub_user *user_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000182 unsigned options,
Benny Prijono834aee32006-02-19 01:38:06 +0000183 pjsip_evsub **p_evsub )
184{
185 pj_status_t status;
186 pjsip_pres *pres;
187 pjsip_evsub *sub;
188
189 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
190
191 pjsip_dlg_inc_lock(dlg);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000192
193 /* Create event subscription */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000194 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE,
195 options, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000196 if (status != PJ_SUCCESS)
197 goto on_return;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000198
Benny Prijono834aee32006-02-19 01:38:06 +0000199 /* Create presence */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000200 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000201 pres->dlg = dlg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000202 pres->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000203 if (user_cb)
204 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000205
Benny Prijono834aee32006-02-19 01:38:06 +0000206 /* Attach to evsub */
207 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
208
209 *p_evsub = sub;
210
211on_return:
212 pjsip_dlg_dec_lock(dlg);
213 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000214}
215
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000216
217/*
Benny Prijono834aee32006-02-19 01:38:06 +0000218 * Create server subscription.
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000219 */
Benny Prijono834aee32006-02-19 01:38:06 +0000220PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
221 const pjsip_evsub_user *user_cb,
222 pjsip_rx_data *rdata,
223 pjsip_evsub **p_evsub )
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000224{
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000225 pjsip_accept_hdr *accept;
Benny Prijono834aee32006-02-19 01:38:06 +0000226 pjsip_event_hdr *event;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000227 content_type_e content_type = CONTENT_TYPE_NONE;
Benny Prijono834aee32006-02-19 01:38:06 +0000228 pjsip_evsub *sub;
229 pjsip_pres *pres;
230 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000231
Benny Prijono834aee32006-02-19 01:38:06 +0000232 /* Check arguments */
233 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000234
Benny Prijono834aee32006-02-19 01:38:06 +0000235 /* Must be request message */
236 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
237 PJSIP_ENOTREQUESTMSG);
238
239 /* Check that request is SUBSCRIBE */
240 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
241 &pjsip_subscribe_method)==0,
242 PJSIP_SIMPLE_ENOTSUBSCRIBE);
243
244 /* Check that Event header contains "presence" */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000245 event = (pjsip_event_hdr*)
246 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000247 if (!event) {
248 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
249 }
250 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
251 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
252 }
253
254 /* Check that request contains compatible Accept header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000255 accept = (pjsip_accept_hdr*)
256 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000257 if (accept) {
258 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +0000259 for (i=0; i<accept->count; ++i) {
260 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
261 content_type = CONTENT_TYPE_PIDF;
262 break;
263 } else
264 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
265 content_type = CONTENT_TYPE_XPIDF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000266 break;
267 }
268 }
269
Benny Prijono834aee32006-02-19 01:38:06 +0000270 if (i==accept->count) {
271 /* Nothing is acceptable */
272 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000273 }
274
Benny Prijono834aee32006-02-19 01:38:06 +0000275 } else {
276 /* No Accept header.
277 * Treat as "application/pidf+xml"
278 */
279 content_type = CONTENT_TYPE_PIDF;
280 }
281
Benny Prijono834aee32006-02-19 01:38:06 +0000282 /* Lock dialog */
283 pjsip_dlg_inc_lock(dlg);
284
285
286 /* Create server subscription */
Benny Prijono26ff9062006-02-21 23:47:00 +0000287 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000288 if (status != PJ_SUCCESS)
289 goto on_return;
290
291 /* Create server presence subscription */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000292 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000293 pres->dlg = dlg;
294 pres->sub = sub;
295 pres->content_type = content_type;
296 if (user_cb)
297 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
298
299 /* Attach to evsub */
300 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
301
302 /* Done: */
303 *p_evsub = sub;
304
305on_return:
306 pjsip_dlg_dec_lock(dlg);
307 return status;
308}
309
310
311/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000312 * Forcefully terminate presence.
313 */
314PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
315 pj_bool_t notify )
316{
317 return pjsip_evsub_terminate(sub, notify);
318}
319
320/*
Benny Prijono834aee32006-02-19 01:38:06 +0000321 * Create SUBSCRIBE
322 */
323PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
324 pj_int32_t expires,
325 pjsip_tx_data **p_tdata)
326{
327 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
328 p_tdata);
329}
330
331
332/*
333 * Accept incoming subscription.
334 */
335PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
336 pjsip_rx_data *rdata,
337 int st_code,
338 const pjsip_hdr *hdr_list )
339{
340 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
341}
342
343
344/*
345 * Get presence status.
346 */
347PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
348 pjsip_pres_status *status )
349{
350 pjsip_pres *pres;
351
352 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
353
Benny Prijono9d4469d2007-05-02 05:14:29 +0000354 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000355 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
356
357 if (pres->tmp_status._is_valid)
358 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
359 else
360 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
361
362 return PJ_SUCCESS;
363}
364
365
366/*
367 * Set presence status.
368 */
369PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
370 const pjsip_pres_status *status )
371{
372 unsigned i;
373 pjsip_pres *pres;
374
375 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
376
Benny Prijono9d4469d2007-05-02 05:14:29 +0000377 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000378 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
379
380 for (i=0; i<status->info_cnt; ++i) {
381 pres->status.info[i].basic_open = status->info[i].basic_open;
382 if (status->info[i].id.slen == 0) {
383 pj_create_unique_string(pres->dlg->pool,
384 &pres->status.info[i].id);
385 } else {
386 pj_strdup(pres->dlg->pool,
387 &pres->status.info[i].id,
388 &status->info[i].id);
389 }
390 pj_strdup(pres->dlg->pool,
391 &pres->status.info[i].contact,
392 &status->info[i].contact);
Benny Prijono4461c7d2007-08-25 13:36:15 +0000393
394 /* Duplicate <person> */
395 pres->status.info[i].rpid.activity =
396 status->info[i].rpid.activity;
397 pj_strdup(pres->dlg->pool,
398 &pres->status.info[i].rpid.id,
399 &status->info[i].rpid.id);
400 pj_strdup(pres->dlg->pool,
401 &pres->status.info[i].rpid.note,
402 &status->info[i].rpid.note);
403
Benny Prijono834aee32006-02-19 01:38:06 +0000404 }
405
406 pres->status.info_cnt = status->info_cnt;
407
408 return PJ_SUCCESS;
409}
410
411
412/*
Benny Prijono834aee32006-02-19 01:38:06 +0000413 * Create message body.
414 */
415static pj_status_t pres_create_msg_body( pjsip_pres *pres,
416 pjsip_tx_data *tdata)
417{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000418 pj_str_t entity;
Benny Prijono834aee32006-02-19 01:38:06 +0000419
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000420 /* Get publisher URI */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000421 entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000422 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
423 pres->dlg->local.info->uri,
424 entity.ptr, PJSIP_MAX_URL_SIZE);
425 if (entity.slen < 1)
426 return PJ_ENOMEM;
427
Benny Prijono834aee32006-02-19 01:38:06 +0000428 if (pres->content_type == CONTENT_TYPE_PIDF) {
429
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000430 return pjsip_pres_create_pidf(tdata->pool, &pres->status,
431 &entity, &tdata->msg->body);
Benny Prijono834aee32006-02-19 01:38:06 +0000432
433 } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
434
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000435 return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
436 &entity, &tdata->msg->body);
Benny Prijono834aee32006-02-19 01:38:06 +0000437
438 } else {
439 return PJSIP_SIMPLE_EBADCONTENT;
440 }
Benny Prijono834aee32006-02-19 01:38:06 +0000441}
442
443
444/*
445 * Create NOTIFY
446 */
447PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
448 pjsip_evsub_state state,
449 const pj_str_t *state_str,
450 const pj_str_t *reason,
451 pjsip_tx_data **p_tdata)
452{
453 pjsip_pres *pres;
454 pjsip_tx_data *tdata;
455 pj_status_t status;
456
457 /* Check arguments. */
458 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
459
460 /* Get the presence object. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000461 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000462 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
463
464 /* Must have at least one presence info. */
465 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
466
467
468 /* Lock object. */
469 pjsip_dlg_inc_lock(pres->dlg);
470
471 /* Create the NOTIFY request. */
472 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
473 if (status != PJ_SUCCESS)
474 goto on_return;
475
476
477 /* Create message body to reflect the presence status. */
478 status = pres_create_msg_body( pres, tdata );
479 if (status != PJ_SUCCESS)
480 goto on_return;
481
482
483 /* Done. */
484 *p_tdata = tdata;
485
486
487on_return:
488 pjsip_dlg_dec_lock(pres->dlg);
489 return status;
490}
491
492
493/*
494 * Create NOTIFY that reflect current state.
495 */
496PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
497 pjsip_tx_data **p_tdata )
498{
499 pjsip_pres *pres;
500 pjsip_tx_data *tdata;
501 pj_status_t status;
502
503 /* Check arguments. */
504 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
505
506 /* Get the presence object. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000507 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000508 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
509
510 /* Must have at least one presence info. */
511 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
512
513
514 /* Lock object. */
515 pjsip_dlg_inc_lock(pres->dlg);
516
517 /* Create the NOTIFY request. */
518 status = pjsip_evsub_current_notify( sub, &tdata);
519 if (status != PJ_SUCCESS)
520 goto on_return;
521
522
523 /* Create message body to reflect the presence status. */
524 status = pres_create_msg_body( pres, tdata );
525 if (status != PJ_SUCCESS)
526 goto on_return;
527
528
529 /* Done. */
530 *p_tdata = tdata;
531
532
533on_return:
534 pjsip_dlg_dec_lock(pres->dlg);
535 return status;
536}
537
538
539/*
540 * Send request.
541 */
542PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
543 pjsip_tx_data *tdata )
544{
545 return pjsip_evsub_send_request(sub, tdata);
546}
547
548
549/*
550 * This callback is called by event subscription when subscription
551 * state has changed.
552 */
553static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
554{
555 pjsip_pres *pres;
556
Benny Prijono9d4469d2007-05-02 05:14:29 +0000557 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000558 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
559
560 if (pres->user_cb.on_evsub_state)
561 (*pres->user_cb.on_evsub_state)(sub, event);
562}
563
564/*
565 * Called when transaction state has changed.
566 */
567static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
568 pjsip_event *event)
569{
570 pjsip_pres *pres;
571
Benny Prijono9d4469d2007-05-02 05:14:29 +0000572 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000573 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
574
575 if (pres->user_cb.on_tsx_state)
576 (*pres->user_cb.on_tsx_state)(sub, tsx, event);
577}
578
579
580/*
581 * Called when SUBSCRIBE is received.
582 */
583static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
584 pjsip_rx_data *rdata,
585 int *p_st_code,
586 pj_str_t **p_st_text,
587 pjsip_hdr *res_hdr,
588 pjsip_msg_body **p_body)
589{
590 pjsip_pres *pres;
591
Benny Prijono9d4469d2007-05-02 05:14:29 +0000592 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000593 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
594
595 if (pres->user_cb.on_rx_refresh) {
596 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
597 res_hdr, p_body);
598
599 } else {
600 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
601 pjsip_tx_data *tdata;
602 pj_str_t timeout = { "timeout", 7};
603 pj_status_t status;
604
605 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
606 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
607 NULL, &timeout, &tdata);
608 } else {
609 status = pjsip_pres_current_notify(sub, &tdata);
610 }
611
612 if (status == PJ_SUCCESS)
613 pjsip_pres_send_request(sub, tdata);
614 }
615}
616
Benny Prijono834aee32006-02-19 01:38:06 +0000617
618/*
Benny Prijonob0808372006-03-02 21:18:58 +0000619 * Process the content of incoming NOTIFY request and update temporary
620 * status.
621 *
622 * return PJ_SUCCESS if incoming request is acceptable. If return value
623 * is not PJ_SUCCESS, res_hdr may be added with Warning header.
Benny Prijono834aee32006-02-19 01:38:06 +0000624 */
Benny Prijonob0808372006-03-02 21:18:58 +0000625static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
626 pjsip_rx_data *rdata,
627 int *p_st_code,
628 pj_str_t **p_st_text,
629 pjsip_hdr *res_hdr)
Benny Prijono834aee32006-02-19 01:38:06 +0000630{
631 pjsip_ctype_hdr *ctype_hdr;
Benny Prijono834aee32006-02-19 01:38:06 +0000632 pj_status_t status;
633
Benny Prijonob0808372006-03-02 21:18:58 +0000634 *p_st_text = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +0000635
636 /* Check Content-Type and msg body are present. */
637 ctype_hdr = rdata->msg_info.ctype;
638
639 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
640
641 pjsip_warning_hdr *warn_hdr;
642 pj_str_t warn_text;
643
644 *p_st_code = PJSIP_SC_BAD_REQUEST;
645
646 warn_text = pj_str("Message body is not present");
647 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
648 pjsip_endpt_name(pres->dlg->endpt),
649 &warn_text);
650 pj_list_push_back(res_hdr, warn_hdr);
651
Benny Prijonob0808372006-03-02 21:18:58 +0000652 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
Benny Prijono834aee32006-02-19 01:38:06 +0000653 }
654
655 /* Parse content. */
656
657 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
658 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
659 {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000660 status = pjsip_pres_parse_pidf( rdata, pres->dlg->pool,
661 &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000662 }
663 else
664 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
665 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
666 {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000667 status = pjsip_pres_parse_xpidf( rdata, pres->dlg->pool,
668 &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000669 }
670 else
671 {
672 status = PJSIP_SIMPLE_EBADCONTENT;
673 }
674
675 if (status != PJ_SUCCESS) {
676 /* Unsupported or bad Content-Type */
677 pjsip_accept_hdr *accept_hdr;
678 pjsip_warning_hdr *warn_hdr;
679
680 *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
681
682 /* Add Accept header */
683 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
684 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
685 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
686 pj_list_push_back(res_hdr, accept_hdr);
687
688 /* Add Warning header */
689 warn_hdr = pjsip_warning_hdr_create_from_status(
690 rdata->tp_info.pool,
691 pjsip_endpt_name(pres->dlg->endpt),
692 status);
693 pj_list_push_back(res_hdr, warn_hdr);
694
Benny Prijonob0808372006-03-02 21:18:58 +0000695 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000696 }
697
698 /* If application calls pres_get_status(), redirect the call to
699 * retrieve the temporary status.
700 */
701 pres->tmp_status._is_valid = PJ_TRUE;
702
Benny Prijonob0808372006-03-02 21:18:58 +0000703 return PJ_SUCCESS;
704}
705
706
707/*
708 * Called when NOTIFY is received.
709 */
710static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
711 pjsip_rx_data *rdata,
712 int *p_st_code,
713 pj_str_t **p_st_text,
714 pjsip_hdr *res_hdr,
715 pjsip_msg_body **p_body)
716{
717 pjsip_pres *pres;
718 pj_status_t status;
719
Benny Prijono9d4469d2007-05-02 05:14:29 +0000720 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijonob0808372006-03-02 21:18:58 +0000721 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
722
Benny Prijonob0808372006-03-02 21:18:58 +0000723 if (rdata->msg_info.msg->body) {
724 status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
725 res_hdr );
726 if (status != PJ_SUCCESS)
727 return;
728
729 } else {
Benny Prijonoe6821552006-06-19 12:03:35 +0000730 /* This has just been changed. Previously, we treat incoming NOTIFY
731 * with no message body as having the presence subscription closed.
732 * Now we treat it as no change in presence status (ref: EyeBeam).
733 */
734#if 1
735 *p_st_code = 200;
736 return;
737#else
Benny Prijonob0808372006-03-02 21:18:58 +0000738 unsigned i;
Benny Prijonob0808372006-03-02 21:18:58 +0000739 /* Subscription is terminated. Consider contact is offline */
740 pres->tmp_status._is_valid = PJ_TRUE;
741 for (i=0; i<pres->tmp_status.info_cnt; ++i)
742 pres->tmp_status.info[i].basic_open = PJ_FALSE;
Benny Prijonoe6821552006-06-19 12:03:35 +0000743#endif
Benny Prijonob0808372006-03-02 21:18:58 +0000744 }
745
Benny Prijono834aee32006-02-19 01:38:06 +0000746 /* Notify application. */
747 if (pres->user_cb.on_rx_notify) {
748 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
749 res_hdr, p_body);
750 }
751
752
753 /* If application responded NOTIFY with 2xx, copy temporary status
754 * to main status, and mark the temporary status as invalid.
755 */
756 if ((*p_st_code)/100 == 2) {
757 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
758 }
759
760 pres->tmp_status._is_valid = PJ_FALSE;
761
762 /* Done */
763}
764
765/*
766 * Called when it's time to send SUBSCRIBE.
767 */
768static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
769{
770 pjsip_pres *pres;
771
Benny Prijono9d4469d2007-05-02 05:14:29 +0000772 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000773 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
774
775 if (pres->user_cb.on_client_refresh) {
776 (*pres->user_cb.on_client_refresh)(sub);
777 } else {
778 pj_status_t status;
779 pjsip_tx_data *tdata;
780
781 status = pjsip_pres_initiate(sub, -1, &tdata);
782 if (status == PJ_SUCCESS)
783 pjsip_pres_send_request(sub, tdata);
784 }
785}
786
787/*
788 * Called when no refresh is received after the interval.
789 */
790static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
791{
792 pjsip_pres *pres;
793
Benny Prijono9d4469d2007-05-02 05:14:29 +0000794 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000795 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
796
797 if (pres->user_cb.on_server_timeout) {
798 (*pres->user_cb.on_server_timeout)(sub);
799 } else {
800 pj_status_t status;
801 pjsip_tx_data *tdata;
802 pj_str_t reason = { "timeout", 7 };
803
804 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
805 NULL, &reason, &tdata);
806 if (status == PJ_SUCCESS)
807 pjsip_pres_send_request(sub, tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000808 }
809}
810