blob: 935a41e781b02b96a8a7f7d72885732499808615 [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 Prijono384dab42009-10-14 01:58:04 +0000685 /* Don't want to accept the request if shutdown is in progress */
686 if (pjsua_var.thread_quit_flag) {
687 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
688 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
689 NULL, NULL);
690 return PJ_TRUE;
691 }
692
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000693 PJSUA_LOCK();
694
Benny Prijonoa91a0032006-02-26 21:23:45 +0000695 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000696 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000697 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000698
Benny Prijono6f979412006-06-15 12:25:46 +0000699 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
700 acc_id));
701
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000702 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000703 if (acc->contact.slen) {
704 contact = acc->contact;
705 } else {
706 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
707 acc_id, rdata);
708 if (status != PJ_SUCCESS) {
709 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
710 status);
711 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000712 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
713 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000714 return PJ_TRUE;
715 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000716 }
717
Benny Prijono834aee32006-02-19 01:38:06 +0000718 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000719 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000720 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000721 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000722 pjsua_perror(THIS_FILE,
723 "Unable to create UAS dialog for subscription",
724 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000725 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000726 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
727 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000728 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000729 }
730
Benny Prijono48ab2b72007-11-08 09:24:30 +0000731 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000732 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000733 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000734
Benny Prijono834aee32006-02-19 01:38:06 +0000735 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000736 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000737 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
738
739 /* Create server presence subscription: */
740 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
741 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000742 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
743 pjsip_tx_data *tdata;
744
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000745 pjsua_perror(THIS_FILE, "Unable to create server subscription",
746 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000747
748 if (code==599 || code > 699 || code < 300) {
749 code = 400;
750 }
751
752 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
753 if (status == PJ_SUCCESS) {
754 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
755 tdata);
756 }
757
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000758 PJSUA_UNLOCK();
759 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000760 }
761
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000762 /* If account is locked to specific transport, then lock dialog
763 * to this transport too.
764 */
765 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
766 pjsip_tpselector tp_sel;
767
768 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
769 pjsip_dlg_set_transport(dlg, &tp_sel);
770 }
771
Benny Prijono834aee32006-02-19 01:38:06 +0000772 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000773 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000774 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000775 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000776 uapres->acc_id = acc_id;
777 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000778 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
779 uapres->remote, PJSIP_MAX_URL_SIZE);
780 if (status < 1)
781 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
782 else
783 uapres->remote[status] = '\0';
784
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000785 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000786
787 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000788 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000789
790
Benny Prijono63fba012008-07-17 14:19:10 +0000791 /* Capture the value of Expires header. */
792 expires_hdr = (pjsip_expires_hdr*)
793 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
794 NULL);
795 if (expires_hdr)
796 uapres->expires = expires_hdr->ivalue;
797 else
798 uapres->expires = -1;
799
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000800 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000801 reason = pj_str("OK");
802 pjsua_msg_data_init(&msg_data);
803
804 /* Notify application callback, if any */
805 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
806 pjsua_buddy_id buddy_id;
807
808 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
809
810 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
811 &dlg->remote.info_str,
812 rdata, &st_code, &reason,
813 &msg_data);
814 }
815
816 /* Handle rejection case */
817 if (st_code >= 300) {
818 pjsip_tx_data *tdata;
819
820 /* Create response */
821 status = pjsip_dlg_create_response(dlg, rdata, st_code,
822 &reason, &tdata);
823 if (status != PJ_SUCCESS) {
824 pjsua_perror(THIS_FILE, "Error creating response", status);
825 pj_list_erase(uapres);
826 pjsip_pres_terminate(sub, PJ_FALSE);
827 PJSUA_UNLOCK();
828 return PJ_FALSE;
829 }
830
831 /* Add header list, if any */
832 pjsua_process_msg_data(tdata, &msg_data);
833
834 /* Send the response */
835 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
836 tdata);
837 if (status != PJ_SUCCESS) {
838 pjsua_perror(THIS_FILE, "Error sending response", status);
839 /* This is not fatal */
840 }
841
842 /* Terminate presence subscription */
843 pj_list_erase(uapres);
844 pjsip_pres_terminate(sub, PJ_FALSE);
845 PJSUA_UNLOCK();
846 return PJ_TRUE;
847 }
848
849 /* Create and send 2xx response to the SUBSCRIBE request: */
850 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000851 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000852 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
853 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000854 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000855 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000856 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000857 return PJ_FALSE;
858 }
859
Benny Prijono63fba012008-07-17 14:19:10 +0000860 /* If code is 200, send NOTIFY now */
861 if (st_code == 200) {
862 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
863 NULL, NULL, PJ_TRUE, &msg_data);
864 }
865
866 /* Done: */
867
868 PJSUA_UNLOCK();
869
870 return PJ_TRUE;
871}
872
873
874/*
875 * Send NOTIFY.
876 */
877PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
878 pjsua_srv_pres *srv_pres,
879 pjsip_evsub_state ev_state,
880 const pj_str_t *state_str,
881 const pj_str_t *reason,
882 pj_bool_t with_body,
883 const pjsua_msg_data *msg_data)
884{
885 pjsua_acc *acc;
886 pjsip_pres_status pres_status;
887 pjsua_buddy_id buddy_id;
888 pjsip_tx_data *tdata;
889 pj_status_t status;
890
891 /* Check parameters */
892 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
893
894 /* Check that account ID is valid */
895 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
896 PJ_EINVAL);
897 /* Check that account is valid */
898 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
899
900 PJSUA_LOCK();
901
902 acc = &pjsua_var.acc[acc_id];
903
904 /* Check that the server presence subscription is still valid */
905 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
906 /* Subscription has been terminated */
907 PJSUA_UNLOCK();
908 return PJ_EINVALIDOP;
909 }
Benny Prijono834aee32006-02-19 01:38:06 +0000910
911 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000912 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000913 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000914 pres_status.info[0].basic_open = acc->online_status;
915 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000916 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000917 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000918 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000919 /* add RPID information */
920 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
921 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000922
Benny Prijono63fba012008-07-17 14:19:10 +0000923 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000924
Benny Prijonoc61cc042007-06-27 13:01:59 +0000925 /* Check expires value. If it's zero, send our presense state but
926 * set subscription state to TERMINATED.
927 */
Benny Prijono63fba012008-07-17 14:19:10 +0000928 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000929 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000930
Benny Prijono63fba012008-07-17 14:19:10 +0000931 /* Create and send the NOTIFY to active subscription: */
932 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
933 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000934 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000935 /* Force removal of message body if msg_body==FALSE */
936 if (!with_body) {
937 tdata->msg->body = NULL;
938 }
939 pjsua_process_msg_data(tdata, msg_data);
940 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000941 }
Benny Prijono834aee32006-02-19 01:38:06 +0000942
943 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000944 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
945 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000946 pj_list_erase(srv_pres);
947 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000948 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000949 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000950 }
951
952
Benny Prijonoa17496a2007-10-31 10:20:31 +0000953 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000954 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000955 if (buddy_id != PJSUA_INVALID_ID) {
956 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
957 if (b->monitor && b->sub == NULL) {
958 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
959 "activating outgoing subscription", buddy_id));
960 subscribe_buddy_presence(buddy_id);
961 }
962 }
963
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000964 PJSUA_UNLOCK();
965
Benny Prijono63fba012008-07-17 14:19:10 +0000966 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000967}
968
969
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000970/*
971 * Client presence publication callback.
972 */
973static void publish_cb(struct pjsip_publishc_cbparam *param)
974{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000975 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000976
977 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
Benny Prijono53984d12009-04-28 22:19:49 +0000978
979 pjsip_publishc_destroy(param->pubc);
980 acc->publish_sess = NULL;
981
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000982 if (param->status != PJ_SUCCESS) {
983 char errmsg[PJ_ERR_MSG_SIZE];
984
985 pj_strerror(param->status, errmsg, sizeof(errmsg));
986 PJ_LOG(1,(THIS_FILE,
987 "Client publication (PUBLISH) failed, status=%d, msg=%s",
988 param->status, errmsg));
Benny Prijono53984d12009-04-28 22:19:49 +0000989 } else if (param->code == 412) {
990 /* 412 (Conditional Request Failed)
991 * The PUBLISH refresh has failed, retry with new one.
992 */
993 pjsua_pres_init_publish_acc(acc->index);
994
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000995 } else {
996 PJ_LOG(1,(THIS_FILE,
997 "Client publication (PUBLISH) failed (%d/%.*s)",
998 param->code, (int)param->reason.slen,
999 param->reason.ptr));
1000 }
1001
Benny Prijono53984d12009-04-28 22:19:49 +00001002 } else {
Benny Prijono534a9ba2009-10-13 14:01:59 +00001003 if (param->expiration < 1) {
Benny Prijono53984d12009-04-28 22:19:49 +00001004 /* Could happen if server "forgot" to include Expires header
1005 * in the response. We will not renew, so destroy the pubc.
1006 */
1007 pjsip_publishc_destroy(param->pubc);
1008 acc->publish_sess = NULL;
1009 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001010 }
1011}
1012
1013
1014/*
1015 * Send PUBLISH request.
1016 */
1017static pj_status_t send_publish(int acc_id, pj_bool_t active)
1018{
1019 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1020 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1021 pjsip_pres_status pres_status;
1022 pjsip_tx_data *tdata;
1023 pj_status_t status;
1024
1025
1026 /* Create PUBLISH request */
1027 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +00001028 char *bpos;
1029 pj_str_t entity;
1030
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001031 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1032 if (status != PJ_SUCCESS) {
1033 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1034 goto on_error;
1035 }
1036
1037 /* Set our online status: */
1038 pj_bzero(&pres_status, sizeof(pres_status));
1039 pres_status.info_cnt = 1;
1040 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001041 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001042 /* .. including RPID information */
1043 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1044 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001045
Benny Prijono8c6e8842007-02-24 15:33:54 +00001046 /* Be careful not to send PIDF with presence entity ID containing
1047 * "<" character.
1048 */
1049 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1050 char *epos = pj_strchr(&acc_cfg->id, '>');
1051 if (epos - bpos < 2) {
1052 pj_assert(!"Unexpected invalid URI");
1053 status = PJSIP_EINVALIDURI;
1054 goto on_error;
1055 }
1056 entity.ptr = bpos+1;
1057 entity.slen = epos - bpos - 1;
1058 } else {
1059 entity = acc_cfg->id;
1060 }
1061
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001062 /* Create and add PIDF message body */
1063 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001064 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001065 if (status != PJ_SUCCESS) {
1066 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1067 status);
1068 pjsip_tx_data_dec_ref(tdata);
1069 goto on_error;
1070 }
1071 } else {
1072 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1073 if (status != PJ_SUCCESS) {
1074 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1075 goto on_error;
1076 }
1077 }
1078
1079 /* Add headers etc */
1080 pjsua_process_msg_data(tdata, NULL);
1081
1082 /* Send the PUBLISH request */
1083 status = pjsip_publishc_send(acc->publish_sess, tdata);
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001084 if (status == PJ_EPENDING) {
1085 PJ_LOG(3,(THIS_FILE, "Previous request is in progress, "
1086 "PUBLISH request is queued"));
1087 } else if (status != PJ_SUCCESS) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001088 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1089 goto on_error;
1090 }
1091
1092 acc->publish_state = acc->online_status;
1093 return PJ_SUCCESS;
1094
1095on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001096 if (acc->publish_sess) {
1097 pjsip_publishc_destroy(acc->publish_sess);
1098 acc->publish_sess = NULL;
1099 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001100 return status;
1101}
1102
1103
1104/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001105pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001106{
1107 const pj_str_t STR_PRESENCE = { "presence", 8 };
1108 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1109 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1110 pj_status_t status;
1111
1112 /* Create and init client publication session */
1113 if (acc_cfg->publish_enabled) {
1114
1115 /* Create client publication */
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001116 status = pjsip_publishc_create(pjsua_var.endpt, &acc_cfg->publish_opt,
1117 acc, &publish_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001118 &acc->publish_sess);
1119 if (status != PJ_SUCCESS) {
1120 acc->publish_sess = NULL;
1121 return status;
1122 }
1123
1124 /* Initialize client publication */
1125 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1126 &acc_cfg->id, &acc_cfg->id,
1127 &acc_cfg->id,
Benny Prijono53984d12009-04-28 22:19:49 +00001128 PJSUA_PUBLISH_EXPIRATION);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001129 if (status != PJ_SUCCESS) {
1130 acc->publish_sess = NULL;
1131 return status;
1132 }
1133
Benny Prijono703b7d72007-03-20 09:13:24 +00001134 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001135 if (acc->cred_cnt) {
1136 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1137 acc->cred);
1138 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001139
1140 /* Set route-set */
1141 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1142
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001143 /* Send initial PUBLISH request */
1144 if (acc->online_status != 0) {
1145 status = send_publish(acc_id, PJ_TRUE);
1146 if (status != PJ_SUCCESS)
1147 return status;
1148 }
1149
1150 } else {
1151 acc->publish_sess = NULL;
1152 }
1153
1154 return PJ_SUCCESS;
1155}
1156
1157
1158/* Init presence for account */
1159pj_status_t pjsua_pres_init_acc(int acc_id)
1160{
1161 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1162
1163 /* Init presence subscription */
1164 pj_list_init(&acc->pres_srv_list);
1165
Benny Prijono8b6834f2007-02-24 13:29:22 +00001166 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001167}
1168
1169
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001170/* Terminate server subscription for the account */
1171void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001172{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001173 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1174 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001175 pjsua_srv_pres *uapres;
1176
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001177 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001178
Benny Prijono922933b2007-01-21 16:23:56 +00001179 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001180 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001181
1182 pjsip_pres_status pres_status;
1183 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001184 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 pjsip_tx_data *tdata;
1186
Benny Prijono5516f912008-05-05 12:06:08 +00001187 next = uapres->next;
1188
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001189 pjsip_pres_get_status(uapres->sub, &pres_status);
1190
1191 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1192 pjsip_pres_set_status(uapres->sub, &pres_status);
1193
1194 if (pjsip_pres_notify(uapres->sub,
1195 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1196 &reason, &tdata)==PJ_SUCCESS)
1197 {
1198 pjsip_pres_send_request(uapres->sub, tdata);
1199 }
1200
Benny Prijono5516f912008-05-05 12:06:08 +00001201 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001202 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001203
Benny Prijono922933b2007-01-21 16:23:56 +00001204 /* Clear server presence subscription list because account might be reused
1205 * later. */
1206 pj_list_init(&acc->pres_srv_list);
1207
1208 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001209 if (acc->publish_sess) {
1210 acc->online_status = PJ_FALSE;
1211 send_publish(acc_id, PJ_FALSE);
Benny Prijono534a9ba2009-10-13 14:01:59 +00001212 /* By ticket #364, don't destroy the session yet (let the callback
1213 destroy it)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001214 if (acc->publish_sess) {
1215 pjsip_publishc_destroy(acc->publish_sess);
1216 acc->publish_sess = NULL;
1217 }
Benny Prijono534a9ba2009-10-13 14:01:59 +00001218 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001219 acc_cfg->publish_enabled = PJ_FALSE;
1220 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221}
1222
1223
Benny Prijono4461c7d2007-08-25 13:36:15 +00001224/* Update server subscription (e.g. when our online status has changed) */
1225void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001226{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001227 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1228 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001229 pjsua_srv_pres *uapres;
1230
1231 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1232
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001233 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001234
1235 pjsip_pres_status pres_status;
1236 pjsip_tx_data *tdata;
1237
1238 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001239
1240 /* Only send NOTIFY once subscription is active. Some subscriptions
1241 * may still be in NULL (when app is adding a new buddy while in the
1242 * on_incoming_subscribe() callback) or PENDING (when user approval is
1243 * being requested) state and we don't send NOTIFY to these subs until
1244 * the user accepted the request.
1245 */
1246 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1247 (force || pres_status.info[0].basic_open != acc->online_status))
1248 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001249
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001250 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001251 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1252 sizeof(pjrpid_element));
1253
Benny Prijono834aee32006-02-19 01:38:06 +00001254 pjsip_pres_set_status(uapres->sub, &pres_status);
1255
Benny Prijono21b9ad92006-08-15 13:11:22 +00001256 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1257 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001259 }
Benny Prijono834aee32006-02-19 01:38:06 +00001260 }
1261
1262 uapres = uapres->next;
1263 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001264
Benny Prijono8b6834f2007-02-24 13:29:22 +00001265 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1266 * session. If we don't have a PUBLISH session, then it could be
1267 * that we're waiting until registration has completed before we
1268 * send the first PUBLISH.
1269 */
1270 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001271 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001272 send_publish(acc_id, PJ_TRUE);
1273 }
1274 }
Benny Prijono834aee32006-02-19 01:38:06 +00001275}
1276
1277
1278
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001279/***************************************************************************
1280 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001281 */
1282
1283/* Callback called when *client* subscription state has changed. */
1284static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1285{
1286 pjsua_buddy *buddy;
1287
1288 PJ_UNUSED_ARG(event);
1289
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001290 PJSUA_LOCK();
1291
Benny Prijonoa1e69682007-05-11 15:14:34 +00001292 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001293 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001294 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001295 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001296 (int)pjsua_var.buddy[buddy->index].uri.slen,
1297 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001298 pjsip_evsub_get_state_name(sub)));
1299
1300 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001301 if (buddy->term_reason.ptr == NULL) {
1302 buddy->term_reason.ptr = (char*)
1303 pj_pool_alloc(buddy->pool,
1304 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1305 }
1306 pj_strncpy(&buddy->term_reason,
1307 pjsip_evsub_get_termination_reason(sub),
1308 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1309 } else {
1310 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001311 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001312
1313 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001314 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1315 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001316
1317 /* Clear subscription */
1318 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1319 buddy->sub = NULL;
1320 buddy->status.info_cnt = 0;
1321 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1322 }
Benny Prijono834aee32006-02-19 01:38:06 +00001323 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001324
1325 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001326}
1327
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001328
1329/* Callback when transaction state has changed. */
1330static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1331 pjsip_transaction *tsx,
1332 pjsip_event *event)
1333{
1334 pjsua_buddy *buddy;
1335 pjsip_contact_hdr *contact_hdr;
1336
1337 PJSUA_LOCK();
1338
Benny Prijonoa1e69682007-05-11 15:14:34 +00001339 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001340 if (!buddy) {
1341 PJSUA_UNLOCK();
1342 return;
1343 }
1344
1345 /* We only use this to update buddy's Contact, when it's not
1346 * set.
1347 */
1348 if (buddy->contact.slen != 0) {
1349 /* Contact already set */
1350 PJSUA_UNLOCK();
1351 return;
1352 }
1353
1354 /* Only care about 2xx response to outgoing SUBSCRIBE */
1355 if (tsx->status_code/100 != 2 ||
1356 tsx->role != PJSIP_UAC_ROLE ||
1357 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001358 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 {
1360 PJSUA_UNLOCK();
1361 return;
1362 }
1363
1364 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001365 contact_hdr = (pjsip_contact_hdr*)
1366 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367 PJSIP_H_CONTACT, NULL);
1368 if (!contact_hdr) {
1369 PJSUA_UNLOCK();
1370 return;
1371 }
1372
Benny Prijonoa1e69682007-05-11 15:14:34 +00001373 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001374 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1376 contact_hdr->uri,
1377 buddy->contact.ptr,
1378 PJSIP_MAX_URL_SIZE);
1379 if (buddy->contact.slen < 0)
1380 buddy->contact.slen = 0;
1381
1382 PJSUA_UNLOCK();
1383}
1384
1385
Benny Prijono834aee32006-02-19 01:38:06 +00001386/* Callback called when we receive NOTIFY */
1387static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1388 pjsip_rx_data *rdata,
1389 int *p_st_code,
1390 pj_str_t **p_st_text,
1391 pjsip_hdr *res_hdr,
1392 pjsip_msg_body **p_body)
1393{
1394 pjsua_buddy *buddy;
1395
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001396 PJSUA_LOCK();
1397
Benny Prijonoa1e69682007-05-11 15:14:34 +00001398 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001399 if (buddy) {
1400 /* Update our info. */
1401 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001402 }
1403
1404 /* The default is to send 200 response to NOTIFY.
1405 * Just leave it there..
1406 */
1407 PJ_UNUSED_ARG(rdata);
1408 PJ_UNUSED_ARG(p_st_code);
1409 PJ_UNUSED_ARG(p_st_text);
1410 PJ_UNUSED_ARG(res_hdr);
1411 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412
1413 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001414}
1415
1416
1417/* Event subscription callback. */
1418static pjsip_evsub_user pres_callback =
1419{
1420 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001421 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001422
1423 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1424 * we want to authenticate
1425 */
1426
1427 &pjsua_evsub_on_rx_notify,
1428
1429 NULL, /* on_client_refresh: Use default behaviour, which is to
1430 * refresh client subscription. */
1431
1432 NULL, /* on_server_timeout: Use default behaviour, which is to send
1433 * NOTIFY to terminate.
1434 */
1435};
1436
1437
1438/* It does what it says.. */
1439static void subscribe_buddy_presence(unsigned index)
1440{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001441 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 pjsua_buddy *buddy;
1443 int acc_id;
1444 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001445 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001446 pjsip_tx_data *tdata;
1447 pj_status_t status;
1448
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001449 buddy = &pjsua_var.buddy[index];
1450 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001451
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001452 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001453
Benny Prijonob4a17c92006-07-10 14:40:21 +00001454 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1455 acc_id, index));
1456
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001457 /* Generate suitable Contact header unless one is already set in
1458 * the account
1459 */
1460 if (acc->contact.slen) {
1461 contact = acc->contact;
1462 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001463 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1464
1465 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001466 acc_id, &buddy->uri);
1467 if (status != PJ_SUCCESS) {
1468 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1469 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001470 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001471 return;
1472 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001473 }
1474
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001475 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001476 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001477 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001478 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001479 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001480 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001481 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001482 pjsua_perror(THIS_FILE, "Unable to create dialog",
1483 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001484 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001485 return;
1486 }
1487
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001488 /* Increment the dialog's lock otherwise when presence session creation
1489 * fails the dialog will be destroyed prematurely.
1490 */
1491 pjsip_dlg_inc_lock(buddy->dlg);
1492
Benny Prijonof9c40c32007-06-28 07:20:26 +00001493 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001494 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1495 if (status != PJ_SUCCESS) {
1496 pjsua_var.buddy[index].sub = NULL;
1497 pjsua_perror(THIS_FILE, "Unable to create presence client",
1498 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001499 /* This should destroy the dialog since there's no session
1500 * referencing it
1501 */
1502 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001503 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001504 return;
1505 }
1506
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001507 /* If account is locked to specific transport, then lock dialog
1508 * to this transport too.
1509 */
1510 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1511 pjsip_tpselector tp_sel;
1512
1513 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001514 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001515 }
1516
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001517 /* Set route-set */
1518 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001519 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001520 }
1521
1522 /* Set credentials */
1523 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001524 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001525 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001526 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001527
Benny Prijono48ab2b72007-11-08 09:24:30 +00001528 /* Set authentication preference */
1529 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1530
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001531 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001532
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001533 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001534 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001535 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001536 if (buddy->sub) {
1537 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1538 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001539 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001540 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1541 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001542 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001543 return;
1544 }
1545
Benny Prijono21b9ad92006-08-15 13:11:22 +00001546 pjsua_process_msg_data(tdata, NULL);
1547
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001548 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001549 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001550 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001551 if (buddy->sub) {
1552 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1553 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001554 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001555 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1556 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001557 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001558 return;
1559 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001560
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001561 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001562 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001563}
1564
1565
1566/* It does what it says... */
1567static void unsubscribe_buddy_presence(unsigned index)
1568{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001569 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001570 pjsip_tx_data *tdata;
1571 pj_status_t status;
1572
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573 buddy = &pjsua_var.buddy[index];
1574
1575 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001576 return;
1577
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1579 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001580 return;
1581 }
1582
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001583 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001584 if (status == PJ_SUCCESS) {
1585 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001587 }
Benny Prijono834aee32006-02-19 01:38:06 +00001588
Benny Prijono48da92e2007-05-16 08:56:30 +00001589 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001590 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1591 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001592 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1593 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001594 }
1595}
1596
1597
Benny Prijonof9c40c32007-06-28 07:20:26 +00001598/* Lock all buddies */
1599#define LOCK_BUDDIES unsigned cnt_ = 0; \
1600 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1601 unsigned i_; \
1602 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1603 if (pjsua_var.buddy[i_].sub) { \
1604 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1605 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1606 } \
1607 } \
1608 PJSUA_LOCK();
1609
1610/* Unlock all buddies */
1611#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1612 for (i_=0; i_<cnt_; ++i_) { \
1613 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1614 }
1615
1616
1617
Benny Prijono834aee32006-02-19 01:38:06 +00001618/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001620{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001621 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001622
Benny Prijonof9c40c32007-06-28 07:20:26 +00001623 LOCK_BUDDIES;
1624
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001626
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627 if (!pjsua_var.buddy[i].uri.slen)
1628 continue;
1629
1630 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001631 subscribe_buddy_presence(i);
1632
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001633 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001634 unsubscribe_buddy_presence(i);
1635
1636 }
1637 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001638
1639 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001640}
1641
Benny Prijono7a5f5102007-05-29 00:33:09 +00001642/* Timer callback to re-create client subscription */
1643static void pres_timer_cb(pj_timer_heap_t *th,
1644 pj_timer_entry *entry)
1645{
Benny Prijono53984d12009-04-28 22:19:49 +00001646 unsigned i;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001647 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1648
Benny Prijono53984d12009-04-28 22:19:49 +00001649 /* Retry failed PUBLISH requests */
1650 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1651 pjsua_acc *acc = &pjsua_var.acc[i];
1652 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
1653 pjsua_pres_init_publish_acc(acc->index);
1654 }
1655
Benny Prijono7a5f5102007-05-29 00:33:09 +00001656 entry->id = PJ_FALSE;
1657 refresh_client_subscriptions();
1658
1659 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1660 entry->id = PJ_TRUE;
1661
Benny Prijonof9c40c32007-06-28 07:20:26 +00001662 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001663}
1664
Benny Prijono834aee32006-02-19 01:38:06 +00001665
1666/*
1667 * Init presence
1668 */
1669pj_status_t pjsua_pres_init()
1670{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001671 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001672 pj_status_t status;
1673
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001674 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001675 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001676 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1677 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001678 }
1679
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1681 reset_buddy(i);
1682 }
1683
Benny Prijono834aee32006-02-19 01:38:06 +00001684 return status;
1685}
1686
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001687
Benny Prijono834aee32006-02-19 01:38:06 +00001688/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001689 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001690 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001691pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001692{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001693 /* Start presence timer to re-subscribe to buddy's presence when
1694 * subscription has failed.
1695 */
1696 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1697 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1698
1699 pjsua_var.pres_timer.cb = &pres_timer_cb;
1700 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1701 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001702 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001703 }
1704
Benny Prijono9fc735d2006-05-28 14:58:12 +00001705 return PJ_SUCCESS;
1706}
1707
1708
1709/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001710 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001711 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001712void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001713{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001714 unsigned i;
1715
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001717
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001718 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1719 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001720 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001721 }
Benny Prijono834aee32006-02-19 01:38:06 +00001722}
1723
1724
1725/*
1726 * Shutdown presence.
1727 */
1728void pjsua_pres_shutdown(void)
1729{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001730 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001731
Benny Prijono384dab42009-10-14 01:58:04 +00001732 PJ_LOG(4,(THIS_FILE, "Shutting down presence.."));
1733
Benny Prijono7a5f5102007-05-29 00:33:09 +00001734 if (pjsua_var.pres_timer.id != 0) {
1735 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1736 pjsua_var.pres_timer.id = PJ_FALSE;
1737 }
1738
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001739 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1740 if (!pjsua_var.acc[i].valid)
1741 continue;
1742 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001743 }
1744
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001745 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1746 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001747 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001748
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001749 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001750}