blob: 4515636c6ca6c8a8a071ebf5c668e1cea1aa10fe [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +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 Prijono834aee32006-02-19 01:38:06 +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 Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
Benny Prijono834aee32006-02-19 01:38:06 +000022
Benny Prijono834aee32006-02-19 01:38:06 +000023
24#define THIS_FILE "pjsua_pres.c"
25
Benny Prijonoa17496a2007-10-31 10:20:31 +000026
27static void subscribe_buddy_presence(unsigned index);
28
29
30/*
31 * Find buddy.
32 */
33static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
34{
35 const pjsip_sip_uri *sip_uri;
36 unsigned i;
37
Benny Prijonof0f8fd12007-11-10 12:05:59 +000038 uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +000039
40 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
41 return PJSUA_INVALID_ID;
42
43 sip_uri = (const pjsip_sip_uri*) uri;
44
45 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
46 const pjsua_buddy *b = &pjsua_var.buddy[i];
47
48 if (!pjsua_buddy_is_valid(i))
49 continue;
50
51 if (pj_stricmp(&sip_uri->user, &b->name)==0 &&
52 pj_stricmp(&sip_uri->host, &b->host)==0 &&
53 (sip_uri->port==(int)b->port || (sip_uri->port==0 && b->port==5060)))
54 {
55 /* Match */
56 return i;
57 }
58 }
59
60 return PJSUA_INVALID_ID;
61}
Benny Prijono7a5f5102007-05-29 00:33:09 +000062
Benny Prijono834aee32006-02-19 01:38:06 +000063
Benny Prijonoeebe9af2006-06-13 22:57:13 +000064/*
65 * Get total number of buddies.
66 */
67PJ_DEF(unsigned) pjsua_get_buddy_count(void)
68{
69 return pjsua_var.buddy_cnt;
70}
Benny Prijono834aee32006-02-19 01:38:06 +000071
Benny Prijonoeebe9af2006-06-13 22:57:13 +000072
73/*
Benny Prijono705e7842008-07-21 18:12:51 +000074 * Find buddy.
75 */
76PJ_DEF(pjsua_buddy_id) pjsua_buddy_find(const pj_str_t *uri_str)
77{
78 pj_str_t input;
79 pj_pool_t *pool;
80 pjsip_uri *uri;
81 pjsua_buddy_id buddy_id;
82
83 pool = pjsua_pool_create("buddyfind", 512, 512);
84 pj_strdup_with_null(pool, &input, uri_str);
85
86 uri = pjsip_parse_uri(pool, input.ptr, input.slen, 0);
87 if (!uri)
88 buddy_id = PJSUA_INVALID_ID;
89 else
90 buddy_id = pjsua_find_buddy(uri);
91
92 pj_pool_release(pool);
93
94 return buddy_id;
95}
96
97
98/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 * Check if buddy ID is valid.
100 */
101PJ_DEF(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id)
102{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000103 return buddy_id>=0 && buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy) &&
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000104 pjsua_var.buddy[buddy_id].uri.slen != 0;
105}
106
107
108/*
109 * Enum buddy IDs.
110 */
111PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[],
112 unsigned *count)
113{
114 unsigned i, c;
115
116 PJ_ASSERT_RETURN(ids && count, PJ_EINVAL);
117
118 PJSUA_LOCK();
119
120 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
121 if (!pjsua_var.buddy[i].uri.slen)
122 continue;
123 ids[c] = i;
124 ++c;
125 }
126
127 *count = c;
128
129 PJSUA_UNLOCK();
130
131 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000132}
133
134
135/*
136 * Get detailed buddy info.
137 */
138PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
139 pjsua_buddy_info *info)
140{
Benny Prijono20da7992008-12-18 16:48:43 +0000141 unsigned total=0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000142 pjsua_buddy *buddy;
143
144 PJ_ASSERT_RETURN(buddy_id>=0 &&
145 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
146 PJ_EINVAL);
147
148 PJSUA_LOCK();
149
Benny Prijonoac623b32006-07-03 15:19:31 +0000150 pj_bzero(info, sizeof(pjsua_buddy_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151
152 buddy = &pjsua_var.buddy[buddy_id];
153 info->id = buddy->index;
154 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
155 PJSUA_UNLOCK();
156 return PJ_SUCCESS;
157 }
158
159 /* uri */
160 info->uri.ptr = info->buf_ + total;
161 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
162 total += info->uri.slen;
163
164 /* contact */
165 info->contact.ptr = info->buf_ + total;
166 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
167 total += info->contact.slen;
Benny Prijono97276602007-06-23 01:07:08 +0000168
Benny Prijono28add7e2009-06-15 16:03:40 +0000169 /* Presence status */
170 pj_memcpy(&info->pres_status, &buddy->status, sizeof(pjsip_pres_status));
171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 /* status and status text */
173 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
174 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
175 info->status_text = pj_str("?");
176 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
177 info->status = PJSUA_BUDDY_STATUS_ONLINE;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000178
179 /* copy RPID information */
180 info->rpid = buddy->status.info[0].rpid;
181
182 if (info->rpid.note.slen)
183 info->status_text = info->rpid.note;
184 else
185 info->status_text = pj_str("Online");
186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 } else {
188 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
189 info->status_text = pj_str("Offline");
190 }
191
192 /* monitor pres */
193 info->monitor_pres = buddy->monitor;
194
Benny Prijono63fba012008-07-17 14:19:10 +0000195 /* subscription state and termination reason */
196 if (buddy->sub) {
197 info->sub_state = pjsip_evsub_get_state(buddy->sub);
Benny Prijonod06d8c52009-06-30 13:53:47 +0000198 info->sub_state_name = pjsip_evsub_get_state_name(buddy->sub);
Benny Prijono63fba012008-07-17 14:19:10 +0000199 if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
200 total < sizeof(info->buf_))
201 {
202 info->sub_term_reason.ptr = info->buf_ + total;
203 pj_strncpy(&info->sub_term_reason,
204 pjsip_evsub_get_termination_reason(buddy->sub),
205 sizeof(info->buf_) - total);
206 total += info->sub_term_reason.slen;
207 } else {
208 info->sub_term_reason = pj_str("");
209 }
210 } else if (total < sizeof(info->buf_)) {
Benny Prijonod06d8c52009-06-30 13:53:47 +0000211 info->sub_state_name = "NULL";
Benny Prijono63fba012008-07-17 14:19:10 +0000212 info->sub_term_reason.ptr = info->buf_ + total;
213 pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
214 sizeof(info->buf_) - total);
215 total += info->sub_term_reason.slen;
216 } else {
Benny Prijonod06d8c52009-06-30 13:53:47 +0000217 info->sub_state_name = "NULL";
Benny Prijono63fba012008-07-17 14:19:10 +0000218 info->sub_term_reason = pj_str("");
219 }
220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 PJSUA_UNLOCK();
222 return PJ_SUCCESS;
223}
224
Benny Prijono705e7842008-07-21 18:12:51 +0000225/*
226 * Set the user data associated with the buddy object.
227 */
228PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
229 void *user_data)
230{
231 PJ_ASSERT_RETURN(buddy_id>=0 &&
232 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
233 PJ_EINVAL);
234
235 PJSUA_LOCK();
236
237 pjsua_var.buddy[buddy_id].user_data = user_data;
238
239 PJSUA_UNLOCK();
240
241 return PJ_SUCCESS;
242}
243
244
245/*
246 * Get the user data associated with the budy object.
247 */
248PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
249{
250 void *user_data;
251
252 PJ_ASSERT_RETURN(buddy_id>=0 &&
253 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
254 NULL);
255
256 PJSUA_LOCK();
257
258 user_data = pjsua_var.buddy[buddy_id].user_data;
259
260 PJSUA_UNLOCK();
261
262 return user_data;
263}
264
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000265
266/*
267 * Reset buddy descriptor.
268 */
269static void reset_buddy(pjsua_buddy_id id)
270{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000271 pj_pool_t *pool = pjsua_var.buddy[id].pool;
Benny Prijonoac623b32006-07-03 15:19:31 +0000272 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000273 pjsua_var.buddy[id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000274 pjsua_var.buddy[id].index = id;
275}
276
277
278/*
279 * Add new buddy.
280 */
281PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
282 pjsua_buddy_id *p_buddy_id)
283{
284 pjsip_name_addr *url;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000285 pjsua_buddy *buddy;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000286 pjsip_sip_uri *sip_uri;
287 int index;
288 pj_str_t tmp;
289
290 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
291 PJ_ARRAY_SIZE(pjsua_var.buddy),
292 PJ_ETOOMANY);
293
294 PJSUA_LOCK();
295
296 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000297 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000298 if (pjsua_var.buddy[index].uri.slen == 0)
299 break;
300 }
301
302 /* Expect to find an empty slot */
303 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
304 PJSUA_UNLOCK();
305 /* This shouldn't happen */
306 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
307 return PJ_ETOOMANY;
308 }
309
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000310 buddy = &pjsua_var.buddy[index];
311
312 /* Create pool for this buddy */
313 if (buddy->pool) {
314 pj_pool_reset(buddy->pool);
315 } else {
316 char name[PJ_MAX_OBJ_NAME];
317 pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
318 buddy->pool = pjsua_pool_create(name, 512, 256);
319 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000320
Benny Prijono63fba012008-07-17 14:19:10 +0000321 /* Init buffers for presence subscription status */
322 buddy->term_reason.ptr = (char*)
323 pj_pool_alloc(buddy->pool,
324 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
325
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000326 /* Get name and display name for buddy */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000327 pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
328 url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000329 PJSIP_PARSE_URI_AS_NAMEADDR);
330
331 if (url == NULL) {
332 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000333 pj_pool_release(buddy->pool);
334 buddy->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000335 PJSUA_UNLOCK();
336 return PJSIP_EINVALIDURI;
337 }
338
Benny Prijonofc493592007-02-18 20:56:32 +0000339 /* Only support SIP schemes */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000340 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
341 pj_pool_release(buddy->pool);
342 buddy->pool = NULL;
343 PJSUA_UNLOCK();
Benny Prijonofc493592007-02-18 20:56:32 +0000344 return PJSIP_EINVALIDSCHEME;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000345 }
Benny Prijonofc493592007-02-18 20:56:32 +0000346
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000347 /* Reset buddy, to make sure everything is cleared with default
348 * values
349 */
350 reset_buddy(index);
351
352 /* Save URI */
353 pjsua_var.buddy[index].uri = tmp;
354
Benny Prijono9c1528f2007-02-10 19:22:25 +0000355 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 pjsua_var.buddy[index].name = sip_uri->user;
357 pjsua_var.buddy[index].display = url->display;
358 pjsua_var.buddy[index].host = sip_uri->host;
359 pjsua_var.buddy[index].port = sip_uri->port;
360 pjsua_var.buddy[index].monitor = cfg->subscribe;
361 if (pjsua_var.buddy[index].port == 0)
362 pjsua_var.buddy[index].port = 5060;
363
Benny Prijono705e7842008-07-21 18:12:51 +0000364 /* Save user data */
365 pjsua_var.buddy[index].user_data = (void*)cfg->user_data;
366
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000367 if (p_buddy_id)
368 *p_buddy_id = index;
369
370 pjsua_var.buddy_cnt++;
371
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000372 PJSUA_UNLOCK();
373
Benny Prijonof9c40c32007-06-28 07:20:26 +0000374 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
375
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000376 return PJ_SUCCESS;
377}
378
379
380/*
381 * Delete buddy.
382 */
383PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
384{
385 PJ_ASSERT_RETURN(buddy_id>=0 &&
386 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
387 PJ_EINVAL);
388
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000389 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000390 return PJ_SUCCESS;
391 }
392
393 /* Unsubscribe presence */
394 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
395
Benny Prijonof9c40c32007-06-28 07:20:26 +0000396 PJSUA_LOCK();
397
Benny Prijonoa5776cb2009-04-14 15:11:23 +0000398 /* Not interested with further events for this buddy */
399 if (pjsua_var.buddy[buddy_id].sub) {
400 pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub,
401 pjsua_var.mod.id, NULL);
402 }
403
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000404 /* Remove buddy */
405 pjsua_var.buddy[buddy_id].uri.slen = 0;
406 pjsua_var.buddy_cnt--;
407
408 /* Reset buddy struct */
409 reset_buddy(buddy_id);
410
411 PJSUA_UNLOCK();
412 return PJ_SUCCESS;
413}
414
415
416/*
417 * Enable/disable buddy's presence monitoring.
418 */
419PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
420 pj_bool_t subscribe)
421{
422 pjsua_buddy *buddy;
423
424 PJ_ASSERT_RETURN(buddy_id>=0 &&
425 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
426 PJ_EINVAL);
427
428 PJSUA_LOCK();
429
430 buddy = &pjsua_var.buddy[buddy_id];
431 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000432
433 PJSUA_UNLOCK();
434
Benny Prijonof9c40c32007-06-28 07:20:26 +0000435 pjsua_pres_refresh();
436
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000437 return PJ_SUCCESS;
438}
439
440
441/*
Benny Prijono10861432007-10-31 10:54:53 +0000442 * Update buddy's presence.
443 */
444PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
445{
446 pjsua_buddy *buddy;
447
448 PJ_ASSERT_RETURN(buddy_id>=0 &&
449 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
450 PJ_EINVAL);
451
452 PJSUA_LOCK();
453
454 buddy = &pjsua_var.buddy[buddy_id];
455
456 /* Return error if buddy's presence monitoring is not enabled */
457 if (!buddy->monitor) {
458 PJSUA_UNLOCK();
459 return PJ_EINVALIDOP;
460 }
461
462 /* Ignore if presence is already active for the buddy */
463 if (buddy->sub) {
464 PJSUA_UNLOCK();
465 return PJ_SUCCESS;
466 }
467
468 /* Initiate presence subscription */
469 subscribe_buddy_presence(buddy_id);
470
471 PJSUA_UNLOCK();
472
473 return PJ_SUCCESS;
474}
475
476
477/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000478 * Dump presence subscriptions to log file.
479 */
480PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
481{
482 unsigned acc_id;
483 unsigned i;
484
485
486 PJSUA_LOCK();
487
488 /*
489 * When no detail is required, just dump number of server and client
490 * subscriptions.
491 */
492 if (verbose == PJ_FALSE) {
493
494 int count = 0;
495
496 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
497
498 if (!pjsua_var.acc[acc_id].valid)
499 continue;
500
501 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
502 struct pjsua_srv_pres *uapres;
503
504 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
505 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
506 ++count;
507 uapres = uapres->next;
508 }
509 }
510 }
511
512 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
513 count));
514
515 count = 0;
516
517 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
518 if (pjsua_var.buddy[i].uri.slen == 0)
519 continue;
520 if (pjsua_var.buddy[i].sub) {
521 ++count;
522 }
523 }
524
525 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
526 count));
527 PJSUA_UNLOCK();
528 return;
529 }
530
531
532 /*
533 * Dumping all server (UAS) subscriptions
534 */
535 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
536
537 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
538
539 if (!pjsua_var.acc[acc_id].valid)
540 continue;
541
542 PJ_LOG(3,(THIS_FILE, " %.*s",
543 (int)pjsua_var.acc[acc_id].cfg.id.slen,
544 pjsua_var.acc[acc_id].cfg.id.ptr));
545
546 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
547
548 PJ_LOG(3,(THIS_FILE, " - none - "));
549
550 } else {
551 struct pjsua_srv_pres *uapres;
552
553 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
554 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
555
556 PJ_LOG(3,(THIS_FILE, " %10s %s",
557 pjsip_evsub_get_state_name(uapres->sub),
558 uapres->remote));
559
560 uapres = uapres->next;
561 }
562 }
563 }
564
565 /*
566 * Dumping all client (UAC) subscriptions
567 */
568 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
569
570 if (pjsua_var.buddy_cnt == 0) {
571
572 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
573
574 } else {
575 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
576
577 if (pjsua_var.buddy[i].uri.slen == 0)
578 continue;
579
580 if (pjsua_var.buddy[i].sub) {
581 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
582 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
583 (int)pjsua_var.buddy[i].uri.slen,
584 pjsua_var.buddy[i].uri.ptr));
585 } else {
586 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
587 "(null)",
588 (int)pjsua_var.buddy[i].uri.slen,
589 pjsua_var.buddy[i].uri.ptr));
590 }
591 }
592 }
593
594 PJSUA_UNLOCK();
595}
596
597
598/***************************************************************************
599 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000600 */
601
602/* Proto */
603static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
604
605/* The module instance. */
606static pjsip_module mod_pjsua_pres =
607{
608 NULL, NULL, /* prev, next. */
609 { "mod-pjsua-pres", 14 }, /* Name. */
610 -1, /* Id */
611 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000612 NULL, /* load() */
613 NULL, /* start() */
614 NULL, /* stop() */
615 NULL, /* unload() */
616 &pres_on_rx_request, /* on_rx_request() */
617 NULL, /* on_rx_response() */
618 NULL, /* on_tx_request. */
619 NULL, /* on_tx_response() */
620 NULL, /* on_tsx_state() */
621
622};
623
624
625/* Callback called when *server* subscription state has changed. */
626static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
627{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000628 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000629
630 PJ_UNUSED_ARG(event);
631
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 PJSUA_LOCK();
633
Benny Prijonoa1e69682007-05-11 15:14:34 +0000634 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000635 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000636 pjsip_evsub_state state;
637
Benny Prijonoba736c42008-07-10 20:45:03 +0000638 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000639 uapres->remote, pjsip_evsub_get_state_name(sub)));
640
Benny Prijono63fba012008-07-17 14:19:10 +0000641 state = pjsip_evsub_get_state(sub);
642
643 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
644 pj_str_t from;
645
646 from = uapres->dlg->remote.info_str;
647 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
648 uapres, &from,
649 state, event);
650 }
651
652 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000653 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000654 pj_list_erase(uapres);
655 }
656 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657
658 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000659}
660
661/* This is called when request is received.
662 * We need to check for incoming SUBSCRIBE request.
663 */
664static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
665{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000666 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000667 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000668 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000669 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
670 pjsua_srv_pres *uapres;
671 pjsip_evsub *sub;
672 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000673 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000674 pjsip_status_code st_code;
675 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000676 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000677 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000678 pj_status_t status;
679
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000680 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000681 return PJ_FALSE;
682
683 /* Incoming SUBSCRIBE: */
684
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000685 PJSUA_LOCK();
686
Benny Prijonoa91a0032006-02-26 21:23:45 +0000687 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000689 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000690
Benny Prijono6f979412006-06-15 12:25:46 +0000691 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
692 acc_id));
693
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000694 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000695 if (acc->contact.slen) {
696 contact = acc->contact;
697 } else {
698 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
699 acc_id, rdata);
700 if (status != PJ_SUCCESS) {
701 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
702 status);
703 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000704 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
705 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000706 return PJ_TRUE;
707 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000708 }
709
Benny Prijono834aee32006-02-19 01:38:06 +0000710 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000711 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000712 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000713 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000714 pjsua_perror(THIS_FILE,
715 "Unable to create UAS dialog for subscription",
716 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000717 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000718 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
719 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000720 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000721 }
722
Benny Prijono48ab2b72007-11-08 09:24:30 +0000723 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000724 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000725 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000726
Benny Prijono834aee32006-02-19 01:38:06 +0000727 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000728 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000729 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
730
731 /* Create server presence subscription: */
732 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
733 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000734 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
735 pjsip_tx_data *tdata;
736
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000737 pjsua_perror(THIS_FILE, "Unable to create server subscription",
738 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000739
740 if (code==599 || code > 699 || code < 300) {
741 code = 400;
742 }
743
744 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
745 if (status == PJ_SUCCESS) {
746 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
747 tdata);
748 }
749
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000750 PJSUA_UNLOCK();
751 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000752 }
753
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000754 /* If account is locked to specific transport, then lock dialog
755 * to this transport too.
756 */
757 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
758 pjsip_tpselector tp_sel;
759
760 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
761 pjsip_dlg_set_transport(dlg, &tp_sel);
762 }
763
Benny Prijono834aee32006-02-19 01:38:06 +0000764 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000765 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000766 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000767 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000768 uapres->acc_id = acc_id;
769 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000770 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
771 uapres->remote, PJSIP_MAX_URL_SIZE);
772 if (status < 1)
773 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
774 else
775 uapres->remote[status] = '\0';
776
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000777 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000778
779 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000780 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000781
782
Benny Prijono63fba012008-07-17 14:19:10 +0000783 /* Capture the value of Expires header. */
784 expires_hdr = (pjsip_expires_hdr*)
785 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
786 NULL);
787 if (expires_hdr)
788 uapres->expires = expires_hdr->ivalue;
789 else
790 uapres->expires = -1;
791
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000792 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000793 reason = pj_str("OK");
794 pjsua_msg_data_init(&msg_data);
795
796 /* Notify application callback, if any */
797 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
798 pjsua_buddy_id buddy_id;
799
800 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
801
802 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
803 &dlg->remote.info_str,
804 rdata, &st_code, &reason,
805 &msg_data);
806 }
807
808 /* Handle rejection case */
809 if (st_code >= 300) {
810 pjsip_tx_data *tdata;
811
812 /* Create response */
813 status = pjsip_dlg_create_response(dlg, rdata, st_code,
814 &reason, &tdata);
815 if (status != PJ_SUCCESS) {
816 pjsua_perror(THIS_FILE, "Error creating response", status);
817 pj_list_erase(uapres);
818 pjsip_pres_terminate(sub, PJ_FALSE);
819 PJSUA_UNLOCK();
820 return PJ_FALSE;
821 }
822
823 /* Add header list, if any */
824 pjsua_process_msg_data(tdata, &msg_data);
825
826 /* Send the response */
827 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
828 tdata);
829 if (status != PJ_SUCCESS) {
830 pjsua_perror(THIS_FILE, "Error sending response", status);
831 /* This is not fatal */
832 }
833
834 /* Terminate presence subscription */
835 pj_list_erase(uapres);
836 pjsip_pres_terminate(sub, PJ_FALSE);
837 PJSUA_UNLOCK();
838 return PJ_TRUE;
839 }
840
841 /* Create and send 2xx response to the SUBSCRIBE request: */
842 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000843 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000844 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
845 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000846 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000847 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000848 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000849 return PJ_FALSE;
850 }
851
Benny Prijono63fba012008-07-17 14:19:10 +0000852 /* If code is 200, send NOTIFY now */
853 if (st_code == 200) {
854 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
855 NULL, NULL, PJ_TRUE, &msg_data);
856 }
857
858 /* Done: */
859
860 PJSUA_UNLOCK();
861
862 return PJ_TRUE;
863}
864
865
866/*
867 * Send NOTIFY.
868 */
869PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
870 pjsua_srv_pres *srv_pres,
871 pjsip_evsub_state ev_state,
872 const pj_str_t *state_str,
873 const pj_str_t *reason,
874 pj_bool_t with_body,
875 const pjsua_msg_data *msg_data)
876{
877 pjsua_acc *acc;
878 pjsip_pres_status pres_status;
879 pjsua_buddy_id buddy_id;
880 pjsip_tx_data *tdata;
881 pj_status_t status;
882
883 /* Check parameters */
884 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
885
886 /* Check that account ID is valid */
887 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
888 PJ_EINVAL);
889 /* Check that account is valid */
890 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
891
892 PJSUA_LOCK();
893
894 acc = &pjsua_var.acc[acc_id];
895
896 /* Check that the server presence subscription is still valid */
897 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
898 /* Subscription has been terminated */
899 PJSUA_UNLOCK();
900 return PJ_EINVALIDOP;
901 }
Benny Prijono834aee32006-02-19 01:38:06 +0000902
903 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000904 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000905 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000906 pres_status.info[0].basic_open = acc->online_status;
907 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000908 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000909 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000910 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000911 /* add RPID information */
912 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
913 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000914
Benny Prijono63fba012008-07-17 14:19:10 +0000915 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000916
Benny Prijonoc61cc042007-06-27 13:01:59 +0000917 /* Check expires value. If it's zero, send our presense state but
918 * set subscription state to TERMINATED.
919 */
Benny Prijono63fba012008-07-17 14:19:10 +0000920 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000921 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000922
Benny Prijono63fba012008-07-17 14:19:10 +0000923 /* Create and send the NOTIFY to active subscription: */
924 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
925 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000926 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000927 /* Force removal of message body if msg_body==FALSE */
928 if (!with_body) {
929 tdata->msg->body = NULL;
930 }
931 pjsua_process_msg_data(tdata, msg_data);
932 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000933 }
Benny Prijono834aee32006-02-19 01:38:06 +0000934
935 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000936 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
937 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000938 pj_list_erase(srv_pres);
939 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000940 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000941 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000942 }
943
944
Benny Prijonoa17496a2007-10-31 10:20:31 +0000945 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000946 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000947 if (buddy_id != PJSUA_INVALID_ID) {
948 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
949 if (b->monitor && b->sub == NULL) {
950 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
951 "activating outgoing subscription", buddy_id));
952 subscribe_buddy_presence(buddy_id);
953 }
954 }
955
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000956 PJSUA_UNLOCK();
957
Benny Prijono63fba012008-07-17 14:19:10 +0000958 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000959}
960
961
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000962/*
963 * Client presence publication callback.
964 */
965static void publish_cb(struct pjsip_publishc_cbparam *param)
966{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000967 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000968
969 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
Benny Prijono53984d12009-04-28 22:19:49 +0000970
971 pjsip_publishc_destroy(param->pubc);
972 acc->publish_sess = NULL;
973
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000974 if (param->status != PJ_SUCCESS) {
975 char errmsg[PJ_ERR_MSG_SIZE];
976
977 pj_strerror(param->status, errmsg, sizeof(errmsg));
978 PJ_LOG(1,(THIS_FILE,
979 "Client publication (PUBLISH) failed, status=%d, msg=%s",
980 param->status, errmsg));
Benny Prijono53984d12009-04-28 22:19:49 +0000981 } else if (param->code == 412) {
982 /* 412 (Conditional Request Failed)
983 * The PUBLISH refresh has failed, retry with new one.
984 */
985 pjsua_pres_init_publish_acc(acc->index);
986
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000987 } else {
988 PJ_LOG(1,(THIS_FILE,
989 "Client publication (PUBLISH) failed (%d/%.*s)",
990 param->code, (int)param->reason.slen,
991 param->reason.ptr));
992 }
993
Benny Prijono53984d12009-04-28 22:19:49 +0000994 } else {
Benny Prijono534a9ba2009-10-13 14:01:59 +0000995 if (param->expiration < 1) {
Benny Prijono53984d12009-04-28 22:19:49 +0000996 /* Could happen if server "forgot" to include Expires header
997 * in the response. We will not renew, so destroy the pubc.
998 */
999 pjsip_publishc_destroy(param->pubc);
1000 acc->publish_sess = NULL;
1001 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001002 }
1003}
1004
1005
1006/*
1007 * Send PUBLISH request.
1008 */
1009static pj_status_t send_publish(int acc_id, pj_bool_t active)
1010{
1011 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1012 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1013 pjsip_pres_status pres_status;
1014 pjsip_tx_data *tdata;
1015 pj_status_t status;
1016
1017
1018 /* Create PUBLISH request */
1019 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +00001020 char *bpos;
1021 pj_str_t entity;
1022
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001023 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1024 if (status != PJ_SUCCESS) {
1025 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1026 goto on_error;
1027 }
1028
1029 /* Set our online status: */
1030 pj_bzero(&pres_status, sizeof(pres_status));
1031 pres_status.info_cnt = 1;
1032 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001033 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001034 /* .. including RPID information */
1035 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1036 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001037
Benny Prijono8c6e8842007-02-24 15:33:54 +00001038 /* Be careful not to send PIDF with presence entity ID containing
1039 * "<" character.
1040 */
1041 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1042 char *epos = pj_strchr(&acc_cfg->id, '>');
1043 if (epos - bpos < 2) {
1044 pj_assert(!"Unexpected invalid URI");
1045 status = PJSIP_EINVALIDURI;
1046 goto on_error;
1047 }
1048 entity.ptr = bpos+1;
1049 entity.slen = epos - bpos - 1;
1050 } else {
1051 entity = acc_cfg->id;
1052 }
1053
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001054 /* Create and add PIDF message body */
1055 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001056 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001057 if (status != PJ_SUCCESS) {
1058 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1059 status);
1060 pjsip_tx_data_dec_ref(tdata);
1061 goto on_error;
1062 }
1063 } else {
1064 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1065 if (status != PJ_SUCCESS) {
1066 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1067 goto on_error;
1068 }
1069 }
1070
1071 /* Add headers etc */
1072 pjsua_process_msg_data(tdata, NULL);
1073
1074 /* Send the PUBLISH request */
1075 status = pjsip_publishc_send(acc->publish_sess, tdata);
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001076 if (status == PJ_EPENDING) {
1077 PJ_LOG(3,(THIS_FILE, "Previous request is in progress, "
1078 "PUBLISH request is queued"));
1079 } else if (status != PJ_SUCCESS) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001080 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1081 goto on_error;
1082 }
1083
1084 acc->publish_state = acc->online_status;
1085 return PJ_SUCCESS;
1086
1087on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001088 if (acc->publish_sess) {
1089 pjsip_publishc_destroy(acc->publish_sess);
1090 acc->publish_sess = NULL;
1091 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001092 return status;
1093}
1094
1095
1096/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001097pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001098{
1099 const pj_str_t STR_PRESENCE = { "presence", 8 };
1100 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1101 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1102 pj_status_t status;
1103
1104 /* Create and init client publication session */
1105 if (acc_cfg->publish_enabled) {
1106
1107 /* Create client publication */
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001108 status = pjsip_publishc_create(pjsua_var.endpt, &acc_cfg->publish_opt,
1109 acc, &publish_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001110 &acc->publish_sess);
1111 if (status != PJ_SUCCESS) {
1112 acc->publish_sess = NULL;
1113 return status;
1114 }
1115
1116 /* Initialize client publication */
1117 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1118 &acc_cfg->id, &acc_cfg->id,
1119 &acc_cfg->id,
Benny Prijono53984d12009-04-28 22:19:49 +00001120 PJSUA_PUBLISH_EXPIRATION);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001121 if (status != PJ_SUCCESS) {
1122 acc->publish_sess = NULL;
1123 return status;
1124 }
1125
Benny Prijono703b7d72007-03-20 09:13:24 +00001126 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001127 if (acc->cred_cnt) {
1128 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1129 acc->cred);
1130 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001131
1132 /* Set route-set */
1133 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1134
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001135 /* Send initial PUBLISH request */
1136 if (acc->online_status != 0) {
1137 status = send_publish(acc_id, PJ_TRUE);
1138 if (status != PJ_SUCCESS)
1139 return status;
1140 }
1141
1142 } else {
1143 acc->publish_sess = NULL;
1144 }
1145
1146 return PJ_SUCCESS;
1147}
1148
1149
1150/* Init presence for account */
1151pj_status_t pjsua_pres_init_acc(int acc_id)
1152{
1153 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1154
1155 /* Init presence subscription */
1156 pj_list_init(&acc->pres_srv_list);
1157
Benny Prijono8b6834f2007-02-24 13:29:22 +00001158 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001159}
1160
1161
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001162/* Terminate server subscription for the account */
1163void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001164{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001165 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1166 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001167 pjsua_srv_pres *uapres;
1168
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001169 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001170
Benny Prijono922933b2007-01-21 16:23:56 +00001171 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001172 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001173
1174 pjsip_pres_status pres_status;
1175 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001176 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001177 pjsip_tx_data *tdata;
1178
Benny Prijono5516f912008-05-05 12:06:08 +00001179 next = uapres->next;
1180
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001181 pjsip_pres_get_status(uapres->sub, &pres_status);
1182
1183 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1184 pjsip_pres_set_status(uapres->sub, &pres_status);
1185
1186 if (pjsip_pres_notify(uapres->sub,
1187 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1188 &reason, &tdata)==PJ_SUCCESS)
1189 {
1190 pjsip_pres_send_request(uapres->sub, tdata);
1191 }
1192
Benny Prijono5516f912008-05-05 12:06:08 +00001193 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001194 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001195
Benny Prijono922933b2007-01-21 16:23:56 +00001196 /* Clear server presence subscription list because account might be reused
1197 * later. */
1198 pj_list_init(&acc->pres_srv_list);
1199
1200 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001201 if (acc->publish_sess) {
1202 acc->online_status = PJ_FALSE;
1203 send_publish(acc_id, PJ_FALSE);
Benny Prijono534a9ba2009-10-13 14:01:59 +00001204 /* By ticket #364, don't destroy the session yet (let the callback
1205 destroy it)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001206 if (acc->publish_sess) {
1207 pjsip_publishc_destroy(acc->publish_sess);
1208 acc->publish_sess = NULL;
1209 }
Benny Prijono534a9ba2009-10-13 14:01:59 +00001210 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001211 acc_cfg->publish_enabled = PJ_FALSE;
1212 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001213}
1214
1215
Benny Prijono4461c7d2007-08-25 13:36:15 +00001216/* Update server subscription (e.g. when our online status has changed) */
1217void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001218{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001219 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1220 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221 pjsua_srv_pres *uapres;
1222
1223 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1224
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001225 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001226
1227 pjsip_pres_status pres_status;
1228 pjsip_tx_data *tdata;
1229
1230 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001231
1232 /* Only send NOTIFY once subscription is active. Some subscriptions
1233 * may still be in NULL (when app is adding a new buddy while in the
1234 * on_incoming_subscribe() callback) or PENDING (when user approval is
1235 * being requested) state and we don't send NOTIFY to these subs until
1236 * the user accepted the request.
1237 */
1238 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1239 (force || pres_status.info[0].basic_open != acc->online_status))
1240 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001241
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001242 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001243 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1244 sizeof(pjrpid_element));
1245
Benny Prijono834aee32006-02-19 01:38:06 +00001246 pjsip_pres_set_status(uapres->sub, &pres_status);
1247
Benny Prijono21b9ad92006-08-15 13:11:22 +00001248 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1249 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001250 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001251 }
Benny Prijono834aee32006-02-19 01:38:06 +00001252 }
1253
1254 uapres = uapres->next;
1255 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001256
Benny Prijono8b6834f2007-02-24 13:29:22 +00001257 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1258 * session. If we don't have a PUBLISH session, then it could be
1259 * that we're waiting until registration has completed before we
1260 * send the first PUBLISH.
1261 */
1262 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001263 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001264 send_publish(acc_id, PJ_TRUE);
1265 }
1266 }
Benny Prijono834aee32006-02-19 01:38:06 +00001267}
1268
1269
1270
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271/***************************************************************************
1272 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001273 */
1274
1275/* Callback called when *client* subscription state has changed. */
1276static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1277{
1278 pjsua_buddy *buddy;
1279
1280 PJ_UNUSED_ARG(event);
1281
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001282 PJSUA_LOCK();
1283
Benny Prijonoa1e69682007-05-11 15:14:34 +00001284 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001285 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001286 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001287 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288 (int)pjsua_var.buddy[buddy->index].uri.slen,
1289 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001290 pjsip_evsub_get_state_name(sub)));
1291
1292 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001293 if (buddy->term_reason.ptr == NULL) {
1294 buddy->term_reason.ptr = (char*)
1295 pj_pool_alloc(buddy->pool,
1296 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1297 }
1298 pj_strncpy(&buddy->term_reason,
1299 pjsip_evsub_get_termination_reason(sub),
1300 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1301 } else {
1302 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001303 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001304
1305 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001306 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1307 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001308
1309 /* Clear subscription */
1310 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1311 buddy->sub = NULL;
1312 buddy->status.info_cnt = 0;
1313 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1314 }
Benny Prijono834aee32006-02-19 01:38:06 +00001315 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316
1317 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001318}
1319
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320
1321/* Callback when transaction state has changed. */
1322static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1323 pjsip_transaction *tsx,
1324 pjsip_event *event)
1325{
1326 pjsua_buddy *buddy;
1327 pjsip_contact_hdr *contact_hdr;
1328
1329 PJSUA_LOCK();
1330
Benny Prijonoa1e69682007-05-11 15:14:34 +00001331 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001332 if (!buddy) {
1333 PJSUA_UNLOCK();
1334 return;
1335 }
1336
1337 /* We only use this to update buddy's Contact, when it's not
1338 * set.
1339 */
1340 if (buddy->contact.slen != 0) {
1341 /* Contact already set */
1342 PJSUA_UNLOCK();
1343 return;
1344 }
1345
1346 /* Only care about 2xx response to outgoing SUBSCRIBE */
1347 if (tsx->status_code/100 != 2 ||
1348 tsx->role != PJSIP_UAC_ROLE ||
1349 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001350 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001351 {
1352 PJSUA_UNLOCK();
1353 return;
1354 }
1355
1356 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001357 contact_hdr = (pjsip_contact_hdr*)
1358 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 PJSIP_H_CONTACT, NULL);
1360 if (!contact_hdr) {
1361 PJSUA_UNLOCK();
1362 return;
1363 }
1364
Benny Prijonoa1e69682007-05-11 15:14:34 +00001365 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001366 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1368 contact_hdr->uri,
1369 buddy->contact.ptr,
1370 PJSIP_MAX_URL_SIZE);
1371 if (buddy->contact.slen < 0)
1372 buddy->contact.slen = 0;
1373
1374 PJSUA_UNLOCK();
1375}
1376
1377
Benny Prijono834aee32006-02-19 01:38:06 +00001378/* Callback called when we receive NOTIFY */
1379static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1380 pjsip_rx_data *rdata,
1381 int *p_st_code,
1382 pj_str_t **p_st_text,
1383 pjsip_hdr *res_hdr,
1384 pjsip_msg_body **p_body)
1385{
1386 pjsua_buddy *buddy;
1387
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001388 PJSUA_LOCK();
1389
Benny Prijonoa1e69682007-05-11 15:14:34 +00001390 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001391 if (buddy) {
1392 /* Update our info. */
1393 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001394 }
1395
1396 /* The default is to send 200 response to NOTIFY.
1397 * Just leave it there..
1398 */
1399 PJ_UNUSED_ARG(rdata);
1400 PJ_UNUSED_ARG(p_st_code);
1401 PJ_UNUSED_ARG(p_st_text);
1402 PJ_UNUSED_ARG(res_hdr);
1403 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404
1405 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001406}
1407
1408
1409/* Event subscription callback. */
1410static pjsip_evsub_user pres_callback =
1411{
1412 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001413 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001414
1415 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1416 * we want to authenticate
1417 */
1418
1419 &pjsua_evsub_on_rx_notify,
1420
1421 NULL, /* on_client_refresh: Use default behaviour, which is to
1422 * refresh client subscription. */
1423
1424 NULL, /* on_server_timeout: Use default behaviour, which is to send
1425 * NOTIFY to terminate.
1426 */
1427};
1428
1429
1430/* It does what it says.. */
1431static void subscribe_buddy_presence(unsigned index)
1432{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001433 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001434 pjsua_buddy *buddy;
1435 int acc_id;
1436 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001437 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001438 pjsip_tx_data *tdata;
1439 pj_status_t status;
1440
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001441 buddy = &pjsua_var.buddy[index];
1442 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001443
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001445
Benny Prijonob4a17c92006-07-10 14:40:21 +00001446 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1447 acc_id, index));
1448
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001449 /* Generate suitable Contact header unless one is already set in
1450 * the account
1451 */
1452 if (acc->contact.slen) {
1453 contact = acc->contact;
1454 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001455 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1456
1457 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001458 acc_id, &buddy->uri);
1459 if (status != PJ_SUCCESS) {
1460 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1461 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001462 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001463 return;
1464 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001465 }
1466
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001467 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001468 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001470 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001471 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001472 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001473 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001474 pjsua_perror(THIS_FILE, "Unable to create dialog",
1475 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001476 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001477 return;
1478 }
1479
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001480 /* Increment the dialog's lock otherwise when presence session creation
1481 * fails the dialog will be destroyed prematurely.
1482 */
1483 pjsip_dlg_inc_lock(buddy->dlg);
1484
Benny Prijonof9c40c32007-06-28 07:20:26 +00001485 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001486 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1487 if (status != PJ_SUCCESS) {
1488 pjsua_var.buddy[index].sub = NULL;
1489 pjsua_perror(THIS_FILE, "Unable to create presence client",
1490 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001491 /* This should destroy the dialog since there's no session
1492 * referencing it
1493 */
1494 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001495 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001496 return;
1497 }
1498
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001499 /* If account is locked to specific transport, then lock dialog
1500 * to this transport too.
1501 */
1502 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1503 pjsip_tpselector tp_sel;
1504
1505 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001506 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001507 }
1508
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 /* Set route-set */
1510 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001511 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512 }
1513
1514 /* Set credentials */
1515 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001516 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001517 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001518 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001519
Benny Prijono48ab2b72007-11-08 09:24:30 +00001520 /* Set authentication preference */
1521 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1522
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001523 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001524
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001525 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001526 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001527 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001528 if (buddy->sub) {
1529 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1530 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001531 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001532 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1533 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001534 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001535 return;
1536 }
1537
Benny Prijono21b9ad92006-08-15 13:11:22 +00001538 pjsua_process_msg_data(tdata, NULL);
1539
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001540 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001541 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001542 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001543 if (buddy->sub) {
1544 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1545 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001547 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1548 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001549 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001550 return;
1551 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001552
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001553 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001554 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001555}
1556
1557
1558/* It does what it says... */
1559static void unsubscribe_buddy_presence(unsigned index)
1560{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001561 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001562 pjsip_tx_data *tdata;
1563 pj_status_t status;
1564
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565 buddy = &pjsua_var.buddy[index];
1566
1567 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001568 return;
1569
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001570 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1571 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001572 return;
1573 }
1574
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001575 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001576 if (status == PJ_SUCCESS) {
1577 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001579 }
Benny Prijono834aee32006-02-19 01:38:06 +00001580
Benny Prijono48da92e2007-05-16 08:56:30 +00001581 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1583 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001584 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1585 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001586 }
1587}
1588
1589
Benny Prijonof9c40c32007-06-28 07:20:26 +00001590/* Lock all buddies */
1591#define LOCK_BUDDIES unsigned cnt_ = 0; \
1592 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1593 unsigned i_; \
1594 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1595 if (pjsua_var.buddy[i_].sub) { \
1596 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1597 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1598 } \
1599 } \
1600 PJSUA_LOCK();
1601
1602/* Unlock all buddies */
1603#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1604 for (i_=0; i_<cnt_; ++i_) { \
1605 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1606 }
1607
1608
1609
Benny Prijono834aee32006-02-19 01:38:06 +00001610/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001612{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001613 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001614
Benny Prijonof9c40c32007-06-28 07:20:26 +00001615 LOCK_BUDDIES;
1616
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001618
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619 if (!pjsua_var.buddy[i].uri.slen)
1620 continue;
1621
1622 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001623 subscribe_buddy_presence(i);
1624
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001626 unsubscribe_buddy_presence(i);
1627
1628 }
1629 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001630
1631 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001632}
1633
Benny Prijono7a5f5102007-05-29 00:33:09 +00001634/* Timer callback to re-create client subscription */
1635static void pres_timer_cb(pj_timer_heap_t *th,
1636 pj_timer_entry *entry)
1637{
Benny Prijono53984d12009-04-28 22:19:49 +00001638 unsigned i;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001639 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1640
Benny Prijono53984d12009-04-28 22:19:49 +00001641 /* Retry failed PUBLISH requests */
1642 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1643 pjsua_acc *acc = &pjsua_var.acc[i];
1644 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
1645 pjsua_pres_init_publish_acc(acc->index);
1646 }
1647
Benny Prijono7a5f5102007-05-29 00:33:09 +00001648 entry->id = PJ_FALSE;
1649 refresh_client_subscriptions();
1650
1651 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1652 entry->id = PJ_TRUE;
1653
Benny Prijonof9c40c32007-06-28 07:20:26 +00001654 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001655}
1656
Benny Prijono834aee32006-02-19 01:38:06 +00001657
1658/*
1659 * Init presence
1660 */
1661pj_status_t pjsua_pres_init()
1662{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001664 pj_status_t status;
1665
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001666 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001667 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001668 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1669 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001670 }
1671
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001672 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1673 reset_buddy(i);
1674 }
1675
Benny Prijono834aee32006-02-19 01:38:06 +00001676 return status;
1677}
1678
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001679
Benny Prijono834aee32006-02-19 01:38:06 +00001680/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001681 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001682 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001684{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001685 /* Start presence timer to re-subscribe to buddy's presence when
1686 * subscription has failed.
1687 */
1688 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1689 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1690
1691 pjsua_var.pres_timer.cb = &pres_timer_cb;
1692 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1693 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001694 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001695 }
1696
Benny Prijono9fc735d2006-05-28 14:58:12 +00001697 return PJ_SUCCESS;
1698}
1699
1700
1701/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001702 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001703 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001704void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001705{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001706 unsigned i;
1707
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001708 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001709
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001710 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1711 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001712 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001713 }
Benny Prijono834aee32006-02-19 01:38:06 +00001714}
1715
1716
1717/*
1718 * Shutdown presence.
1719 */
1720void pjsua_pres_shutdown(void)
1721{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001722 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001723
Benny Prijono7a5f5102007-05-29 00:33:09 +00001724 if (pjsua_var.pres_timer.id != 0) {
1725 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1726 pjsua_var.pres_timer.id = PJ_FALSE;
1727 }
1728
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1730 if (!pjsua_var.acc[i].valid)
1731 continue;
1732 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001733 }
1734
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1736 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001737 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001738
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001739 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001740}