1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pjsip-simple/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_multipart.h>
25 #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>
31 #include <pj/pool.h>
32 #include <pj/string.h>
33 
34 
35 #define THIS_FILE		    "presence.c"
36 #define PRES_DEFAULT_EXPIRES	    PJSIP_PRES_DEFAULT_EXPIRES
37 
38 #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 
44 /*
45  * Presence module (mod-presence)
46  */
47 static struct pjsip_module mod_presence =
48 {
49     NULL, NULL,			    /* prev, next.			*/
50     { "mod-presence", 12 },	    /* Name.				*/
51     -1,				    /* Id				*/
52     PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority				*/
53     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  */
68 typedef enum content_type_e
69 {
70     CONTENT_TYPE_NONE,
71     CONTENT_TYPE_PIDF,
72     CONTENT_TYPE_XPIDF,
73 } content_type_e;
74 
75 /*
76  * This structure describe a presentity, for both subscriber and notifier.
77  */
78 struct pjsip_pres
79 {
80     pjsip_evsub		*sub;		/**< Event subscribtion record.	    */
81     pjsip_dialog	*dlg;		/**< The dialog.		    */
82     content_type_e	 content_type;	/**< Content-Type.		    */
83     pj_pool_t		*status_pool;	/**< Pool for pres_status	    */
84     pjsip_pres_status	 status;	/**< Presence status.		    */
85     pj_pool_t		*tmp_pool;	/**< Pool for tmp_status	    */
86     pjsip_pres_status	 tmp_status;	/**< Temp, before NOTIFY is answred.*/
87     pjsip_evsub_user	 user_cb;	/**< The user callback.		    */
88 };
89 
90 
91 typedef struct pjsip_pres pjsip_pres;
92 
93 
94 /*
95  * Forward decl for evsub callback.
96  */
97 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
98 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
99 				     pjsip_event *event);
100 static 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);
106 static 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);
112 static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
113 static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
114 
115 
116 /*
117  * Event subscription callback for presence.
118  */
119 static 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  */
133 static const pj_str_t STR_EVENT	    = { "Event", 5 };
134 static const pj_str_t STR_PRESENCE	    = { "presence", 8 };
135 static const pj_str_t STR_APPLICATION	    = { "application", 11 };
136 static const pj_str_t STR_PIDF_XML	    = { "pidf+xml", 8};
137 static const pj_str_t STR_XPIDF_XML	    = { "xpidf+xml", 9};
138 static const pj_str_t STR_APP_PIDF_XML	    = { "application/pidf+xml", 20 };
139 static const pj_str_t STR_APP_XPIDF_XML    = { "application/xpidf+xml", 21 };
140 
141 
142 /*
143  * Init presence module.
144  */
pjsip_pres_init_module(pjsip_endpoint * endpt,pjsip_module * mod_evsub)145 PJ_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;
175 }
176 
177 
178 /*
179  * Get presence module instance.
180  */
pjsip_pres_instance(void)181 PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
182 {
183     return &mod_presence;
184 }
185 
186 
187 /*
188  * Create client subscription.
189  */
pjsip_pres_create_uac(pjsip_dialog * dlg,const pjsip_evsub_user * user_cb,unsigned options,pjsip_evsub ** p_evsub)190 PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
191 					   const pjsip_evsub_user *user_cb,
192 					   unsigned options,
193 					   pjsip_evsub **p_evsub )
194 {
195     pj_status_t status;
196     pjsip_pres *pres;
197     char obj_name[PJ_MAX_OBJ_NAME];
198     pjsip_evsub *sub;
199 
200     PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
201 
202     pjsip_dlg_inc_lock(dlg);
203 
204     /* Create event subscription */
205     status = pjsip_evsub_create_uac( dlg,  &pres_user, &STR_PRESENCE,
206 				     options, &sub);
207     if (status != PJ_SUCCESS)
208 	goto on_return;
209 
210     /* Create presence */
211     pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
212     pres->dlg = dlg;
213     pres->sub = sub;
214     if (user_cb)
215 	pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
216 
217     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 
224     /* Attach to evsub */
225     pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
226 
227     *p_evsub = sub;
228 
229 on_return:
230     pjsip_dlg_dec_lock(dlg);
231     return status;
232 }
233 
234 
235 /*
236  * Create server subscription.
237  */
pjsip_pres_create_uas(pjsip_dialog * dlg,const pjsip_evsub_user * user_cb,pjsip_rx_data * rdata,pjsip_evsub ** p_evsub)238 PJ_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 )
242 {
243     pjsip_accept_hdr *accept;
244     pjsip_event_hdr *event;
245     content_type_e content_type = CONTENT_TYPE_NONE;
246     pjsip_evsub *sub;
247     pjsip_pres *pres;
248     char obj_name[PJ_MAX_OBJ_NAME];
249     pj_status_t status;
250 
251     /* Check arguments */
252     PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
253 
254     /* 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" */
264     event = (pjsip_event_hdr*)
265     	    pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
266     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. */
274     accept = (pjsip_accept_hdr*)
275     	     pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
276     if (accept) {
277 	unsigned i;
278 	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;
285 		break;
286 	    }
287 	}
288 
289 	if (i==accept->count) {
290 	    /* Nothing is acceptable */
291 	    return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
292 	}
293 
294     } else {
295 	/* No Accept header.
296 	 * Treat as "application/pidf+xml"
297 	 */
298 	content_type = CONTENT_TYPE_PIDF;
299     }
300 
301     /* Lock dialog */
302     pjsip_dlg_inc_lock(dlg);
303 
304 
305     /* Create server subscription */
306     status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
307     if (status != PJ_SUCCESS)
308 	goto on_return;
309 
310     /* Create server presence subscription */
311     pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
312     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 
318     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 
325     /* Attach to evsub */
326     pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
327 
328     /* Done: */
329     *p_evsub = sub;
330 
331 on_return:
332     pjsip_dlg_dec_lock(dlg);
333     return status;
334 }
335 
336 
337 /*
338  * Forcefully terminate presence.
339  */
pjsip_pres_terminate(pjsip_evsub * sub,pj_bool_t notify)340 PJ_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 /*
347  * Create SUBSCRIBE
348  */
pjsip_pres_initiate(pjsip_evsub * sub,pj_uint32_t expires,pjsip_tx_data ** p_tdata)349 PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
350 					 pj_uint32_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  * Add custom headers.
360  */
pjsip_pres_add_header(pjsip_evsub * sub,const pjsip_hdr * hdr_list)361 PJ_DEF(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub,
362 					   const pjsip_hdr *hdr_list )
363 {
364     return pjsip_evsub_add_header( sub, hdr_list );
365 }
366 
367 
368 /*
369  * Accept incoming subscription.
370  */
pjsip_pres_accept(pjsip_evsub * sub,pjsip_rx_data * rdata,int st_code,const pjsip_hdr * hdr_list)371 PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
372 				       pjsip_rx_data *rdata,
373 				       int st_code,
374 				       const pjsip_hdr *hdr_list )
375 {
376     return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
377 }
378 
379 
380 /*
381  * Get presence status.
382  */
pjsip_pres_get_status(pjsip_evsub * sub,pjsip_pres_status * status)383 PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
384 					   pjsip_pres_status *status )
385 {
386     pjsip_pres *pres;
387 
388     PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
389 
390     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
391     PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
392 
393     if (pres->tmp_status._is_valid) {
394 	PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
395 	pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
396     } else {
397 	PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
398 	pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
399     }
400 
401     return PJ_SUCCESS;
402 }
403 
404 
405 /*
406  * Set presence status.
407  */
pjsip_pres_set_status(pjsip_evsub * sub,const pjsip_pres_status * status)408 PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
409 					   const pjsip_pres_status *status )
410 {
411     unsigned i;
412     pj_pool_t *tmp;
413     pjsip_pres *pres;
414 
415     PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
416 
417     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
418     PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
419 
420     for (i=0; i<status->info_cnt; ++i) {
421 	pres->status.info[i].basic_open = status->info[i].basic_open;
422 	if (pres->status.info[i].id.slen) {
423 	    /* Id already set */
424 	} else if (status->info[i].id.slen == 0) {
425 	    pj_create_unique_string(pres->dlg->pool,
426 	    			    &pres->status.info[i].id);
427 	} else {
428 	    pj_strdup(pres->dlg->pool,
429 		      &pres->status.info[i].id,
430 		      &status->info[i].id);
431 	}
432 	pj_strdup(pres->tmp_pool,
433 		  &pres->status.info[i].contact,
434 		  &status->info[i].contact);
435 
436 	/* Duplicate <person> */
437 	pres->status.info[i].rpid.activity =
438 	    status->info[i].rpid.activity;
439 	pj_strdup(pres->tmp_pool,
440 		  &pres->status.info[i].rpid.id,
441 		  &status->info[i].rpid.id);
442 	pj_strdup(pres->tmp_pool,
443 		  &pres->status.info[i].rpid.note,
444 		  &status->info[i].rpid.note);
445 
446     }
447 
448     pres->status.info_cnt = status->info_cnt;
449 
450     /* Swap pools */
451     tmp = pres->tmp_pool;
452     pres->tmp_pool = pres->status_pool;
453     pres->status_pool = tmp;
454     pj_pool_reset(pres->tmp_pool);
455 
456     return PJ_SUCCESS;
457 }
458 
459 
460 /*
461  * Create message body.
462  */
pres_create_msg_body(pjsip_pres * pres,pjsip_tx_data * tdata)463 static pj_status_t pres_create_msg_body( pjsip_pres *pres,
464 					 pjsip_tx_data *tdata)
465 {
466     pj_str_t entity;
467 
468     /* Get publisher URI */
469     entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
470     entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
471 				  pres->dlg->local.info->uri,
472 				  entity.ptr, PJSIP_MAX_URL_SIZE);
473     if (entity.slen < 1)
474 	return PJ_ENOMEM;
475 
476     if (pres->content_type == CONTENT_TYPE_PIDF) {
477 
478 	return pjsip_pres_create_pidf(tdata->pool, &pres->status,
479 				      &entity, &tdata->msg->body);
480 
481     } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
482 
483 	return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
484 				       &entity, &tdata->msg->body);
485 
486     } else {
487 	return PJSIP_SIMPLE_EBADCONTENT;
488     }
489 }
490 
491 
492 /*
493  * Create NOTIFY
494  */
pjsip_pres_notify(pjsip_evsub * sub,pjsip_evsub_state state,const pj_str_t * state_str,const pj_str_t * reason,pjsip_tx_data ** p_tdata)495 PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
496 				       pjsip_evsub_state state,
497 				       const pj_str_t *state_str,
498 				       const pj_str_t *reason,
499 				       pjsip_tx_data **p_tdata)
500 {
501     pjsip_pres *pres;
502     pjsip_tx_data *tdata;
503     pj_status_t status;
504 
505     /* Check arguments. */
506     PJ_ASSERT_RETURN(sub, PJ_EINVAL);
507 
508     /* Get the presence object. */
509     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
510     PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
511 
512     /* Must have at least one presence info, unless state is
513      * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription
514      * has not been active (e.g. we're waiting for user authorization)
515      * and remote cancels the subscription.
516      */
517     PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED ||
518 		     pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
519 
520 
521     /* Lock object. */
522     pjsip_dlg_inc_lock(pres->dlg);
523 
524     /* Create the NOTIFY request. */
525     status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
526     if (status != PJ_SUCCESS)
527 	goto on_return;
528 
529 
530     /* Create message body to reflect the presence status.
531      * Only do this if we have presence status info to send (see above).
532      */
533     if (pres->status.info_cnt > 0) {
534 	status = pres_create_msg_body( pres, tdata );
535 	if (status != PJ_SUCCESS)
536 	    goto on_return;
537     }
538 
539     /* Done. */
540     *p_tdata = tdata;
541 
542 
543 on_return:
544     pjsip_dlg_dec_lock(pres->dlg);
545     return status;
546 }
547 
548 
549 /*
550  * Create NOTIFY that reflect current state.
551  */
pjsip_pres_current_notify(pjsip_evsub * sub,pjsip_tx_data ** p_tdata)552 PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
553 					       pjsip_tx_data **p_tdata )
554 {
555     pjsip_pres *pres;
556     pjsip_tx_data *tdata;
557     pj_status_t status;
558 
559     /* Check arguments. */
560     PJ_ASSERT_RETURN(sub, PJ_EINVAL);
561 
562     /* Get the presence object. */
563     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
564     PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
565 
566     /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE
567      * to refresh subscription while we're waiting for user authorization.
568      */
569     //PJ_ASSERT_RETURN(pres->status.info_cnt > 0,
570     //		       PJSIP_SIMPLE_ENOPRESENCEINFO);
571 
572 
573     /* Lock object. */
574     pjsip_dlg_inc_lock(pres->dlg);
575 
576     /* Create the NOTIFY request. */
577     status = pjsip_evsub_current_notify( sub, &tdata);
578     if (status != PJ_SUCCESS)
579 	goto on_return;
580 
581 
582     /* Create message body to reflect the presence status. */
583     if (pres->status.info_cnt > 0) {
584 	status = pres_create_msg_body( pres, tdata );
585 	if (status != PJ_SUCCESS)
586 	    goto on_return;
587     }
588 
589     /* Done. */
590     *p_tdata = tdata;
591 
592 
593 on_return:
594     pjsip_dlg_dec_lock(pres->dlg);
595     return status;
596 }
597 
598 
599 /*
600  * Send request.
601  */
pjsip_pres_send_request(pjsip_evsub * sub,pjsip_tx_data * tdata)602 PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
603 					     pjsip_tx_data *tdata )
604 {
605     return pjsip_evsub_send_request(sub, tdata);
606 }
607 
608 
609 /*
610  * This callback is called by event subscription when subscription
611  * state has changed.
612  */
pres_on_evsub_state(pjsip_evsub * sub,pjsip_event * event)613 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
614 {
615     pjsip_pres *pres;
616 
617     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
618     PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
619 
620     if (pres->user_cb.on_evsub_state)
621 	(*pres->user_cb.on_evsub_state)(sub, event);
622 
623     if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
624 	if (pres->status_pool) {
625 	    pj_pool_release(pres->status_pool);
626 	    pres->status_pool = NULL;
627 	}
628 	if (pres->tmp_pool) {
629 	    pj_pool_release(pres->tmp_pool);
630 	    pres->tmp_pool = NULL;
631 	}
632     }
633 }
634 
635 /*
636  * Called when transaction state has changed.
637  */
pres_on_evsub_tsx_state(pjsip_evsub * sub,pjsip_transaction * tsx,pjsip_event * event)638 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
639 				     pjsip_event *event)
640 {
641     pjsip_pres *pres;
642 
643     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
644     PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
645 
646     if (pres->user_cb.on_tsx_state)
647 	(*pres->user_cb.on_tsx_state)(sub, tsx, event);
648 }
649 
650 
651 /*
652  * Called when SUBSCRIBE is received.
653  */
pres_on_evsub_rx_refresh(pjsip_evsub * sub,pjsip_rx_data * rdata,int * p_st_code,pj_str_t ** p_st_text,pjsip_hdr * res_hdr,pjsip_msg_body ** p_body)654 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
655 				      pjsip_rx_data *rdata,
656 				      int *p_st_code,
657 				      pj_str_t **p_st_text,
658 				      pjsip_hdr *res_hdr,
659 				      pjsip_msg_body **p_body)
660 {
661     pjsip_pres *pres;
662 
663     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
664     PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
665 
666     if (pres->user_cb.on_rx_refresh) {
667 	(*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
668 				       res_hdr, p_body);
669 
670     } else {
671 	/* Implementors MUST send NOTIFY if it implements on_rx_refresh */
672 	pjsip_tx_data *tdata;
673 	pj_str_t timeout = { "timeout", 7};
674 	pj_status_t status;
675 
676 	if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
677 	    status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
678 					NULL, &timeout, &tdata);
679 	} else {
680 	    status = pjsip_pres_current_notify(sub, &tdata);
681 	}
682 
683 	if (status == PJ_SUCCESS)
684 	    pjsip_pres_send_request(sub, tdata);
685     }
686 }
687 
688 
689 /*
690  * Process the content of incoming NOTIFY request and update temporary
691  * status.
692  *
693  * return PJ_SUCCESS if incoming request is acceptable. If return value
694  *	  is not PJ_SUCCESS, res_hdr may be added with Warning header.
695  */
pres_process_rx_notify(pjsip_pres * pres,pjsip_rx_data * rdata,int * p_st_code,pj_str_t ** p_st_text,pjsip_hdr * res_hdr)696 static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
697 					   pjsip_rx_data *rdata,
698 					   int *p_st_code,
699 					   pj_str_t **p_st_text,
700 					   pjsip_hdr *res_hdr)
701 {
702     const pj_str_t STR_MULTIPART = { "multipart", 9 };
703     pjsip_ctype_hdr *ctype_hdr;
704     pj_status_t status = PJ_SUCCESS;
705 
706     *p_st_text = NULL;
707 
708     /* Check Content-Type and msg body are present. */
709     ctype_hdr = rdata->msg_info.ctype;
710 
711     if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
712 
713 	pjsip_warning_hdr *warn_hdr;
714 	pj_str_t warn_text;
715 
716 	*p_st_code = PJSIP_SC_BAD_REQUEST;
717 
718 	warn_text = pj_str("Message body is not present");
719 	warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
720 					    pjsip_endpt_name(pres->dlg->endpt),
721 					    &warn_text);
722 	pj_list_push_back(res_hdr, warn_hdr);
723 
724 	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
725     }
726 
727     /* Parse content. */
728     if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) {
729 	pjsip_multipart_part *mpart;
730 	pjsip_media_type ctype;
731 
732 	pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
733 			      (pj_str_t*)&STR_PIDF_XML);
734 	mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
735 					  &ctype, NULL);
736 	if (mpart) {
737 	    status = pjsip_pres_parse_pidf2((char*)mpart->body->data,
738 					    mpart->body->len, pres->tmp_pool,
739 					    &pres->tmp_status);
740 	}
741 
742 	if (mpart==NULL) {
743 	    pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
744 				  (pj_str_t*)&STR_XPIDF_XML);
745 	    mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
746 					      &ctype, NULL);
747 	    if (mpart) {
748 		status = pjsip_pres_parse_xpidf2((char*)mpart->body->data,
749 						 mpart->body->len,
750 						 pres->tmp_pool,
751 						 &pres->tmp_status);
752 	    } else {
753 		status = PJSIP_SIMPLE_EBADCONTENT;
754 	    }
755 	}
756     }
757     else
758     if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
759 	pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
760     {
761 	status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
762 					&pres->tmp_status);
763     }
764     else
765     if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
766 	pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
767     {
768 	status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
769 					 &pres->tmp_status);
770     }
771     else
772     {
773 	status = PJSIP_SIMPLE_EBADCONTENT;
774     }
775 
776     if (status != PJ_SUCCESS) {
777 	/* Unsupported or bad Content-Type */
778 	if (PJSIP_PRES_BAD_CONTENT_RESPONSE >= 300) {
779 	    pjsip_accept_hdr *accept_hdr;
780 	    pjsip_warning_hdr *warn_hdr;
781 
782 	    *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
783 
784 	    /* Add Accept header */
785 	    accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
786 	    accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
787 	    accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
788 	    pj_list_push_back(res_hdr, accept_hdr);
789 
790 	    /* Add Warning header */
791 	    warn_hdr = pjsip_warning_hdr_create_from_status(
792 					rdata->tp_info.pool,
793 					pjsip_endpt_name(pres->dlg->endpt),
794 					status);
795 	    pj_list_push_back(res_hdr, warn_hdr);
796 
797 	    return status;
798 	} else {
799 	    pj_assert(PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 2);
800 	    PJ_PERROR(4,(THIS_FILE, status,
801 			 "Ignoring presence error due to "
802 		         "PJSIP_PRES_BAD_CONTENT_RESPONSE setting [%d]",
803 		         PJSIP_PRES_BAD_CONTENT_RESPONSE));
804 	    *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
805 	    status = PJ_SUCCESS;
806 	}
807     }
808 
809     /* If application calls pres_get_status(), redirect the call to
810      * retrieve the temporary status.
811      */
812     pres->tmp_status._is_valid = PJ_TRUE;
813 
814     return PJ_SUCCESS;
815 }
816 
817 
818 /*
819  * Called when NOTIFY is received.
820  */
pres_on_evsub_rx_notify(pjsip_evsub * sub,pjsip_rx_data * rdata,int * p_st_code,pj_str_t ** p_st_text,pjsip_hdr * res_hdr,pjsip_msg_body ** p_body)821 static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
822 				     pjsip_rx_data *rdata,
823 				     int *p_st_code,
824 				     pj_str_t **p_st_text,
825 				     pjsip_hdr *res_hdr,
826 				     pjsip_msg_body **p_body)
827 {
828     pjsip_pres *pres;
829     pj_status_t status;
830 
831     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
832     PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
833 
834     if (rdata->msg_info.msg->body) {
835 	status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
836 					 res_hdr );
837 	if (status != PJ_SUCCESS)
838 	    return;
839 
840     } else {
841 #if 1
842 	/* This is the newest change, http://trac.pjsip.org/repos/ticket/873
843 	 * Some app want to be notified about the empty NOTIFY, e.g. to
844 	 * decide whether it should consider the buddy as offline.
845 	 * In this case, leave the buddy state unchanged, but set the
846 	 * "tuple_node" in pjsip_pres_status to NULL.
847 	 */
848 	unsigned i;
849 	for (i=0; i<pres->status.info_cnt; ++i) {
850 	    pres->status.info[i].tuple_node = NULL;
851 	}
852 
853 #elif 0
854 	/* This has just been changed. Previously, we treat incoming NOTIFY
855 	 * with no message body as having the presence subscription closed.
856 	 * Now we treat it as no change in presence status (ref: EyeBeam).
857 	 */
858 	*p_st_code = 200;
859 	return;
860 #else
861 	unsigned i;
862 	/* Subscription is terminated. Consider contact is offline */
863 	pres->tmp_status._is_valid = PJ_TRUE;
864 	for (i=0; i<pres->tmp_status.info_cnt; ++i)
865 	    pres->tmp_status.info[i].basic_open = PJ_FALSE;
866 #endif
867     }
868 
869     /* Notify application. */
870     if (pres->user_cb.on_rx_notify) {
871 	(*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
872 				      res_hdr, p_body);
873     }
874 
875 
876     /* If application responded NOTIFY with 2xx, copy temporary status
877      * to main status, and mark the temporary status as invalid.
878      */
879     if ((*p_st_code)/100 == 2) {
880 	pj_pool_t *tmp;
881 
882 	pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
883 
884 	/* Swap the pool */
885 	tmp = pres->tmp_pool;
886 	pres->tmp_pool = pres->status_pool;
887 	pres->status_pool = tmp;
888     }
889 
890     pres->tmp_status._is_valid = PJ_FALSE;
891     pj_pool_reset(pres->tmp_pool);
892 
893     /* Done */
894 }
895 
896 /*
897  * Called when it's time to send SUBSCRIBE.
898  */
pres_on_evsub_client_refresh(pjsip_evsub * sub)899 static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
900 {
901     pjsip_pres *pres;
902 
903     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
904     PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
905 
906     if (pres->user_cb.on_client_refresh) {
907 	(*pres->user_cb.on_client_refresh)(sub);
908     } else {
909 	pj_status_t status;
910 	pjsip_tx_data *tdata;
911 
912 	status = pjsip_pres_initiate(sub, PJSIP_EXPIRES_NOT_SPECIFIED,
913 				     &tdata);
914 	if (status == PJ_SUCCESS)
915 	    pjsip_pres_send_request(sub, tdata);
916     }
917 }
918 
919 /*
920  * Called when no refresh is received after the interval.
921  */
pres_on_evsub_server_timeout(pjsip_evsub * sub)922 static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
923 {
924     pjsip_pres *pres;
925 
926     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
927     PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
928 
929     if (pres->user_cb.on_server_timeout) {
930 	(*pres->user_cb.on_server_timeout)(sub);
931     } else {
932 	pj_status_t status;
933 	pjsip_tx_data *tdata;
934 	pj_str_t reason = { "timeout", 7 };
935 
936 	status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
937 				   NULL, &reason, &tdata);
938 	if (status == PJ_SUCCESS)
939 	    pjsip_pres_send_request(sub, tdata);
940     }
941 }
942 
943