blob: 24049359f50a39754ecb69993cbae579911a1733 [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono5dcb38d2005-11-21 01:55:47 +00005 *
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 */
Benny Prijono834aee32006-02-19 01:38:06 +000020#include <pjsip-simple/presence.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>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000030#include <pj/pool.h>
31#include <pj/string.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000032
Benny Prijono5dcb38d2005-11-21 01:55:47 +000033
Benny Prijono834aee32006-02-19 01:38:06 +000034#define THIS_FILE "presence.c"
Benny Prijonocf2e6732009-06-01 15:39:52 +000035#define PRES_DEFAULT_EXPIRES PJSIP_PRES_DEFAULT_EXPIRES
Benny Prijono5dcb38d2005-11-21 01:55:47 +000036
37/*
Benny Prijono834aee32006-02-19 01:38:06 +000038 * Presence module (mod-presence)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000039 */
Benny Prijono834aee32006-02-19 01:38:06 +000040static struct pjsip_module mod_presence =
Benny Prijono5dcb38d2005-11-21 01:55:47 +000041{
Benny Prijono834aee32006-02-19 01:38:06 +000042 NULL, NULL, /* prev, next. */
43 { "mod-presence", 12 }, /* Name. */
44 -1, /* Id */
Benny Prijono2f8992b2006-02-25 21:16:36 +000045 PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +000046 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 * Presence message body type.
60 */
Benny Prijonoa1e69682007-05-11 15:14:34 +000061typedef enum content_type_e
Benny Prijono834aee32006-02-19 01:38:06 +000062{
63 CONTENT_TYPE_NONE,
64 CONTENT_TYPE_PIDF,
65 CONTENT_TYPE_XPIDF,
Benny Prijonoa1e69682007-05-11 15:14:34 +000066} content_type_e;
Benny Prijono834aee32006-02-19 01:38:06 +000067
68/*
69 * This structure describe a presentity, for both subscriber and notifier.
70 */
71struct pjsip_pres
72{
73 pjsip_evsub *sub; /**< Event subscribtion record. */
74 pjsip_dialog *dlg; /**< The dialog. */
Benny Prijonoa1e69682007-05-11 15:14:34 +000075 content_type_e content_type; /**< Content-Type. */
Benny Prijono28add7e2009-06-15 16:03:40 +000076 pj_pool_t *status_pool; /**< Pool for pres_status */
Benny Prijono834aee32006-02-19 01:38:06 +000077 pjsip_pres_status status; /**< Presence status. */
Benny Prijono28add7e2009-06-15 16:03:40 +000078 pj_pool_t *tmp_pool; /**< Pool for tmp_status */
Benny Prijono834aee32006-02-19 01:38:06 +000079 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
80 pjsip_evsub_user user_cb; /**< The user callback. */
81};
82
83
84typedef struct pjsip_pres pjsip_pres;
85
86
87/*
88 * Forward decl for evsub callback.
89 */
90static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
91static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
92 pjsip_event *event);
93static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
94 pjsip_rx_data *rdata,
95 int *p_st_code,
96 pj_str_t **p_st_text,
97 pjsip_hdr *res_hdr,
98 pjsip_msg_body **p_body);
99static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
100 pjsip_rx_data *rdata,
101 int *p_st_code,
102 pj_str_t **p_st_text,
103 pjsip_hdr *res_hdr,
104 pjsip_msg_body **p_body);
105static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
106static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
107
108
109/*
110 * Event subscription callback for presence.
111 */
112static pjsip_evsub_user pres_user =
113{
114 &pres_on_evsub_state,
115 &pres_on_evsub_tsx_state,
116 &pres_on_evsub_rx_refresh,
117 &pres_on_evsub_rx_notify,
118 &pres_on_evsub_client_refresh,
119 &pres_on_evsub_server_timeout,
120};
121
122
123/*
124 * Some static constants.
125 */
126const pj_str_t STR_EVENT = { "Event", 5 };
127const pj_str_t STR_PRESENCE = { "presence", 8 };
128const pj_str_t STR_APPLICATION = { "application", 11 };
129const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
130const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
131const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
132const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
133
134
135/*
136 * Init presence module.
137 */
138PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
139 pjsip_module *mod_evsub)
140{
141 pj_status_t status;
142 pj_str_t accept[2];
143
144 /* Check arguments. */
145 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
146
147 /* Must have not been registered */
148 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
149
150 /* Register to endpoint */
151 status = pjsip_endpt_register_module(endpt, &mod_presence);
152 if (status != PJ_SUCCESS)
153 return status;
154
155 accept[0] = STR_APP_PIDF_XML;
156 accept[1] = STR_APP_XPIDF_XML;
157
158 /* Register event package to event module. */
159 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
160 PRES_DEFAULT_EXPIRES,
161 PJ_ARRAY_SIZE(accept), accept);
162 if (status != PJ_SUCCESS) {
163 pjsip_endpt_unregister_module(endpt, &mod_presence);
164 return status;
165 }
166
167 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000168}
169
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000170
Benny Prijono834aee32006-02-19 01:38:06 +0000171/*
172 * Get presence module instance.
173 */
174PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
175{
176 return &mod_presence;
177}
178
179
180/*
181 * Create client subscription.
182 */
183PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
184 const pjsip_evsub_user *user_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000185 unsigned options,
Benny Prijono834aee32006-02-19 01:38:06 +0000186 pjsip_evsub **p_evsub )
187{
188 pj_status_t status;
189 pjsip_pres *pres;
Benny Prijono28add7e2009-06-15 16:03:40 +0000190 char obj_name[PJ_MAX_OBJ_NAME];
Benny Prijono834aee32006-02-19 01:38:06 +0000191 pjsip_evsub *sub;
192
193 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
194
195 pjsip_dlg_inc_lock(dlg);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000196
197 /* Create event subscription */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000198 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE,
199 options, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000200 if (status != PJ_SUCCESS)
201 goto on_return;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000202
Benny Prijono834aee32006-02-19 01:38:06 +0000203 /* Create presence */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000204 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000205 pres->dlg = dlg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000206 pres->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000207 if (user_cb)
208 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000209
Benny Prijono28add7e2009-06-15 16:03:40 +0000210 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
211 pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
212 512, 512, NULL);
213 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
214 pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
215 512, 512, NULL);
216
Benny Prijono834aee32006-02-19 01:38:06 +0000217 /* Attach to evsub */
218 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
219
220 *p_evsub = sub;
221
222on_return:
223 pjsip_dlg_dec_lock(dlg);
224 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000225}
226
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000227
228/*
Benny Prijono834aee32006-02-19 01:38:06 +0000229 * Create server subscription.
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000230 */
Benny Prijono834aee32006-02-19 01:38:06 +0000231PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
232 const pjsip_evsub_user *user_cb,
233 pjsip_rx_data *rdata,
234 pjsip_evsub **p_evsub )
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000235{
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000236 pjsip_accept_hdr *accept;
Benny Prijono834aee32006-02-19 01:38:06 +0000237 pjsip_event_hdr *event;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000238 content_type_e content_type = CONTENT_TYPE_NONE;
Benny Prijono834aee32006-02-19 01:38:06 +0000239 pjsip_evsub *sub;
240 pjsip_pres *pres;
Benny Prijono28add7e2009-06-15 16:03:40 +0000241 char obj_name[PJ_MAX_OBJ_NAME];
Benny Prijono834aee32006-02-19 01:38:06 +0000242 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000243
Benny Prijono834aee32006-02-19 01:38:06 +0000244 /* Check arguments */
245 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000246
Benny Prijono834aee32006-02-19 01:38:06 +0000247 /* Must be request message */
248 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
249 PJSIP_ENOTREQUESTMSG);
250
251 /* Check that request is SUBSCRIBE */
252 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
253 &pjsip_subscribe_method)==0,
254 PJSIP_SIMPLE_ENOTSUBSCRIBE);
255
256 /* Check that Event header contains "presence" */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000257 event = (pjsip_event_hdr*)
258 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000259 if (!event) {
260 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
261 }
262 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
263 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
264 }
265
266 /* Check that request contains compatible Accept header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000267 accept = (pjsip_accept_hdr*)
268 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000269 if (accept) {
270 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +0000271 for (i=0; i<accept->count; ++i) {
272 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
273 content_type = CONTENT_TYPE_PIDF;
274 break;
275 } else
276 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
277 content_type = CONTENT_TYPE_XPIDF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000278 break;
279 }
280 }
281
Benny Prijono834aee32006-02-19 01:38:06 +0000282 if (i==accept->count) {
283 /* Nothing is acceptable */
284 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000285 }
286
Benny Prijono834aee32006-02-19 01:38:06 +0000287 } else {
288 /* No Accept header.
289 * Treat as "application/pidf+xml"
290 */
291 content_type = CONTENT_TYPE_PIDF;
292 }
293
Benny Prijono834aee32006-02-19 01:38:06 +0000294 /* Lock dialog */
295 pjsip_dlg_inc_lock(dlg);
296
297
298 /* Create server subscription */
Benny Prijono26ff9062006-02-21 23:47:00 +0000299 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000300 if (status != PJ_SUCCESS)
301 goto on_return;
302
303 /* Create server presence subscription */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000304 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000305 pres->dlg = dlg;
306 pres->sub = sub;
307 pres->content_type = content_type;
308 if (user_cb)
309 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
310
Benny Prijono28add7e2009-06-15 16:03:40 +0000311 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
312 pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
313 512, 512, NULL);
314 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
315 pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
316 512, 512, NULL);
317
Benny Prijono834aee32006-02-19 01:38:06 +0000318 /* Attach to evsub */
319 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
320
321 /* Done: */
322 *p_evsub = sub;
323
324on_return:
325 pjsip_dlg_dec_lock(dlg);
326 return status;
327}
328
329
330/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000331 * Forcefully terminate presence.
332 */
333PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
334 pj_bool_t notify )
335{
336 return pjsip_evsub_terminate(sub, notify);
337}
338
339/*
Benny Prijono834aee32006-02-19 01:38:06 +0000340 * Create SUBSCRIBE
341 */
342PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
343 pj_int32_t expires,
344 pjsip_tx_data **p_tdata)
345{
346 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
347 p_tdata);
348}
349
350
351/*
352 * Accept incoming subscription.
353 */
354PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
355 pjsip_rx_data *rdata,
356 int st_code,
357 const pjsip_hdr *hdr_list )
358{
359 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
360}
361
362
363/*
364 * Get presence status.
365 */
366PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
367 pjsip_pres_status *status )
368{
369 pjsip_pres *pres;
370
371 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
372
Benny Prijono9d4469d2007-05-02 05:14:29 +0000373 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000374 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
375
Benny Prijono28add7e2009-06-15 16:03:40 +0000376 if (pres->tmp_status._is_valid) {
377 PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
Benny Prijono834aee32006-02-19 01:38:06 +0000378 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
Benny Prijono28add7e2009-06-15 16:03:40 +0000379 } else {
380 PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
Benny Prijono834aee32006-02-19 01:38:06 +0000381 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
Benny Prijono28add7e2009-06-15 16:03:40 +0000382 }
Benny Prijono834aee32006-02-19 01:38:06 +0000383
384 return PJ_SUCCESS;
385}
386
387
388/*
389 * Set presence status.
390 */
391PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
392 const pjsip_pres_status *status )
393{
394 unsigned i;
Benny Prijono28add7e2009-06-15 16:03:40 +0000395 pj_pool_t *tmp;
Benny Prijono834aee32006-02-19 01:38:06 +0000396 pjsip_pres *pres;
397
398 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
399
Benny Prijono9d4469d2007-05-02 05:14:29 +0000400 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000401 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
402
403 for (i=0; i<status->info_cnt; ++i) {
404 pres->status.info[i].basic_open = status->info[i].basic_open;
Benny Prijono28add7e2009-06-15 16:03:40 +0000405 if (pres->status.info[i].id.slen) {
406 /* Id already set */
407 } else if (status->info[i].id.slen == 0) {
Benny Prijono834aee32006-02-19 01:38:06 +0000408 pj_create_unique_string(pres->dlg->pool,
409 &pres->status.info[i].id);
410 } else {
411 pj_strdup(pres->dlg->pool,
412 &pres->status.info[i].id,
413 &status->info[i].id);
414 }
Benny Prijono28add7e2009-06-15 16:03:40 +0000415 pj_strdup(pres->tmp_pool,
Benny Prijono834aee32006-02-19 01:38:06 +0000416 &pres->status.info[i].contact,
417 &status->info[i].contact);
Benny Prijono4461c7d2007-08-25 13:36:15 +0000418
419 /* Duplicate <person> */
420 pres->status.info[i].rpid.activity =
421 status->info[i].rpid.activity;
Benny Prijono28add7e2009-06-15 16:03:40 +0000422 pj_strdup(pres->tmp_pool,
Benny Prijono4461c7d2007-08-25 13:36:15 +0000423 &pres->status.info[i].rpid.id,
424 &status->info[i].rpid.id);
Benny Prijono28add7e2009-06-15 16:03:40 +0000425 pj_strdup(pres->tmp_pool,
Benny Prijono4461c7d2007-08-25 13:36:15 +0000426 &pres->status.info[i].rpid.note,
427 &status->info[i].rpid.note);
428
Benny Prijono834aee32006-02-19 01:38:06 +0000429 }
430
431 pres->status.info_cnt = status->info_cnt;
432
Benny Prijono28add7e2009-06-15 16:03:40 +0000433 /* Swap pools */
434 tmp = pres->tmp_pool;
435 pres->tmp_pool = pres->status_pool;
436 pres->status_pool = tmp;
437 pj_pool_reset(pres->tmp_pool);
438
Benny Prijono834aee32006-02-19 01:38:06 +0000439 return PJ_SUCCESS;
440}
441
442
443/*
Benny Prijono834aee32006-02-19 01:38:06 +0000444 * Create message body.
445 */
446static pj_status_t pres_create_msg_body( pjsip_pres *pres,
447 pjsip_tx_data *tdata)
448{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000449 pj_str_t entity;
Benny Prijono834aee32006-02-19 01:38:06 +0000450
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000451 /* Get publisher URI */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000452 entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000453 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
454 pres->dlg->local.info->uri,
455 entity.ptr, PJSIP_MAX_URL_SIZE);
456 if (entity.slen < 1)
457 return PJ_ENOMEM;
458
Benny Prijono834aee32006-02-19 01:38:06 +0000459 if (pres->content_type == CONTENT_TYPE_PIDF) {
460
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000461 return pjsip_pres_create_pidf(tdata->pool, &pres->status,
462 &entity, &tdata->msg->body);
Benny Prijono834aee32006-02-19 01:38:06 +0000463
464 } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
465
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000466 return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
467 &entity, &tdata->msg->body);
Benny Prijono834aee32006-02-19 01:38:06 +0000468
469 } else {
470 return PJSIP_SIMPLE_EBADCONTENT;
471 }
Benny Prijono834aee32006-02-19 01:38:06 +0000472}
473
474
475/*
476 * Create NOTIFY
477 */
478PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
479 pjsip_evsub_state state,
480 const pj_str_t *state_str,
481 const pj_str_t *reason,
482 pjsip_tx_data **p_tdata)
483{
484 pjsip_pres *pres;
485 pjsip_tx_data *tdata;
486 pj_status_t status;
487
488 /* Check arguments. */
489 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
490
491 /* Get the presence object. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000492 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000493 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
494
Benny Prijono75130572008-07-17 13:53:41 +0000495 /* Must have at least one presence info, unless state is
496 * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription
497 * has not been active (e.g. we're waiting for user authorization)
498 * and remote cancels the subscription.
499 */
500 PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED ||
501 pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
Benny Prijono834aee32006-02-19 01:38:06 +0000502
503
504 /* Lock object. */
505 pjsip_dlg_inc_lock(pres->dlg);
506
507 /* Create the NOTIFY request. */
508 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
509 if (status != PJ_SUCCESS)
510 goto on_return;
511
512
Benny Prijono75130572008-07-17 13:53:41 +0000513 /* Create message body to reflect the presence status.
514 * Only do this if we have presence status info to send (see above).
515 */
516 if (pres->status.info_cnt > 0) {
517 status = pres_create_msg_body( pres, tdata );
518 if (status != PJ_SUCCESS)
519 goto on_return;
520 }
Benny Prijono834aee32006-02-19 01:38:06 +0000521
522 /* Done. */
523 *p_tdata = tdata;
524
525
526on_return:
527 pjsip_dlg_dec_lock(pres->dlg);
528 return status;
529}
530
531
532/*
533 * Create NOTIFY that reflect current state.
534 */
535PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
536 pjsip_tx_data **p_tdata )
537{
538 pjsip_pres *pres;
539 pjsip_tx_data *tdata;
540 pj_status_t status;
541
542 /* Check arguments. */
543 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
544
545 /* Get the presence object. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000546 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000547 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
548
Benny Prijono75130572008-07-17 13:53:41 +0000549 /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE
550 * to refresh subscription while we're waiting for user authorization.
551 */
552 //PJ_ASSERT_RETURN(pres->status.info_cnt > 0,
553 // PJSIP_SIMPLE_ENOPRESENCEINFO);
Benny Prijono834aee32006-02-19 01:38:06 +0000554
555
556 /* Lock object. */
557 pjsip_dlg_inc_lock(pres->dlg);
558
559 /* Create the NOTIFY request. */
560 status = pjsip_evsub_current_notify( sub, &tdata);
561 if (status != PJ_SUCCESS)
562 goto on_return;
563
564
565 /* Create message body to reflect the presence status. */
Benny Prijono75130572008-07-17 13:53:41 +0000566 if (pres->status.info_cnt > 0) {
567 status = pres_create_msg_body( pres, tdata );
568 if (status != PJ_SUCCESS)
569 goto on_return;
570 }
Benny Prijono834aee32006-02-19 01:38:06 +0000571
572 /* Done. */
573 *p_tdata = tdata;
574
575
576on_return:
577 pjsip_dlg_dec_lock(pres->dlg);
578 return status;
579}
580
581
582/*
583 * Send request.
584 */
585PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
586 pjsip_tx_data *tdata )
587{
588 return pjsip_evsub_send_request(sub, tdata);
589}
590
591
592/*
593 * This callback is called by event subscription when subscription
594 * state has changed.
595 */
596static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
597{
598 pjsip_pres *pres;
599
Benny Prijono9d4469d2007-05-02 05:14:29 +0000600 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000601 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
602
603 if (pres->user_cb.on_evsub_state)
604 (*pres->user_cb.on_evsub_state)(sub, event);
Benny Prijono28add7e2009-06-15 16:03:40 +0000605
606 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
607 if (pres->status_pool) {
608 pj_pool_release(pres->status_pool);
609 pres->status_pool = NULL;
610 }
611 if (pres->tmp_pool) {
612 pj_pool_release(pres->tmp_pool);
613 pres->tmp_pool = NULL;
614 }
615 }
Benny Prijono834aee32006-02-19 01:38:06 +0000616}
617
618/*
619 * Called when transaction state has changed.
620 */
621static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
622 pjsip_event *event)
623{
624 pjsip_pres *pres;
625
Benny Prijono9d4469d2007-05-02 05:14:29 +0000626 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000627 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
628
629 if (pres->user_cb.on_tsx_state)
630 (*pres->user_cb.on_tsx_state)(sub, tsx, event);
631}
632
633
634/*
635 * Called when SUBSCRIBE is received.
636 */
637static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
638 pjsip_rx_data *rdata,
639 int *p_st_code,
640 pj_str_t **p_st_text,
641 pjsip_hdr *res_hdr,
642 pjsip_msg_body **p_body)
643{
644 pjsip_pres *pres;
645
Benny Prijono9d4469d2007-05-02 05:14:29 +0000646 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000647 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
648
649 if (pres->user_cb.on_rx_refresh) {
650 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
651 res_hdr, p_body);
652
653 } else {
654 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
655 pjsip_tx_data *tdata;
656 pj_str_t timeout = { "timeout", 7};
657 pj_status_t status;
658
659 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
660 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
661 NULL, &timeout, &tdata);
662 } else {
663 status = pjsip_pres_current_notify(sub, &tdata);
664 }
665
666 if (status == PJ_SUCCESS)
667 pjsip_pres_send_request(sub, tdata);
668 }
669}
670
Benny Prijono834aee32006-02-19 01:38:06 +0000671
672/*
Benny Prijonob0808372006-03-02 21:18:58 +0000673 * Process the content of incoming NOTIFY request and update temporary
674 * status.
675 *
676 * return PJ_SUCCESS if incoming request is acceptable. If return value
677 * is not PJ_SUCCESS, res_hdr may be added with Warning header.
Benny Prijono834aee32006-02-19 01:38:06 +0000678 */
Benny Prijonob0808372006-03-02 21:18:58 +0000679static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
680 pjsip_rx_data *rdata,
681 int *p_st_code,
682 pj_str_t **p_st_text,
683 pjsip_hdr *res_hdr)
Benny Prijono834aee32006-02-19 01:38:06 +0000684{
685 pjsip_ctype_hdr *ctype_hdr;
Benny Prijono834aee32006-02-19 01:38:06 +0000686 pj_status_t status;
687
Benny Prijonob0808372006-03-02 21:18:58 +0000688 *p_st_text = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +0000689
690 /* Check Content-Type and msg body are present. */
691 ctype_hdr = rdata->msg_info.ctype;
692
693 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
694
695 pjsip_warning_hdr *warn_hdr;
696 pj_str_t warn_text;
697
698 *p_st_code = PJSIP_SC_BAD_REQUEST;
699
700 warn_text = pj_str("Message body is not present");
701 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
702 pjsip_endpt_name(pres->dlg->endpt),
703 &warn_text);
704 pj_list_push_back(res_hdr, warn_hdr);
705
Benny Prijonob0808372006-03-02 21:18:58 +0000706 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
Benny Prijono834aee32006-02-19 01:38:06 +0000707 }
708
709 /* Parse content. */
710
711 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
712 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
713 {
Benny Prijono28add7e2009-06-15 16:03:40 +0000714 status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000715 &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000716 }
717 else
718 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
719 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
720 {
Benny Prijono28add7e2009-06-15 16:03:40 +0000721 status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000722 &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000723 }
724 else
725 {
726 status = PJSIP_SIMPLE_EBADCONTENT;
727 }
728
729 if (status != PJ_SUCCESS) {
730 /* Unsupported or bad Content-Type */
731 pjsip_accept_hdr *accept_hdr;
732 pjsip_warning_hdr *warn_hdr;
733
734 *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
735
736 /* Add Accept header */
737 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
738 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
739 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
740 pj_list_push_back(res_hdr, accept_hdr);
741
742 /* Add Warning header */
743 warn_hdr = pjsip_warning_hdr_create_from_status(
744 rdata->tp_info.pool,
745 pjsip_endpt_name(pres->dlg->endpt),
746 status);
747 pj_list_push_back(res_hdr, warn_hdr);
748
Benny Prijonob0808372006-03-02 21:18:58 +0000749 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000750 }
751
752 /* If application calls pres_get_status(), redirect the call to
753 * retrieve the temporary status.
754 */
755 pres->tmp_status._is_valid = PJ_TRUE;
756
Benny Prijonob0808372006-03-02 21:18:58 +0000757 return PJ_SUCCESS;
758}
759
760
761/*
762 * Called when NOTIFY is received.
763 */
764static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
765 pjsip_rx_data *rdata,
766 int *p_st_code,
767 pj_str_t **p_st_text,
768 pjsip_hdr *res_hdr,
769 pjsip_msg_body **p_body)
770{
771 pjsip_pres *pres;
772 pj_status_t status;
773
Benny Prijono9d4469d2007-05-02 05:14:29 +0000774 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijonob0808372006-03-02 21:18:58 +0000775 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
776
Benny Prijonob0808372006-03-02 21:18:58 +0000777 if (rdata->msg_info.msg->body) {
778 status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
779 res_hdr );
780 if (status != PJ_SUCCESS)
781 return;
782
783 } else {
Benny Prijono28add7e2009-06-15 16:03:40 +0000784#if 1
785 /* This is the newest change, http://trac.pjsip.org/repos/ticket/873
786 * Some app want to be notified about the empty NOTIFY, e.g. to
787 * decide whether it should consider the buddy as offline.
788 * In this case, leave the buddy state unchanged, but set the
789 * "tuple_node" in pjsip_pres_status to NULL.
790 */
791 unsigned i;
792 for (i=0; i<pres->status.info_cnt; ++i) {
793 pres->status.info[i].tuple_node = NULL;
794 }
795
796#elif 0
Benny Prijonoe6821552006-06-19 12:03:35 +0000797 /* This has just been changed. Previously, we treat incoming NOTIFY
798 * with no message body as having the presence subscription closed.
799 * Now we treat it as no change in presence status (ref: EyeBeam).
800 */
Benny Prijonoe6821552006-06-19 12:03:35 +0000801 *p_st_code = 200;
802 return;
803#else
Benny Prijonob0808372006-03-02 21:18:58 +0000804 unsigned i;
Benny Prijonob0808372006-03-02 21:18:58 +0000805 /* Subscription is terminated. Consider contact is offline */
806 pres->tmp_status._is_valid = PJ_TRUE;
807 for (i=0; i<pres->tmp_status.info_cnt; ++i)
808 pres->tmp_status.info[i].basic_open = PJ_FALSE;
Benny Prijonoe6821552006-06-19 12:03:35 +0000809#endif
Benny Prijonob0808372006-03-02 21:18:58 +0000810 }
811
Benny Prijono834aee32006-02-19 01:38:06 +0000812 /* Notify application. */
813 if (pres->user_cb.on_rx_notify) {
814 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
815 res_hdr, p_body);
816 }
817
818
819 /* If application responded NOTIFY with 2xx, copy temporary status
820 * to main status, and mark the temporary status as invalid.
821 */
822 if ((*p_st_code)/100 == 2) {
Benny Prijono28add7e2009-06-15 16:03:40 +0000823 pj_pool_t *tmp;
824
Benny Prijono834aee32006-02-19 01:38:06 +0000825 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
Benny Prijono28add7e2009-06-15 16:03:40 +0000826
827 /* Swap the pool */
828 tmp = pres->tmp_pool;
829 pres->tmp_pool = pres->status_pool;
830 pres->status_pool = tmp;
Benny Prijono834aee32006-02-19 01:38:06 +0000831 }
832
833 pres->tmp_status._is_valid = PJ_FALSE;
Benny Prijono28add7e2009-06-15 16:03:40 +0000834 pj_pool_reset(pres->tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +0000835
836 /* Done */
837}
838
839/*
840 * Called when it's time to send SUBSCRIBE.
841 */
842static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
843{
844 pjsip_pres *pres;
845
Benny Prijono9d4469d2007-05-02 05:14:29 +0000846 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000847 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
848
849 if (pres->user_cb.on_client_refresh) {
850 (*pres->user_cb.on_client_refresh)(sub);
851 } else {
852 pj_status_t status;
853 pjsip_tx_data *tdata;
854
855 status = pjsip_pres_initiate(sub, -1, &tdata);
856 if (status == PJ_SUCCESS)
857 pjsip_pres_send_request(sub, tdata);
858 }
859}
860
861/*
862 * Called when no refresh is received after the interval.
863 */
864static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
865{
866 pjsip_pres *pres;
867
Benny Prijono9d4469d2007-05-02 05:14:29 +0000868 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000869 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
870
871 if (pres->user_cb.on_server_timeout) {
872 (*pres->user_cb.on_server_timeout)(sub);
873 } else {
874 pj_status_t status;
875 pjsip_tx_data *tdata;
876 pj_str_t reason = { "timeout", 7 };
877
878 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
879 NULL, &reason, &tdata);
880 if (status == PJ_SUCCESS)
881 pjsip_pres_send_request(sub, tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000882 }
883}
884