blob: 797fb84cfcf3c95e6d2bc10534bb40af96e4101a [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);
198 if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
199 total < sizeof(info->buf_))
200 {
201 info->sub_term_reason.ptr = info->buf_ + total;
202 pj_strncpy(&info->sub_term_reason,
203 pjsip_evsub_get_termination_reason(buddy->sub),
204 sizeof(info->buf_) - total);
205 total += info->sub_term_reason.slen;
206 } else {
207 info->sub_term_reason = pj_str("");
208 }
209 } else if (total < sizeof(info->buf_)) {
210 info->sub_term_reason.ptr = info->buf_ + total;
211 pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
212 sizeof(info->buf_) - total);
213 total += info->sub_term_reason.slen;
214 } else {
215 info->sub_term_reason = pj_str("");
216 }
217
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218 PJSUA_UNLOCK();
219 return PJ_SUCCESS;
220}
221
Benny Prijono705e7842008-07-21 18:12:51 +0000222/*
223 * Set the user data associated with the buddy object.
224 */
225PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
226 void *user_data)
227{
228 PJ_ASSERT_RETURN(buddy_id>=0 &&
229 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
230 PJ_EINVAL);
231
232 PJSUA_LOCK();
233
234 pjsua_var.buddy[buddy_id].user_data = user_data;
235
236 PJSUA_UNLOCK();
237
238 return PJ_SUCCESS;
239}
240
241
242/*
243 * Get the user data associated with the budy object.
244 */
245PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
246{
247 void *user_data;
248
249 PJ_ASSERT_RETURN(buddy_id>=0 &&
250 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
251 NULL);
252
253 PJSUA_LOCK();
254
255 user_data = pjsua_var.buddy[buddy_id].user_data;
256
257 PJSUA_UNLOCK();
258
259 return user_data;
260}
261
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000262
263/*
264 * Reset buddy descriptor.
265 */
266static void reset_buddy(pjsua_buddy_id id)
267{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000268 pj_pool_t *pool = pjsua_var.buddy[id].pool;
Benny Prijonoac623b32006-07-03 15:19:31 +0000269 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000270 pjsua_var.buddy[id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000271 pjsua_var.buddy[id].index = id;
272}
273
274
275/*
276 * Add new buddy.
277 */
278PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
279 pjsua_buddy_id *p_buddy_id)
280{
281 pjsip_name_addr *url;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000282 pjsua_buddy *buddy;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000283 pjsip_sip_uri *sip_uri;
284 int index;
285 pj_str_t tmp;
286
287 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
288 PJ_ARRAY_SIZE(pjsua_var.buddy),
289 PJ_ETOOMANY);
290
291 PJSUA_LOCK();
292
293 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000294 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000295 if (pjsua_var.buddy[index].uri.slen == 0)
296 break;
297 }
298
299 /* Expect to find an empty slot */
300 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
301 PJSUA_UNLOCK();
302 /* This shouldn't happen */
303 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
304 return PJ_ETOOMANY;
305 }
306
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000307 buddy = &pjsua_var.buddy[index];
308
309 /* Create pool for this buddy */
310 if (buddy->pool) {
311 pj_pool_reset(buddy->pool);
312 } else {
313 char name[PJ_MAX_OBJ_NAME];
314 pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
315 buddy->pool = pjsua_pool_create(name, 512, 256);
316 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000317
Benny Prijono63fba012008-07-17 14:19:10 +0000318 /* Init buffers for presence subscription status */
319 buddy->term_reason.ptr = (char*)
320 pj_pool_alloc(buddy->pool,
321 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
322
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000323 /* Get name and display name for buddy */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000324 pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
325 url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000326 PJSIP_PARSE_URI_AS_NAMEADDR);
327
328 if (url == NULL) {
329 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000330 pj_pool_release(buddy->pool);
331 buddy->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000332 PJSUA_UNLOCK();
333 return PJSIP_EINVALIDURI;
334 }
335
Benny Prijonofc493592007-02-18 20:56:32 +0000336 /* Only support SIP schemes */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000337 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
338 pj_pool_release(buddy->pool);
339 buddy->pool = NULL;
340 PJSUA_UNLOCK();
Benny Prijonofc493592007-02-18 20:56:32 +0000341 return PJSIP_EINVALIDSCHEME;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000342 }
Benny Prijonofc493592007-02-18 20:56:32 +0000343
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000344 /* Reset buddy, to make sure everything is cleared with default
345 * values
346 */
347 reset_buddy(index);
348
349 /* Save URI */
350 pjsua_var.buddy[index].uri = tmp;
351
Benny Prijono9c1528f2007-02-10 19:22:25 +0000352 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000353 pjsua_var.buddy[index].name = sip_uri->user;
354 pjsua_var.buddy[index].display = url->display;
355 pjsua_var.buddy[index].host = sip_uri->host;
356 pjsua_var.buddy[index].port = sip_uri->port;
357 pjsua_var.buddy[index].monitor = cfg->subscribe;
358 if (pjsua_var.buddy[index].port == 0)
359 pjsua_var.buddy[index].port = 5060;
360
Benny Prijono705e7842008-07-21 18:12:51 +0000361 /* Save user data */
362 pjsua_var.buddy[index].user_data = (void*)cfg->user_data;
363
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000364 if (p_buddy_id)
365 *p_buddy_id = index;
366
367 pjsua_var.buddy_cnt++;
368
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000369 PJSUA_UNLOCK();
370
Benny Prijonof9c40c32007-06-28 07:20:26 +0000371 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
372
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000373 return PJ_SUCCESS;
374}
375
376
377/*
378 * Delete buddy.
379 */
380PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
381{
382 PJ_ASSERT_RETURN(buddy_id>=0 &&
383 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
384 PJ_EINVAL);
385
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000386 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 return PJ_SUCCESS;
388 }
389
390 /* Unsubscribe presence */
391 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
392
Benny Prijonof9c40c32007-06-28 07:20:26 +0000393 PJSUA_LOCK();
394
Benny Prijonoa5776cb2009-04-14 15:11:23 +0000395 /* Not interested with further events for this buddy */
396 if (pjsua_var.buddy[buddy_id].sub) {
397 pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub,
398 pjsua_var.mod.id, NULL);
399 }
400
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000401 /* Remove buddy */
402 pjsua_var.buddy[buddy_id].uri.slen = 0;
403 pjsua_var.buddy_cnt--;
404
405 /* Reset buddy struct */
406 reset_buddy(buddy_id);
407
408 PJSUA_UNLOCK();
409 return PJ_SUCCESS;
410}
411
412
413/*
414 * Enable/disable buddy's presence monitoring.
415 */
416PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
417 pj_bool_t subscribe)
418{
419 pjsua_buddy *buddy;
420
421 PJ_ASSERT_RETURN(buddy_id>=0 &&
422 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
423 PJ_EINVAL);
424
425 PJSUA_LOCK();
426
427 buddy = &pjsua_var.buddy[buddy_id];
428 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000429
430 PJSUA_UNLOCK();
431
Benny Prijonof9c40c32007-06-28 07:20:26 +0000432 pjsua_pres_refresh();
433
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000434 return PJ_SUCCESS;
435}
436
437
438/*
Benny Prijono10861432007-10-31 10:54:53 +0000439 * Update buddy's presence.
440 */
441PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
442{
443 pjsua_buddy *buddy;
444
445 PJ_ASSERT_RETURN(buddy_id>=0 &&
446 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
447 PJ_EINVAL);
448
449 PJSUA_LOCK();
450
451 buddy = &pjsua_var.buddy[buddy_id];
452
453 /* Return error if buddy's presence monitoring is not enabled */
454 if (!buddy->monitor) {
455 PJSUA_UNLOCK();
456 return PJ_EINVALIDOP;
457 }
458
459 /* Ignore if presence is already active for the buddy */
460 if (buddy->sub) {
461 PJSUA_UNLOCK();
462 return PJ_SUCCESS;
463 }
464
465 /* Initiate presence subscription */
466 subscribe_buddy_presence(buddy_id);
467
468 PJSUA_UNLOCK();
469
470 return PJ_SUCCESS;
471}
472
473
474/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000475 * Dump presence subscriptions to log file.
476 */
477PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
478{
479 unsigned acc_id;
480 unsigned i;
481
482
483 PJSUA_LOCK();
484
485 /*
486 * When no detail is required, just dump number of server and client
487 * subscriptions.
488 */
489 if (verbose == PJ_FALSE) {
490
491 int count = 0;
492
493 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
494
495 if (!pjsua_var.acc[acc_id].valid)
496 continue;
497
498 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
499 struct pjsua_srv_pres *uapres;
500
501 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
502 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
503 ++count;
504 uapres = uapres->next;
505 }
506 }
507 }
508
509 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
510 count));
511
512 count = 0;
513
514 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
515 if (pjsua_var.buddy[i].uri.slen == 0)
516 continue;
517 if (pjsua_var.buddy[i].sub) {
518 ++count;
519 }
520 }
521
522 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
523 count));
524 PJSUA_UNLOCK();
525 return;
526 }
527
528
529 /*
530 * Dumping all server (UAS) subscriptions
531 */
532 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
533
534 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
535
536 if (!pjsua_var.acc[acc_id].valid)
537 continue;
538
539 PJ_LOG(3,(THIS_FILE, " %.*s",
540 (int)pjsua_var.acc[acc_id].cfg.id.slen,
541 pjsua_var.acc[acc_id].cfg.id.ptr));
542
543 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
544
545 PJ_LOG(3,(THIS_FILE, " - none - "));
546
547 } else {
548 struct pjsua_srv_pres *uapres;
549
550 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
551 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
552
553 PJ_LOG(3,(THIS_FILE, " %10s %s",
554 pjsip_evsub_get_state_name(uapres->sub),
555 uapres->remote));
556
557 uapres = uapres->next;
558 }
559 }
560 }
561
562 /*
563 * Dumping all client (UAC) subscriptions
564 */
565 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
566
567 if (pjsua_var.buddy_cnt == 0) {
568
569 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
570
571 } else {
572 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
573
574 if (pjsua_var.buddy[i].uri.slen == 0)
575 continue;
576
577 if (pjsua_var.buddy[i].sub) {
578 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
579 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
580 (int)pjsua_var.buddy[i].uri.slen,
581 pjsua_var.buddy[i].uri.ptr));
582 } else {
583 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
584 "(null)",
585 (int)pjsua_var.buddy[i].uri.slen,
586 pjsua_var.buddy[i].uri.ptr));
587 }
588 }
589 }
590
591 PJSUA_UNLOCK();
592}
593
594
595/***************************************************************************
596 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000597 */
598
599/* Proto */
600static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
601
602/* The module instance. */
603static pjsip_module mod_pjsua_pres =
604{
605 NULL, NULL, /* prev, next. */
606 { "mod-pjsua-pres", 14 }, /* Name. */
607 -1, /* Id */
608 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000609 NULL, /* load() */
610 NULL, /* start() */
611 NULL, /* stop() */
612 NULL, /* unload() */
613 &pres_on_rx_request, /* on_rx_request() */
614 NULL, /* on_rx_response() */
615 NULL, /* on_tx_request. */
616 NULL, /* on_tx_response() */
617 NULL, /* on_tsx_state() */
618
619};
620
621
622/* Callback called when *server* subscription state has changed. */
623static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
624{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000625 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000626
627 PJ_UNUSED_ARG(event);
628
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000629 PJSUA_LOCK();
630
Benny Prijonoa1e69682007-05-11 15:14:34 +0000631 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000632 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000633 pjsip_evsub_state state;
634
Benny Prijonoba736c42008-07-10 20:45:03 +0000635 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000636 uapres->remote, pjsip_evsub_get_state_name(sub)));
637
Benny Prijono63fba012008-07-17 14:19:10 +0000638 state = pjsip_evsub_get_state(sub);
639
640 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
641 pj_str_t from;
642
643 from = uapres->dlg->remote.info_str;
644 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
645 uapres, &from,
646 state, event);
647 }
648
649 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000650 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000651 pj_list_erase(uapres);
652 }
653 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654
655 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000656}
657
658/* This is called when request is received.
659 * We need to check for incoming SUBSCRIBE request.
660 */
661static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
662{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000663 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000664 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000665 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000666 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
667 pjsua_srv_pres *uapres;
668 pjsip_evsub *sub;
669 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000670 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000671 pjsip_status_code st_code;
672 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000673 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000674 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000675 pj_status_t status;
676
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000677 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000678 return PJ_FALSE;
679
680 /* Incoming SUBSCRIBE: */
681
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682 PJSUA_LOCK();
683
Benny Prijonoa91a0032006-02-26 21:23:45 +0000684 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000685 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000686 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000687
Benny Prijono6f979412006-06-15 12:25:46 +0000688 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
689 acc_id));
690
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000691 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000692 if (acc->contact.slen) {
693 contact = acc->contact;
694 } else {
695 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
696 acc_id, rdata);
697 if (status != PJ_SUCCESS) {
698 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
699 status);
700 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000701 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
702 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000703 return PJ_TRUE;
704 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000705 }
706
Benny Prijono834aee32006-02-19 01:38:06 +0000707 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000708 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000709 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000710 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000711 pjsua_perror(THIS_FILE,
712 "Unable to create UAS dialog for subscription",
713 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000714 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000715 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
716 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000717 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000718 }
719
Benny Prijono48ab2b72007-11-08 09:24:30 +0000720 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000721 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000722 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000723
Benny Prijono834aee32006-02-19 01:38:06 +0000724 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000725 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000726 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
727
728 /* Create server presence subscription: */
729 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
730 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000731 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
732 pjsip_tx_data *tdata;
733
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000734 pjsua_perror(THIS_FILE, "Unable to create server subscription",
735 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000736
737 if (code==599 || code > 699 || code < 300) {
738 code = 400;
739 }
740
741 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
742 if (status == PJ_SUCCESS) {
743 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
744 tdata);
745 }
746
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000747 PJSUA_UNLOCK();
748 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000749 }
750
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000751 /* If account is locked to specific transport, then lock dialog
752 * to this transport too.
753 */
754 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
755 pjsip_tpselector tp_sel;
756
757 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
758 pjsip_dlg_set_transport(dlg, &tp_sel);
759 }
760
Benny Prijono834aee32006-02-19 01:38:06 +0000761 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000762 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000763 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000764 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000765 uapres->acc_id = acc_id;
766 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000767 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
768 uapres->remote, PJSIP_MAX_URL_SIZE);
769 if (status < 1)
770 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
771 else
772 uapres->remote[status] = '\0';
773
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000774 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000775
776 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000777 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000778
779
Benny Prijono63fba012008-07-17 14:19:10 +0000780 /* Capture the value of Expires header. */
781 expires_hdr = (pjsip_expires_hdr*)
782 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
783 NULL);
784 if (expires_hdr)
785 uapres->expires = expires_hdr->ivalue;
786 else
787 uapres->expires = -1;
788
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000789 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000790 reason = pj_str("OK");
791 pjsua_msg_data_init(&msg_data);
792
793 /* Notify application callback, if any */
794 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
795 pjsua_buddy_id buddy_id;
796
797 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
798
799 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
800 &dlg->remote.info_str,
801 rdata, &st_code, &reason,
802 &msg_data);
803 }
804
805 /* Handle rejection case */
806 if (st_code >= 300) {
807 pjsip_tx_data *tdata;
808
809 /* Create response */
810 status = pjsip_dlg_create_response(dlg, rdata, st_code,
811 &reason, &tdata);
812 if (status != PJ_SUCCESS) {
813 pjsua_perror(THIS_FILE, "Error creating response", status);
814 pj_list_erase(uapres);
815 pjsip_pres_terminate(sub, PJ_FALSE);
816 PJSUA_UNLOCK();
817 return PJ_FALSE;
818 }
819
820 /* Add header list, if any */
821 pjsua_process_msg_data(tdata, &msg_data);
822
823 /* Send the response */
824 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
825 tdata);
826 if (status != PJ_SUCCESS) {
827 pjsua_perror(THIS_FILE, "Error sending response", status);
828 /* This is not fatal */
829 }
830
831 /* Terminate presence subscription */
832 pj_list_erase(uapres);
833 pjsip_pres_terminate(sub, PJ_FALSE);
834 PJSUA_UNLOCK();
835 return PJ_TRUE;
836 }
837
838 /* Create and send 2xx response to the SUBSCRIBE request: */
839 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000840 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000841 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
842 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000843 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000844 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000845 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000846 return PJ_FALSE;
847 }
848
Benny Prijono63fba012008-07-17 14:19:10 +0000849 /* If code is 200, send NOTIFY now */
850 if (st_code == 200) {
851 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
852 NULL, NULL, PJ_TRUE, &msg_data);
853 }
854
855 /* Done: */
856
857 PJSUA_UNLOCK();
858
859 return PJ_TRUE;
860}
861
862
863/*
864 * Send NOTIFY.
865 */
866PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
867 pjsua_srv_pres *srv_pres,
868 pjsip_evsub_state ev_state,
869 const pj_str_t *state_str,
870 const pj_str_t *reason,
871 pj_bool_t with_body,
872 const pjsua_msg_data *msg_data)
873{
874 pjsua_acc *acc;
875 pjsip_pres_status pres_status;
876 pjsua_buddy_id buddy_id;
877 pjsip_tx_data *tdata;
878 pj_status_t status;
879
880 /* Check parameters */
881 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
882
883 /* Check that account ID is valid */
884 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
885 PJ_EINVAL);
886 /* Check that account is valid */
887 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
888
889 PJSUA_LOCK();
890
891 acc = &pjsua_var.acc[acc_id];
892
893 /* Check that the server presence subscription is still valid */
894 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
895 /* Subscription has been terminated */
896 PJSUA_UNLOCK();
897 return PJ_EINVALIDOP;
898 }
Benny Prijono834aee32006-02-19 01:38:06 +0000899
900 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000901 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000902 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000903 pres_status.info[0].basic_open = acc->online_status;
904 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000905 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000906 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000907 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000908 /* add RPID information */
909 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
910 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000911
Benny Prijono63fba012008-07-17 14:19:10 +0000912 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000913
Benny Prijonoc61cc042007-06-27 13:01:59 +0000914 /* Check expires value. If it's zero, send our presense state but
915 * set subscription state to TERMINATED.
916 */
Benny Prijono63fba012008-07-17 14:19:10 +0000917 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000918 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000919
Benny Prijono63fba012008-07-17 14:19:10 +0000920 /* Create and send the NOTIFY to active subscription: */
921 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
922 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000923 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000924 /* Force removal of message body if msg_body==FALSE */
925 if (!with_body) {
926 tdata->msg->body = NULL;
927 }
928 pjsua_process_msg_data(tdata, msg_data);
929 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000930 }
Benny Prijono834aee32006-02-19 01:38:06 +0000931
932 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000933 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
934 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000935 pj_list_erase(srv_pres);
936 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000937 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000938 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000939 }
940
941
Benny Prijonoa17496a2007-10-31 10:20:31 +0000942 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000943 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000944 if (buddy_id != PJSUA_INVALID_ID) {
945 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
946 if (b->monitor && b->sub == NULL) {
947 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
948 "activating outgoing subscription", buddy_id));
949 subscribe_buddy_presence(buddy_id);
950 }
951 }
952
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000953 PJSUA_UNLOCK();
954
Benny Prijono63fba012008-07-17 14:19:10 +0000955 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000956}
957
958
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000959/*
960 * Client presence publication callback.
961 */
962static void publish_cb(struct pjsip_publishc_cbparam *param)
963{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000964 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000965
966 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
Benny Prijono53984d12009-04-28 22:19:49 +0000967
968 pjsip_publishc_destroy(param->pubc);
969 acc->publish_sess = NULL;
970
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000971 if (param->status != PJ_SUCCESS) {
972 char errmsg[PJ_ERR_MSG_SIZE];
973
974 pj_strerror(param->status, errmsg, sizeof(errmsg));
975 PJ_LOG(1,(THIS_FILE,
976 "Client publication (PUBLISH) failed, status=%d, msg=%s",
977 param->status, errmsg));
Benny Prijono53984d12009-04-28 22:19:49 +0000978 } else if (param->code == 412) {
979 /* 412 (Conditional Request Failed)
980 * The PUBLISH refresh has failed, retry with new one.
981 */
982 pjsua_pres_init_publish_acc(acc->index);
983
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000984 } else {
985 PJ_LOG(1,(THIS_FILE,
986 "Client publication (PUBLISH) failed (%d/%.*s)",
987 param->code, (int)param->reason.slen,
988 param->reason.ptr));
989 }
990
Benny Prijono53984d12009-04-28 22:19:49 +0000991 } else {
992 if (param->expiration == -1) {
993 /* Could happen if server "forgot" to include Expires header
994 * in the response. We will not renew, so destroy the pubc.
995 */
996 pjsip_publishc_destroy(param->pubc);
997 acc->publish_sess = NULL;
998 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000999 }
1000}
1001
1002
1003/*
1004 * Send PUBLISH request.
1005 */
1006static pj_status_t send_publish(int acc_id, pj_bool_t active)
1007{
1008 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1009 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1010 pjsip_pres_status pres_status;
1011 pjsip_tx_data *tdata;
1012 pj_status_t status;
1013
1014
1015 /* Create PUBLISH request */
1016 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +00001017 char *bpos;
1018 pj_str_t entity;
1019
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001020 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1021 if (status != PJ_SUCCESS) {
1022 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1023 goto on_error;
1024 }
1025
1026 /* Set our online status: */
1027 pj_bzero(&pres_status, sizeof(pres_status));
1028 pres_status.info_cnt = 1;
1029 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001030 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001031 /* .. including RPID information */
1032 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1033 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001034
Benny Prijono8c6e8842007-02-24 15:33:54 +00001035 /* Be careful not to send PIDF with presence entity ID containing
1036 * "<" character.
1037 */
1038 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1039 char *epos = pj_strchr(&acc_cfg->id, '>');
1040 if (epos - bpos < 2) {
1041 pj_assert(!"Unexpected invalid URI");
1042 status = PJSIP_EINVALIDURI;
1043 goto on_error;
1044 }
1045 entity.ptr = bpos+1;
1046 entity.slen = epos - bpos - 1;
1047 } else {
1048 entity = acc_cfg->id;
1049 }
1050
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001051 /* Create and add PIDF message body */
1052 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001053 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001054 if (status != PJ_SUCCESS) {
1055 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1056 status);
1057 pjsip_tx_data_dec_ref(tdata);
1058 goto on_error;
1059 }
1060 } else {
1061 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1062 if (status != PJ_SUCCESS) {
1063 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1064 goto on_error;
1065 }
1066 }
1067
1068 /* Add headers etc */
1069 pjsua_process_msg_data(tdata, NULL);
1070
1071 /* Send the PUBLISH request */
1072 status = pjsip_publishc_send(acc->publish_sess, tdata);
1073 if (status != PJ_SUCCESS) {
1074 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1075 goto on_error;
1076 }
1077
1078 acc->publish_state = acc->online_status;
1079 return PJ_SUCCESS;
1080
1081on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001082 if (acc->publish_sess) {
1083 pjsip_publishc_destroy(acc->publish_sess);
1084 acc->publish_sess = NULL;
1085 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001086 return status;
1087}
1088
1089
1090/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001091pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001092{
1093 const pj_str_t STR_PRESENCE = { "presence", 8 };
1094 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1095 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1096 pj_status_t status;
1097
1098 /* Create and init client publication session */
1099 if (acc_cfg->publish_enabled) {
1100
1101 /* Create client publication */
1102 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
1103 &acc->publish_sess);
1104 if (status != PJ_SUCCESS) {
1105 acc->publish_sess = NULL;
1106 return status;
1107 }
1108
1109 /* Initialize client publication */
1110 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1111 &acc_cfg->id, &acc_cfg->id,
1112 &acc_cfg->id,
Benny Prijono53984d12009-04-28 22:19:49 +00001113 PJSUA_PUBLISH_EXPIRATION);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001114 if (status != PJ_SUCCESS) {
1115 acc->publish_sess = NULL;
1116 return status;
1117 }
1118
Benny Prijono703b7d72007-03-20 09:13:24 +00001119 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001120 if (acc->cred_cnt) {
1121 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1122 acc->cred);
1123 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001124
1125 /* Set route-set */
1126 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1127
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001128 /* Send initial PUBLISH request */
1129 if (acc->online_status != 0) {
1130 status = send_publish(acc_id, PJ_TRUE);
1131 if (status != PJ_SUCCESS)
1132 return status;
1133 }
1134
1135 } else {
1136 acc->publish_sess = NULL;
1137 }
1138
1139 return PJ_SUCCESS;
1140}
1141
1142
1143/* Init presence for account */
1144pj_status_t pjsua_pres_init_acc(int acc_id)
1145{
1146 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1147
1148 /* Init presence subscription */
1149 pj_list_init(&acc->pres_srv_list);
1150
Benny Prijono8b6834f2007-02-24 13:29:22 +00001151 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001152}
1153
1154
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155/* Terminate server subscription for the account */
1156void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001157{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001158 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1159 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001160 pjsua_srv_pres *uapres;
1161
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001162 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001163
Benny Prijono922933b2007-01-21 16:23:56 +00001164 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001165 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166
1167 pjsip_pres_status pres_status;
1168 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001169 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001170 pjsip_tx_data *tdata;
1171
Benny Prijono5516f912008-05-05 12:06:08 +00001172 next = uapres->next;
1173
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001174 pjsip_pres_get_status(uapres->sub, &pres_status);
1175
1176 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1177 pjsip_pres_set_status(uapres->sub, &pres_status);
1178
1179 if (pjsip_pres_notify(uapres->sub,
1180 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1181 &reason, &tdata)==PJ_SUCCESS)
1182 {
1183 pjsip_pres_send_request(uapres->sub, tdata);
1184 }
1185
Benny Prijono5516f912008-05-05 12:06:08 +00001186 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001187 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001188
Benny Prijono922933b2007-01-21 16:23:56 +00001189 /* Clear server presence subscription list because account might be reused
1190 * later. */
1191 pj_list_init(&acc->pres_srv_list);
1192
1193 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001194 if (acc->publish_sess) {
1195 acc->online_status = PJ_FALSE;
1196 send_publish(acc_id, PJ_FALSE);
1197 if (acc->publish_sess) {
1198 pjsip_publishc_destroy(acc->publish_sess);
1199 acc->publish_sess = NULL;
1200 }
1201 acc_cfg->publish_enabled = PJ_FALSE;
1202 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001203}
1204
1205
Benny Prijono4461c7d2007-08-25 13:36:15 +00001206/* Update server subscription (e.g. when our online status has changed) */
1207void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001209 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1210 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001211 pjsua_srv_pres *uapres;
1212
1213 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1214
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001215 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001216
1217 pjsip_pres_status pres_status;
1218 pjsip_tx_data *tdata;
1219
1220 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001221
1222 /* Only send NOTIFY once subscription is active. Some subscriptions
1223 * may still be in NULL (when app is adding a new buddy while in the
1224 * on_incoming_subscribe() callback) or PENDING (when user approval is
1225 * being requested) state and we don't send NOTIFY to these subs until
1226 * the user accepted the request.
1227 */
1228 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1229 (force || pres_status.info[0].basic_open != acc->online_status))
1230 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001231
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001232 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001233 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1234 sizeof(pjrpid_element));
1235
Benny Prijono834aee32006-02-19 01:38:06 +00001236 pjsip_pres_set_status(uapres->sub, &pres_status);
1237
Benny Prijono21b9ad92006-08-15 13:11:22 +00001238 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1239 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001240 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001241 }
Benny Prijono834aee32006-02-19 01:38:06 +00001242 }
1243
1244 uapres = uapres->next;
1245 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001246
Benny Prijono8b6834f2007-02-24 13:29:22 +00001247 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1248 * session. If we don't have a PUBLISH session, then it could be
1249 * that we're waiting until registration has completed before we
1250 * send the first PUBLISH.
1251 */
1252 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001253 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001254 send_publish(acc_id, PJ_TRUE);
1255 }
1256 }
Benny Prijono834aee32006-02-19 01:38:06 +00001257}
1258
1259
1260
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001261/***************************************************************************
1262 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001263 */
1264
1265/* Callback called when *client* subscription state has changed. */
1266static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1267{
1268 pjsua_buddy *buddy;
1269
1270 PJ_UNUSED_ARG(event);
1271
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001272 PJSUA_LOCK();
1273
Benny Prijonoa1e69682007-05-11 15:14:34 +00001274 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001275 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001276 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001277 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001278 (int)pjsua_var.buddy[buddy->index].uri.slen,
1279 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001280 pjsip_evsub_get_state_name(sub)));
1281
1282 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001283 if (buddy->term_reason.ptr == NULL) {
1284 buddy->term_reason.ptr = (char*)
1285 pj_pool_alloc(buddy->pool,
1286 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1287 }
1288 pj_strncpy(&buddy->term_reason,
1289 pjsip_evsub_get_termination_reason(sub),
1290 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1291 } else {
1292 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001293 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001294
1295 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001296 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1297 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001298
1299 /* Clear subscription */
1300 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1301 buddy->sub = NULL;
1302 buddy->status.info_cnt = 0;
1303 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1304 }
Benny Prijono834aee32006-02-19 01:38:06 +00001305 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001306
1307 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001308}
1309
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001310
1311/* Callback when transaction state has changed. */
1312static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1313 pjsip_transaction *tsx,
1314 pjsip_event *event)
1315{
1316 pjsua_buddy *buddy;
1317 pjsip_contact_hdr *contact_hdr;
1318
1319 PJSUA_LOCK();
1320
Benny Prijonoa1e69682007-05-11 15:14:34 +00001321 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 if (!buddy) {
1323 PJSUA_UNLOCK();
1324 return;
1325 }
1326
1327 /* We only use this to update buddy's Contact, when it's not
1328 * set.
1329 */
1330 if (buddy->contact.slen != 0) {
1331 /* Contact already set */
1332 PJSUA_UNLOCK();
1333 return;
1334 }
1335
1336 /* Only care about 2xx response to outgoing SUBSCRIBE */
1337 if (tsx->status_code/100 != 2 ||
1338 tsx->role != PJSIP_UAC_ROLE ||
1339 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001340 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341 {
1342 PJSUA_UNLOCK();
1343 return;
1344 }
1345
1346 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001347 contact_hdr = (pjsip_contact_hdr*)
1348 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001349 PJSIP_H_CONTACT, NULL);
1350 if (!contact_hdr) {
1351 PJSUA_UNLOCK();
1352 return;
1353 }
1354
Benny Prijonoa1e69682007-05-11 15:14:34 +00001355 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001356 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001357 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1358 contact_hdr->uri,
1359 buddy->contact.ptr,
1360 PJSIP_MAX_URL_SIZE);
1361 if (buddy->contact.slen < 0)
1362 buddy->contact.slen = 0;
1363
1364 PJSUA_UNLOCK();
1365}
1366
1367
Benny Prijono834aee32006-02-19 01:38:06 +00001368/* Callback called when we receive NOTIFY */
1369static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1370 pjsip_rx_data *rdata,
1371 int *p_st_code,
1372 pj_str_t **p_st_text,
1373 pjsip_hdr *res_hdr,
1374 pjsip_msg_body **p_body)
1375{
1376 pjsua_buddy *buddy;
1377
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001378 PJSUA_LOCK();
1379
Benny Prijonoa1e69682007-05-11 15:14:34 +00001380 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001381 if (buddy) {
1382 /* Update our info. */
1383 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001384 }
1385
1386 /* The default is to send 200 response to NOTIFY.
1387 * Just leave it there..
1388 */
1389 PJ_UNUSED_ARG(rdata);
1390 PJ_UNUSED_ARG(p_st_code);
1391 PJ_UNUSED_ARG(p_st_text);
1392 PJ_UNUSED_ARG(res_hdr);
1393 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001394
1395 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001396}
1397
1398
1399/* Event subscription callback. */
1400static pjsip_evsub_user pres_callback =
1401{
1402 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001403 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001404
1405 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1406 * we want to authenticate
1407 */
1408
1409 &pjsua_evsub_on_rx_notify,
1410
1411 NULL, /* on_client_refresh: Use default behaviour, which is to
1412 * refresh client subscription. */
1413
1414 NULL, /* on_server_timeout: Use default behaviour, which is to send
1415 * NOTIFY to terminate.
1416 */
1417};
1418
1419
1420/* It does what it says.. */
1421static void subscribe_buddy_presence(unsigned index)
1422{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001423 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001424 pjsua_buddy *buddy;
1425 int acc_id;
1426 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001427 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001428 pjsip_tx_data *tdata;
1429 pj_status_t status;
1430
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 buddy = &pjsua_var.buddy[index];
1432 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001433
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001434 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001435
Benny Prijonob4a17c92006-07-10 14:40:21 +00001436 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1437 acc_id, index));
1438
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001439 /* Generate suitable Contact header unless one is already set in
1440 * the account
1441 */
1442 if (acc->contact.slen) {
1443 contact = acc->contact;
1444 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001445 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1446
1447 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001448 acc_id, &buddy->uri);
1449 if (status != PJ_SUCCESS) {
1450 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1451 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001452 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001453 return;
1454 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001455 }
1456
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001457 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001458 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001459 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001460 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001461 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001462 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001463 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001464 pjsua_perror(THIS_FILE, "Unable to create dialog",
1465 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001466 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001467 return;
1468 }
1469
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001470 /* Increment the dialog's lock otherwise when presence session creation
1471 * fails the dialog will be destroyed prematurely.
1472 */
1473 pjsip_dlg_inc_lock(buddy->dlg);
1474
Benny Prijonof9c40c32007-06-28 07:20:26 +00001475 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001476 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1477 if (status != PJ_SUCCESS) {
1478 pjsua_var.buddy[index].sub = NULL;
1479 pjsua_perror(THIS_FILE, "Unable to create presence client",
1480 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001481 /* This should destroy the dialog since there's no session
1482 * referencing it
1483 */
1484 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001485 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001486 return;
1487 }
1488
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001489 /* If account is locked to specific transport, then lock dialog
1490 * to this transport too.
1491 */
1492 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1493 pjsip_tpselector tp_sel;
1494
1495 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001496 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001497 }
1498
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001499 /* Set route-set */
1500 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001501 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502 }
1503
1504 /* Set credentials */
1505 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001506 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001507 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001508 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001509
Benny Prijono48ab2b72007-11-08 09:24:30 +00001510 /* Set authentication preference */
1511 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1512
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001514
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001515 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001516 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001517 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001518 if (buddy->sub) {
1519 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1520 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001521 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001522 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1523 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001524 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001525 return;
1526 }
1527
Benny Prijono21b9ad92006-08-15 13:11:22 +00001528 pjsua_process_msg_data(tdata, NULL);
1529
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001530 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001531 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001532 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001533 if (buddy->sub) {
1534 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1535 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001536 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001537 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1538 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001539 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001540 return;
1541 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001542
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001543 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001544 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001545}
1546
1547
1548/* It does what it says... */
1549static void unsubscribe_buddy_presence(unsigned index)
1550{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001551 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001552 pjsip_tx_data *tdata;
1553 pj_status_t status;
1554
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555 buddy = &pjsua_var.buddy[index];
1556
1557 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001558 return;
1559
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001560 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1561 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001562 return;
1563 }
1564
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001566 if (status == PJ_SUCCESS) {
1567 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001568 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001569 }
Benny Prijono834aee32006-02-19 01:38:06 +00001570
Benny Prijono48da92e2007-05-16 08:56:30 +00001571 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1573 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001574 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1575 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001576 }
1577}
1578
1579
Benny Prijonof9c40c32007-06-28 07:20:26 +00001580/* Lock all buddies */
1581#define LOCK_BUDDIES unsigned cnt_ = 0; \
1582 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1583 unsigned i_; \
1584 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1585 if (pjsua_var.buddy[i_].sub) { \
1586 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1587 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1588 } \
1589 } \
1590 PJSUA_LOCK();
1591
1592/* Unlock all buddies */
1593#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1594 for (i_=0; i_<cnt_; ++i_) { \
1595 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1596 }
1597
1598
1599
Benny Prijono834aee32006-02-19 01:38:06 +00001600/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001601static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001602{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001603 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001604
Benny Prijonof9c40c32007-06-28 07:20:26 +00001605 LOCK_BUDDIES;
1606
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001607 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001608
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001609 if (!pjsua_var.buddy[i].uri.slen)
1610 continue;
1611
1612 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001613 subscribe_buddy_presence(i);
1614
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001615 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001616 unsubscribe_buddy_presence(i);
1617
1618 }
1619 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001620
1621 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001622}
1623
Benny Prijono7a5f5102007-05-29 00:33:09 +00001624/* Timer callback to re-create client subscription */
1625static void pres_timer_cb(pj_timer_heap_t *th,
1626 pj_timer_entry *entry)
1627{
Benny Prijono53984d12009-04-28 22:19:49 +00001628 unsigned i;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001629 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1630
Benny Prijono53984d12009-04-28 22:19:49 +00001631 /* Retry failed PUBLISH requests */
1632 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1633 pjsua_acc *acc = &pjsua_var.acc[i];
1634 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
1635 pjsua_pres_init_publish_acc(acc->index);
1636 }
1637
Benny Prijono7a5f5102007-05-29 00:33:09 +00001638 entry->id = PJ_FALSE;
1639 refresh_client_subscriptions();
1640
1641 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1642 entry->id = PJ_TRUE;
1643
Benny Prijonof9c40c32007-06-28 07:20:26 +00001644 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001645}
1646
Benny Prijono834aee32006-02-19 01:38:06 +00001647
1648/*
1649 * Init presence
1650 */
1651pj_status_t pjsua_pres_init()
1652{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001654 pj_status_t status;
1655
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001657 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001658 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1659 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001660 }
1661
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1663 reset_buddy(i);
1664 }
1665
Benny Prijono834aee32006-02-19 01:38:06 +00001666 return status;
1667}
1668
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001669
Benny Prijono834aee32006-02-19 01:38:06 +00001670/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001671 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001672 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001673pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001674{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001675 /* Start presence timer to re-subscribe to buddy's presence when
1676 * subscription has failed.
1677 */
1678 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1679 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1680
1681 pjsua_var.pres_timer.cb = &pres_timer_cb;
1682 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1683 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001684 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001685 }
1686
Benny Prijono9fc735d2006-05-28 14:58:12 +00001687 return PJ_SUCCESS;
1688}
1689
1690
1691/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001692 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001693 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001694void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001695{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001696 unsigned i;
1697
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001698 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001699
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1701 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001702 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001703 }
Benny Prijono834aee32006-02-19 01:38:06 +00001704}
1705
1706
1707/*
1708 * Shutdown presence.
1709 */
1710void pjsua_pres_shutdown(void)
1711{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001712 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001713
Benny Prijono7a5f5102007-05-29 00:33:09 +00001714 if (pjsua_var.pres_timer.id != 0) {
1715 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1716 pjsua_var.pres_timer.id = PJ_FALSE;
1717 }
1718
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001719 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1720 if (!pjsua_var.acc[i].valid)
1721 continue;
1722 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001723 }
1724
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001725 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1726 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001727 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001728
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001729 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001730}