blob: 59a6bc8bba7fe9d97898f4bfffe0d142074a6273 [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>
Benny Prijonof279c092010-10-12 11:35:55 +000024#include <pjsip/sip_multipart.h>
Benny Prijono834aee32006-02-19 01:38:06 +000025#include <pjsip/sip_endpoint.h>
26#include <pjsip/sip_dialog.h>
27#include <pj/assert.h>
28#include <pj/guid.h>
29#include <pj/log.h>
30#include <pj/os.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000031#include <pj/pool.h>
32#include <pj/string.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000033
Benny Prijono5dcb38d2005-11-21 01:55:47 +000034
Benny Prijono834aee32006-02-19 01:38:06 +000035#define THIS_FILE "presence.c"
Benny Prijonocf2e6732009-06-01 15:39:52 +000036#define PRES_DEFAULT_EXPIRES PJSIP_PRES_DEFAULT_EXPIRES
Benny Prijono5dcb38d2005-11-21 01:55:47 +000037
Benny Prijono1a1f51e2010-11-10 12:13:46 +000038#if PJSIP_PRES_BAD_CONTENT_RESPONSE < 200 || \
39 PJSIP_PRES_BAD_CONTENT_RESPONSE > 699 || \
40 PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 3
41# error Invalid PJSIP_PRES_BAD_CONTENT_RESPONSE value
42#endif
43
Benny Prijono5dcb38d2005-11-21 01:55:47 +000044/*
Benny Prijono834aee32006-02-19 01:38:06 +000045 * Presence module (mod-presence)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000046 */
Benny Prijono834aee32006-02-19 01:38:06 +000047static struct pjsip_module mod_presence =
Benny Prijono5dcb38d2005-11-21 01:55:47 +000048{
Benny Prijono834aee32006-02-19 01:38:06 +000049 NULL, NULL, /* prev, next. */
50 { "mod-presence", 12 }, /* Name. */
51 -1, /* Id */
Benny Prijono2f8992b2006-02-25 21:16:36 +000052 PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +000053 NULL, /* load() */
54 NULL, /* start() */
55 NULL, /* stop() */
56 NULL, /* unload() */
57 NULL, /* on_rx_request() */
58 NULL, /* on_rx_response() */
59 NULL, /* on_tx_request. */
60 NULL, /* on_tx_response() */
61 NULL, /* on_tsx_state() */
62};
63
64
65/*
66 * Presence message body type.
67 */
Benny Prijonoa1e69682007-05-11 15:14:34 +000068typedef enum content_type_e
Benny Prijono834aee32006-02-19 01:38:06 +000069{
70 CONTENT_TYPE_NONE,
71 CONTENT_TYPE_PIDF,
72 CONTENT_TYPE_XPIDF,
Benny Prijonoa1e69682007-05-11 15:14:34 +000073} content_type_e;
Benny Prijono834aee32006-02-19 01:38:06 +000074
75/*
76 * This structure describe a presentity, for both subscriber and notifier.
77 */
78struct pjsip_pres
79{
80 pjsip_evsub *sub; /**< Event subscribtion record. */
81 pjsip_dialog *dlg; /**< The dialog. */
Benny Prijonoa1e69682007-05-11 15:14:34 +000082 content_type_e content_type; /**< Content-Type. */
Benny Prijono28add7e2009-06-15 16:03:40 +000083 pj_pool_t *status_pool; /**< Pool for pres_status */
Benny Prijono834aee32006-02-19 01:38:06 +000084 pjsip_pres_status status; /**< Presence status. */
Benny Prijono28add7e2009-06-15 16:03:40 +000085 pj_pool_t *tmp_pool; /**< Pool for tmp_status */
Benny Prijono834aee32006-02-19 01:38:06 +000086 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
87 pjsip_evsub_user user_cb; /**< The user callback. */
88};
89
90
91typedef struct pjsip_pres pjsip_pres;
92
93
94/*
95 * Forward decl for evsub callback.
96 */
97static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
98static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
99 pjsip_event *event);
100static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
101 pjsip_rx_data *rdata,
102 int *p_st_code,
103 pj_str_t **p_st_text,
104 pjsip_hdr *res_hdr,
105 pjsip_msg_body **p_body);
106static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
107 pjsip_rx_data *rdata,
108 int *p_st_code,
109 pj_str_t **p_st_text,
110 pjsip_hdr *res_hdr,
111 pjsip_msg_body **p_body);
112static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
113static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
114
115
116/*
117 * Event subscription callback for presence.
118 */
119static pjsip_evsub_user pres_user =
120{
121 &pres_on_evsub_state,
122 &pres_on_evsub_tsx_state,
123 &pres_on_evsub_rx_refresh,
124 &pres_on_evsub_rx_notify,
125 &pres_on_evsub_client_refresh,
126 &pres_on_evsub_server_timeout,
127};
128
129
130/*
131 * Some static constants.
132 */
133const pj_str_t STR_EVENT = { "Event", 5 };
134const pj_str_t STR_PRESENCE = { "presence", 8 };
135const pj_str_t STR_APPLICATION = { "application", 11 };
136const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
137const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
138const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
139const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
140
141
142/*
143 * Init presence module.
144 */
145PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
146 pjsip_module *mod_evsub)
147{
148 pj_status_t status;
149 pj_str_t accept[2];
150
151 /* Check arguments. */
152 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
153
154 /* Must have not been registered */
155 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
156
157 /* Register to endpoint */
158 status = pjsip_endpt_register_module(endpt, &mod_presence);
159 if (status != PJ_SUCCESS)
160 return status;
161
162 accept[0] = STR_APP_PIDF_XML;
163 accept[1] = STR_APP_XPIDF_XML;
164
165 /* Register event package to event module. */
166 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
167 PRES_DEFAULT_EXPIRES,
168 PJ_ARRAY_SIZE(accept), accept);
169 if (status != PJ_SUCCESS) {
170 pjsip_endpt_unregister_module(endpt, &mod_presence);
171 return status;
172 }
173
174 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000175}
176
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000177
Benny Prijono834aee32006-02-19 01:38:06 +0000178/*
179 * Get presence module instance.
180 */
181PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
182{
183 return &mod_presence;
184}
185
186
187/*
188 * Create client subscription.
189 */
190PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
191 const pjsip_evsub_user *user_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000192 unsigned options,
Benny Prijono834aee32006-02-19 01:38:06 +0000193 pjsip_evsub **p_evsub )
194{
195 pj_status_t status;
196 pjsip_pres *pres;
Benny Prijono28add7e2009-06-15 16:03:40 +0000197 char obj_name[PJ_MAX_OBJ_NAME];
Benny Prijono834aee32006-02-19 01:38:06 +0000198 pjsip_evsub *sub;
199
200 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
201
202 pjsip_dlg_inc_lock(dlg);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000203
204 /* Create event subscription */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000205 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE,
206 options, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000207 if (status != PJ_SUCCESS)
208 goto on_return;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000209
Benny Prijono834aee32006-02-19 01:38:06 +0000210 /* Create presence */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000211 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000212 pres->dlg = dlg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000213 pres->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000214 if (user_cb)
215 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000216
Benny Prijono28add7e2009-06-15 16:03:40 +0000217 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
218 pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
219 512, 512, NULL);
220 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
221 pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
222 512, 512, NULL);
223
Benny Prijono834aee32006-02-19 01:38:06 +0000224 /* Attach to evsub */
225 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
226
227 *p_evsub = sub;
228
229on_return:
230 pjsip_dlg_dec_lock(dlg);
231 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000232}
233
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000234
235/*
Benny Prijono834aee32006-02-19 01:38:06 +0000236 * Create server subscription.
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000237 */
Benny Prijono834aee32006-02-19 01:38:06 +0000238PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
239 const pjsip_evsub_user *user_cb,
240 pjsip_rx_data *rdata,
241 pjsip_evsub **p_evsub )
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000242{
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000243 pjsip_accept_hdr *accept;
Benny Prijono834aee32006-02-19 01:38:06 +0000244 pjsip_event_hdr *event;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000245 content_type_e content_type = CONTENT_TYPE_NONE;
Benny Prijono834aee32006-02-19 01:38:06 +0000246 pjsip_evsub *sub;
247 pjsip_pres *pres;
Benny Prijono28add7e2009-06-15 16:03:40 +0000248 char obj_name[PJ_MAX_OBJ_NAME];
Benny Prijono834aee32006-02-19 01:38:06 +0000249 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000250
Benny Prijono834aee32006-02-19 01:38:06 +0000251 /* Check arguments */
252 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000253
Benny Prijono834aee32006-02-19 01:38:06 +0000254 /* Must be request message */
255 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
256 PJSIP_ENOTREQUESTMSG);
257
258 /* Check that request is SUBSCRIBE */
259 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
260 &pjsip_subscribe_method)==0,
261 PJSIP_SIMPLE_ENOTSUBSCRIBE);
262
263 /* Check that Event header contains "presence" */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000264 event = (pjsip_event_hdr*)
265 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000266 if (!event) {
267 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
268 }
269 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
270 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
271 }
272
273 /* Check that request contains compatible Accept header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000274 accept = (pjsip_accept_hdr*)
275 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000276 if (accept) {
277 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +0000278 for (i=0; i<accept->count; ++i) {
279 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
280 content_type = CONTENT_TYPE_PIDF;
281 break;
282 } else
283 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
284 content_type = CONTENT_TYPE_XPIDF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000285 break;
286 }
287 }
288
Benny Prijono834aee32006-02-19 01:38:06 +0000289 if (i==accept->count) {
290 /* Nothing is acceptable */
291 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000292 }
293
Benny Prijono834aee32006-02-19 01:38:06 +0000294 } else {
295 /* No Accept header.
296 * Treat as "application/pidf+xml"
297 */
298 content_type = CONTENT_TYPE_PIDF;
299 }
300
Benny Prijono834aee32006-02-19 01:38:06 +0000301 /* Lock dialog */
302 pjsip_dlg_inc_lock(dlg);
303
304
305 /* Create server subscription */
Benny Prijono26ff9062006-02-21 23:47:00 +0000306 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000307 if (status != PJ_SUCCESS)
308 goto on_return;
309
310 /* Create server presence subscription */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000311 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000312 pres->dlg = dlg;
313 pres->sub = sub;
314 pres->content_type = content_type;
315 if (user_cb)
316 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
317
Benny Prijono28add7e2009-06-15 16:03:40 +0000318 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
319 pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
320 512, 512, NULL);
321 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
322 pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
323 512, 512, NULL);
324
Benny Prijono834aee32006-02-19 01:38:06 +0000325 /* Attach to evsub */
326 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
327
328 /* Done: */
329 *p_evsub = sub;
330
331on_return:
332 pjsip_dlg_dec_lock(dlg);
333 return status;
334}
335
336
337/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000338 * Forcefully terminate presence.
339 */
340PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
341 pj_bool_t notify )
342{
343 return pjsip_evsub_terminate(sub, notify);
344}
345
346/*
Benny Prijono834aee32006-02-19 01:38:06 +0000347 * Create SUBSCRIBE
348 */
349PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
350 pj_int32_t expires,
351 pjsip_tx_data **p_tdata)
352{
353 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
354 p_tdata);
355}
356
357
358/*
359 * Accept incoming subscription.
360 */
361PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
362 pjsip_rx_data *rdata,
363 int st_code,
364 const pjsip_hdr *hdr_list )
365{
366 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
367}
368
369
370/*
371 * Get presence status.
372 */
373PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
374 pjsip_pres_status *status )
375{
376 pjsip_pres *pres;
377
378 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
379
Benny Prijono9d4469d2007-05-02 05:14:29 +0000380 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000381 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
382
Benny Prijono28add7e2009-06-15 16:03:40 +0000383 if (pres->tmp_status._is_valid) {
384 PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
Benny Prijono834aee32006-02-19 01:38:06 +0000385 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
Benny Prijono28add7e2009-06-15 16:03:40 +0000386 } else {
387 PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
Benny Prijono834aee32006-02-19 01:38:06 +0000388 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
Benny Prijono28add7e2009-06-15 16:03:40 +0000389 }
Benny Prijono834aee32006-02-19 01:38:06 +0000390
391 return PJ_SUCCESS;
392}
393
394
395/*
396 * Set presence status.
397 */
398PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
399 const pjsip_pres_status *status )
400{
401 unsigned i;
Benny Prijono28add7e2009-06-15 16:03:40 +0000402 pj_pool_t *tmp;
Benny Prijono834aee32006-02-19 01:38:06 +0000403 pjsip_pres *pres;
404
405 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
406
Benny Prijono9d4469d2007-05-02 05:14:29 +0000407 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000408 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
409
410 for (i=0; i<status->info_cnt; ++i) {
411 pres->status.info[i].basic_open = status->info[i].basic_open;
Benny Prijono28add7e2009-06-15 16:03:40 +0000412 if (pres->status.info[i].id.slen) {
413 /* Id already set */
414 } else if (status->info[i].id.slen == 0) {
Benny Prijono834aee32006-02-19 01:38:06 +0000415 pj_create_unique_string(pres->dlg->pool,
416 &pres->status.info[i].id);
417 } else {
418 pj_strdup(pres->dlg->pool,
419 &pres->status.info[i].id,
420 &status->info[i].id);
421 }
Benny Prijono28add7e2009-06-15 16:03:40 +0000422 pj_strdup(pres->tmp_pool,
Benny Prijono834aee32006-02-19 01:38:06 +0000423 &pres->status.info[i].contact,
424 &status->info[i].contact);
Benny Prijono4461c7d2007-08-25 13:36:15 +0000425
426 /* Duplicate <person> */
427 pres->status.info[i].rpid.activity =
428 status->info[i].rpid.activity;
Benny Prijono28add7e2009-06-15 16:03:40 +0000429 pj_strdup(pres->tmp_pool,
Benny Prijono4461c7d2007-08-25 13:36:15 +0000430 &pres->status.info[i].rpid.id,
431 &status->info[i].rpid.id);
Benny Prijono28add7e2009-06-15 16:03:40 +0000432 pj_strdup(pres->tmp_pool,
Benny Prijono4461c7d2007-08-25 13:36:15 +0000433 &pres->status.info[i].rpid.note,
434 &status->info[i].rpid.note);
435
Benny Prijono834aee32006-02-19 01:38:06 +0000436 }
437
438 pres->status.info_cnt = status->info_cnt;
439
Benny Prijono28add7e2009-06-15 16:03:40 +0000440 /* Swap pools */
441 tmp = pres->tmp_pool;
442 pres->tmp_pool = pres->status_pool;
443 pres->status_pool = tmp;
444 pj_pool_reset(pres->tmp_pool);
445
Benny Prijono834aee32006-02-19 01:38:06 +0000446 return PJ_SUCCESS;
447}
448
449
450/*
Benny Prijono834aee32006-02-19 01:38:06 +0000451 * Create message body.
452 */
453static pj_status_t pres_create_msg_body( pjsip_pres *pres,
454 pjsip_tx_data *tdata)
455{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000456 pj_str_t entity;
Benny Prijono834aee32006-02-19 01:38:06 +0000457
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000458 /* Get publisher URI */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000459 entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000460 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
461 pres->dlg->local.info->uri,
462 entity.ptr, PJSIP_MAX_URL_SIZE);
463 if (entity.slen < 1)
464 return PJ_ENOMEM;
465
Benny Prijono834aee32006-02-19 01:38:06 +0000466 if (pres->content_type == CONTENT_TYPE_PIDF) {
467
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000468 return pjsip_pres_create_pidf(tdata->pool, &pres->status,
469 &entity, &tdata->msg->body);
Benny Prijono834aee32006-02-19 01:38:06 +0000470
471 } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
472
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000473 return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
474 &entity, &tdata->msg->body);
Benny Prijono834aee32006-02-19 01:38:06 +0000475
476 } else {
477 return PJSIP_SIMPLE_EBADCONTENT;
478 }
Benny Prijono834aee32006-02-19 01:38:06 +0000479}
480
481
482/*
483 * Create NOTIFY
484 */
485PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
486 pjsip_evsub_state state,
487 const pj_str_t *state_str,
488 const pj_str_t *reason,
489 pjsip_tx_data **p_tdata)
490{
491 pjsip_pres *pres;
492 pjsip_tx_data *tdata;
493 pj_status_t status;
494
495 /* Check arguments. */
496 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
497
498 /* Get the presence object. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000499 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000500 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
501
Benny Prijono75130572008-07-17 13:53:41 +0000502 /* Must have at least one presence info, unless state is
503 * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription
504 * has not been active (e.g. we're waiting for user authorization)
505 * and remote cancels the subscription.
506 */
507 PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED ||
508 pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
Benny Prijono834aee32006-02-19 01:38:06 +0000509
510
511 /* Lock object. */
512 pjsip_dlg_inc_lock(pres->dlg);
513
514 /* Create the NOTIFY request. */
515 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
516 if (status != PJ_SUCCESS)
517 goto on_return;
518
519
Benny Prijono75130572008-07-17 13:53:41 +0000520 /* Create message body to reflect the presence status.
521 * Only do this if we have presence status info to send (see above).
522 */
523 if (pres->status.info_cnt > 0) {
524 status = pres_create_msg_body( pres, tdata );
525 if (status != PJ_SUCCESS)
526 goto on_return;
527 }
Benny Prijono834aee32006-02-19 01:38:06 +0000528
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 * Create NOTIFY that reflect current state.
541 */
542PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
543 pjsip_tx_data **p_tdata )
544{
545 pjsip_pres *pres;
546 pjsip_tx_data *tdata;
547 pj_status_t status;
548
549 /* Check arguments. */
550 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
551
552 /* Get the presence object. */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000553 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000554 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
555
Benny Prijono75130572008-07-17 13:53:41 +0000556 /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE
557 * to refresh subscription while we're waiting for user authorization.
558 */
559 //PJ_ASSERT_RETURN(pres->status.info_cnt > 0,
560 // PJSIP_SIMPLE_ENOPRESENCEINFO);
Benny Prijono834aee32006-02-19 01:38:06 +0000561
562
563 /* Lock object. */
564 pjsip_dlg_inc_lock(pres->dlg);
565
566 /* Create the NOTIFY request. */
567 status = pjsip_evsub_current_notify( sub, &tdata);
568 if (status != PJ_SUCCESS)
569 goto on_return;
570
571
572 /* Create message body to reflect the presence status. */
Benny Prijono75130572008-07-17 13:53:41 +0000573 if (pres->status.info_cnt > 0) {
574 status = pres_create_msg_body( pres, tdata );
575 if (status != PJ_SUCCESS)
576 goto on_return;
577 }
Benny Prijono834aee32006-02-19 01:38:06 +0000578
579 /* Done. */
580 *p_tdata = tdata;
581
582
583on_return:
584 pjsip_dlg_dec_lock(pres->dlg);
585 return status;
586}
587
588
589/*
590 * Send request.
591 */
592PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
593 pjsip_tx_data *tdata )
594{
595 return pjsip_evsub_send_request(sub, tdata);
596}
597
598
599/*
600 * This callback is called by event subscription when subscription
601 * state has changed.
602 */
603static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
604{
605 pjsip_pres *pres;
606
Benny Prijono9d4469d2007-05-02 05:14:29 +0000607 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000608 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
609
610 if (pres->user_cb.on_evsub_state)
611 (*pres->user_cb.on_evsub_state)(sub, event);
Benny Prijono28add7e2009-06-15 16:03:40 +0000612
613 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
614 if (pres->status_pool) {
615 pj_pool_release(pres->status_pool);
616 pres->status_pool = NULL;
617 }
618 if (pres->tmp_pool) {
619 pj_pool_release(pres->tmp_pool);
620 pres->tmp_pool = NULL;
621 }
622 }
Benny Prijono834aee32006-02-19 01:38:06 +0000623}
624
625/*
626 * Called when transaction state has changed.
627 */
628static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
629 pjsip_event *event)
630{
631 pjsip_pres *pres;
632
Benny Prijono9d4469d2007-05-02 05:14:29 +0000633 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000634 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
635
636 if (pres->user_cb.on_tsx_state)
637 (*pres->user_cb.on_tsx_state)(sub, tsx, event);
638}
639
640
641/*
642 * Called when SUBSCRIBE is received.
643 */
644static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
645 pjsip_rx_data *rdata,
646 int *p_st_code,
647 pj_str_t **p_st_text,
648 pjsip_hdr *res_hdr,
649 pjsip_msg_body **p_body)
650{
651 pjsip_pres *pres;
652
Benny Prijono9d4469d2007-05-02 05:14:29 +0000653 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000654 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
655
656 if (pres->user_cb.on_rx_refresh) {
657 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
658 res_hdr, p_body);
659
660 } else {
661 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
662 pjsip_tx_data *tdata;
663 pj_str_t timeout = { "timeout", 7};
664 pj_status_t status;
665
666 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
667 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
668 NULL, &timeout, &tdata);
669 } else {
670 status = pjsip_pres_current_notify(sub, &tdata);
671 }
672
673 if (status == PJ_SUCCESS)
674 pjsip_pres_send_request(sub, tdata);
675 }
676}
677
Benny Prijono834aee32006-02-19 01:38:06 +0000678
679/*
Benny Prijonob0808372006-03-02 21:18:58 +0000680 * Process the content of incoming NOTIFY request and update temporary
681 * status.
682 *
683 * return PJ_SUCCESS if incoming request is acceptable. If return value
684 * is not PJ_SUCCESS, res_hdr may be added with Warning header.
Benny Prijono834aee32006-02-19 01:38:06 +0000685 */
Benny Prijonob0808372006-03-02 21:18:58 +0000686static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
687 pjsip_rx_data *rdata,
688 int *p_st_code,
689 pj_str_t **p_st_text,
690 pjsip_hdr *res_hdr)
Benny Prijono834aee32006-02-19 01:38:06 +0000691{
Benny Prijonof279c092010-10-12 11:35:55 +0000692 const pj_str_t STR_MULTIPART = { "multipart", 9 };
Benny Prijono834aee32006-02-19 01:38:06 +0000693 pjsip_ctype_hdr *ctype_hdr;
Benny Prijono834aee32006-02-19 01:38:06 +0000694 pj_status_t status;
695
Benny Prijonob0808372006-03-02 21:18:58 +0000696 *p_st_text = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +0000697
698 /* Check Content-Type and msg body are present. */
699 ctype_hdr = rdata->msg_info.ctype;
700
701 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
702
703 pjsip_warning_hdr *warn_hdr;
704 pj_str_t warn_text;
705
706 *p_st_code = PJSIP_SC_BAD_REQUEST;
707
708 warn_text = pj_str("Message body is not present");
709 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
710 pjsip_endpt_name(pres->dlg->endpt),
711 &warn_text);
712 pj_list_push_back(res_hdr, warn_hdr);
713
Benny Prijonob0808372006-03-02 21:18:58 +0000714 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
Benny Prijono834aee32006-02-19 01:38:06 +0000715 }
716
717 /* Parse content. */
Benny Prijonof279c092010-10-12 11:35:55 +0000718 if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) {
719 pjsip_multipart_part *mpart;
720 pjsip_media_type ctype;
Benny Prijono834aee32006-02-19 01:38:06 +0000721
Benny Prijonof279c092010-10-12 11:35:55 +0000722 pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
723 (pj_str_t*)&STR_PIDF_XML);
724 mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
725 &ctype, NULL);
726 if (mpart) {
727 status = pjsip_pres_parse_pidf2((char*)mpart->body->data,
728 mpart->body->len, pres->tmp_pool,
729 &pres->tmp_status);
730 }
731
732 if (mpart==NULL) {
733 pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
734 (pj_str_t*)&STR_XPIDF_XML);
735 mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
736 &ctype, NULL);
737 if (mpart) {
738 status = pjsip_pres_parse_xpidf2((char*)mpart->body->data,
739 mpart->body->len,
740 pres->tmp_pool,
741 &pres->tmp_status);
742 } else {
743 status = PJSIP_SIMPLE_EBADCONTENT;
744 }
745 }
746 }
747 else
Benny Prijono834aee32006-02-19 01:38:06 +0000748 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
749 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
750 {
Benny Prijono28add7e2009-06-15 16:03:40 +0000751 status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000752 &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000753 }
754 else
755 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
756 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
757 {
Benny Prijono28add7e2009-06-15 16:03:40 +0000758 status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000759 &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000760 }
761 else
762 {
763 status = PJSIP_SIMPLE_EBADCONTENT;
764 }
765
766 if (status != PJ_SUCCESS) {
767 /* Unsupported or bad Content-Type */
Benny Prijono1a1f51e2010-11-10 12:13:46 +0000768 if (PJSIP_PRES_BAD_CONTENT_RESPONSE >= 300) {
769 pjsip_accept_hdr *accept_hdr;
770 pjsip_warning_hdr *warn_hdr;
Benny Prijono834aee32006-02-19 01:38:06 +0000771
Benny Prijono1a1f51e2010-11-10 12:13:46 +0000772 *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
Benny Prijono834aee32006-02-19 01:38:06 +0000773
Benny Prijono1a1f51e2010-11-10 12:13:46 +0000774 /* Add Accept header */
775 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
776 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
777 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
778 pj_list_push_back(res_hdr, accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000779
Benny Prijono1a1f51e2010-11-10 12:13:46 +0000780 /* Add Warning header */
781 warn_hdr = pjsip_warning_hdr_create_from_status(
782 rdata->tp_info.pool,
783 pjsip_endpt_name(pres->dlg->endpt),
784 status);
785 pj_list_push_back(res_hdr, warn_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000786
Benny Prijono1a1f51e2010-11-10 12:13:46 +0000787 return status;
788 } else {
789 pj_assert(PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 2);
790 PJ_PERROR(4,(THIS_FILE, status,
791 "Ignoring presence error due to "
792 "PJSIP_PRES_BAD_CONTENT_RESPONSE setting [%d]",
793 PJSIP_PRES_BAD_CONTENT_RESPONSE));
794 *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
795 status = PJ_SUCCESS;
796 }
Benny Prijono834aee32006-02-19 01:38:06 +0000797 }
798
799 /* If application calls pres_get_status(), redirect the call to
800 * retrieve the temporary status.
801 */
802 pres->tmp_status._is_valid = PJ_TRUE;
803
Benny Prijonob0808372006-03-02 21:18:58 +0000804 return PJ_SUCCESS;
805}
806
807
808/*
809 * Called when NOTIFY is received.
810 */
811static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
812 pjsip_rx_data *rdata,
813 int *p_st_code,
814 pj_str_t **p_st_text,
815 pjsip_hdr *res_hdr,
816 pjsip_msg_body **p_body)
817{
818 pjsip_pres *pres;
819 pj_status_t status;
820
Benny Prijono9d4469d2007-05-02 05:14:29 +0000821 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijonob0808372006-03-02 21:18:58 +0000822 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
823
Benny Prijonob0808372006-03-02 21:18:58 +0000824 if (rdata->msg_info.msg->body) {
825 status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
826 res_hdr );
827 if (status != PJ_SUCCESS)
828 return;
829
830 } else {
Benny Prijono28add7e2009-06-15 16:03:40 +0000831#if 1
832 /* This is the newest change, http://trac.pjsip.org/repos/ticket/873
833 * Some app want to be notified about the empty NOTIFY, e.g. to
834 * decide whether it should consider the buddy as offline.
835 * In this case, leave the buddy state unchanged, but set the
836 * "tuple_node" in pjsip_pres_status to NULL.
837 */
838 unsigned i;
839 for (i=0; i<pres->status.info_cnt; ++i) {
840 pres->status.info[i].tuple_node = NULL;
841 }
842
843#elif 0
Benny Prijonoe6821552006-06-19 12:03:35 +0000844 /* This has just been changed. Previously, we treat incoming NOTIFY
845 * with no message body as having the presence subscription closed.
846 * Now we treat it as no change in presence status (ref: EyeBeam).
847 */
Benny Prijonoe6821552006-06-19 12:03:35 +0000848 *p_st_code = 200;
849 return;
850#else
Benny Prijonob0808372006-03-02 21:18:58 +0000851 unsigned i;
Benny Prijonob0808372006-03-02 21:18:58 +0000852 /* Subscription is terminated. Consider contact is offline */
853 pres->tmp_status._is_valid = PJ_TRUE;
854 for (i=0; i<pres->tmp_status.info_cnt; ++i)
855 pres->tmp_status.info[i].basic_open = PJ_FALSE;
Benny Prijonoe6821552006-06-19 12:03:35 +0000856#endif
Benny Prijonob0808372006-03-02 21:18:58 +0000857 }
858
Benny Prijono834aee32006-02-19 01:38:06 +0000859 /* Notify application. */
860 if (pres->user_cb.on_rx_notify) {
861 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
862 res_hdr, p_body);
863 }
864
865
866 /* If application responded NOTIFY with 2xx, copy temporary status
867 * to main status, and mark the temporary status as invalid.
868 */
869 if ((*p_st_code)/100 == 2) {
Benny Prijono28add7e2009-06-15 16:03:40 +0000870 pj_pool_t *tmp;
871
Benny Prijono834aee32006-02-19 01:38:06 +0000872 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
Benny Prijono28add7e2009-06-15 16:03:40 +0000873
874 /* Swap the pool */
875 tmp = pres->tmp_pool;
876 pres->tmp_pool = pres->status_pool;
877 pres->status_pool = tmp;
Benny Prijono834aee32006-02-19 01:38:06 +0000878 }
879
880 pres->tmp_status._is_valid = PJ_FALSE;
Benny Prijono28add7e2009-06-15 16:03:40 +0000881 pj_pool_reset(pres->tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +0000882
883 /* Done */
884}
885
886/*
887 * Called when it's time to send SUBSCRIBE.
888 */
889static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
890{
891 pjsip_pres *pres;
892
Benny Prijono9d4469d2007-05-02 05:14:29 +0000893 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000894 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
895
896 if (pres->user_cb.on_client_refresh) {
897 (*pres->user_cb.on_client_refresh)(sub);
898 } else {
899 pj_status_t status;
900 pjsip_tx_data *tdata;
901
902 status = pjsip_pres_initiate(sub, -1, &tdata);
903 if (status == PJ_SUCCESS)
904 pjsip_pres_send_request(sub, tdata);
905 }
906}
907
908/*
909 * Called when no refresh is received after the interval.
910 */
911static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
912{
913 pjsip_pres *pres;
914
Benny Prijono9d4469d2007-05-02 05:14:29 +0000915 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000916 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
917
918 if (pres->user_cb.on_server_timeout) {
919 (*pres->user_cb.on_server_timeout)(sub);
920 } else {
921 pj_status_t status;
922 pjsip_tx_data *tdata;
923 pj_str_t reason = { "timeout", 7 };
924
925 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
926 NULL, &reason, &tdata);
927 if (status == PJ_SUCCESS)
928 pjsip_pres_send_request(sub, tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000929 }
930}
931