blob: d619dd47ff9c8fdd893f8fbd9c7684d62376f9ad [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 Prijonoeebe9af2006-06-13 22:57:13 +0000169 /* status and status text */
170 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
171 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
172 info->status_text = pj_str("?");
173 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
174 info->status = PJSUA_BUDDY_STATUS_ONLINE;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000175
176 /* copy RPID information */
177 info->rpid = buddy->status.info[0].rpid;
178
179 if (info->rpid.note.slen)
180 info->status_text = info->rpid.note;
181 else
182 info->status_text = pj_str("Online");
183
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184 } else {
185 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
186 info->status_text = pj_str("Offline");
187 }
188
189 /* monitor pres */
190 info->monitor_pres = buddy->monitor;
191
Benny Prijono63fba012008-07-17 14:19:10 +0000192 /* subscription state and termination reason */
193 if (buddy->sub) {
194 info->sub_state = pjsip_evsub_get_state(buddy->sub);
195 if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
196 total < sizeof(info->buf_))
197 {
198 info->sub_term_reason.ptr = info->buf_ + total;
199 pj_strncpy(&info->sub_term_reason,
200 pjsip_evsub_get_termination_reason(buddy->sub),
201 sizeof(info->buf_) - total);
202 total += info->sub_term_reason.slen;
203 } else {
204 info->sub_term_reason = pj_str("");
205 }
206 } else if (total < sizeof(info->buf_)) {
207 info->sub_term_reason.ptr = info->buf_ + total;
208 pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
209 sizeof(info->buf_) - total);
210 total += info->sub_term_reason.slen;
211 } else {
212 info->sub_term_reason = pj_str("");
213 }
214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 PJSUA_UNLOCK();
216 return PJ_SUCCESS;
217}
218
Benny Prijono705e7842008-07-21 18:12:51 +0000219/*
220 * Set the user data associated with the buddy object.
221 */
222PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
223 void *user_data)
224{
225 PJ_ASSERT_RETURN(buddy_id>=0 &&
226 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
227 PJ_EINVAL);
228
229 PJSUA_LOCK();
230
231 pjsua_var.buddy[buddy_id].user_data = user_data;
232
233 PJSUA_UNLOCK();
234
235 return PJ_SUCCESS;
236}
237
238
239/*
240 * Get the user data associated with the budy object.
241 */
242PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
243{
244 void *user_data;
245
246 PJ_ASSERT_RETURN(buddy_id>=0 &&
247 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
248 NULL);
249
250 PJSUA_LOCK();
251
252 user_data = pjsua_var.buddy[buddy_id].user_data;
253
254 PJSUA_UNLOCK();
255
256 return user_data;
257}
258
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000259
260/*
261 * Reset buddy descriptor.
262 */
263static void reset_buddy(pjsua_buddy_id id)
264{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000265 pj_pool_t *pool = pjsua_var.buddy[id].pool;
Benny Prijonoac623b32006-07-03 15:19:31 +0000266 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000267 pjsua_var.buddy[id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000268 pjsua_var.buddy[id].index = id;
269}
270
271
272/*
273 * Add new buddy.
274 */
275PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
276 pjsua_buddy_id *p_buddy_id)
277{
278 pjsip_name_addr *url;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000279 pjsua_buddy *buddy;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000280 pjsip_sip_uri *sip_uri;
281 int index;
282 pj_str_t tmp;
283
284 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
285 PJ_ARRAY_SIZE(pjsua_var.buddy),
286 PJ_ETOOMANY);
287
288 PJSUA_LOCK();
289
290 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000291 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000292 if (pjsua_var.buddy[index].uri.slen == 0)
293 break;
294 }
295
296 /* Expect to find an empty slot */
297 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
298 PJSUA_UNLOCK();
299 /* This shouldn't happen */
300 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
301 return PJ_ETOOMANY;
302 }
303
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000304 buddy = &pjsua_var.buddy[index];
305
306 /* Create pool for this buddy */
307 if (buddy->pool) {
308 pj_pool_reset(buddy->pool);
309 } else {
310 char name[PJ_MAX_OBJ_NAME];
311 pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
312 buddy->pool = pjsua_pool_create(name, 512, 256);
313 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000314
Benny Prijono63fba012008-07-17 14:19:10 +0000315 /* Init buffers for presence subscription status */
316 buddy->term_reason.ptr = (char*)
317 pj_pool_alloc(buddy->pool,
318 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
319
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000320 /* Get name and display name for buddy */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000321 pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
322 url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000323 PJSIP_PARSE_URI_AS_NAMEADDR);
324
325 if (url == NULL) {
326 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000327 pj_pool_release(buddy->pool);
328 buddy->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000329 PJSUA_UNLOCK();
330 return PJSIP_EINVALIDURI;
331 }
332
Benny Prijonofc493592007-02-18 20:56:32 +0000333 /* Only support SIP schemes */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000334 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
335 pj_pool_release(buddy->pool);
336 buddy->pool = NULL;
337 PJSUA_UNLOCK();
Benny Prijonofc493592007-02-18 20:56:32 +0000338 return PJSIP_EINVALIDSCHEME;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000339 }
Benny Prijonofc493592007-02-18 20:56:32 +0000340
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341 /* Reset buddy, to make sure everything is cleared with default
342 * values
343 */
344 reset_buddy(index);
345
346 /* Save URI */
347 pjsua_var.buddy[index].uri = tmp;
348
Benny Prijono9c1528f2007-02-10 19:22:25 +0000349 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000350 pjsua_var.buddy[index].name = sip_uri->user;
351 pjsua_var.buddy[index].display = url->display;
352 pjsua_var.buddy[index].host = sip_uri->host;
353 pjsua_var.buddy[index].port = sip_uri->port;
354 pjsua_var.buddy[index].monitor = cfg->subscribe;
355 if (pjsua_var.buddy[index].port == 0)
356 pjsua_var.buddy[index].port = 5060;
357
Benny Prijono705e7842008-07-21 18:12:51 +0000358 /* Save user data */
359 pjsua_var.buddy[index].user_data = (void*)cfg->user_data;
360
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000361 if (p_buddy_id)
362 *p_buddy_id = index;
363
364 pjsua_var.buddy_cnt++;
365
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000366 PJSUA_UNLOCK();
367
Benny Prijonof9c40c32007-06-28 07:20:26 +0000368 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
369
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000370 return PJ_SUCCESS;
371}
372
373
374/*
375 * Delete buddy.
376 */
377PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
378{
379 PJ_ASSERT_RETURN(buddy_id>=0 &&
380 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
381 PJ_EINVAL);
382
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 return PJ_SUCCESS;
385 }
386
387 /* Unsubscribe presence */
388 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
389
Benny Prijonof9c40c32007-06-28 07:20:26 +0000390 PJSUA_LOCK();
391
Benny Prijonoa5776cb2009-04-14 15:11:23 +0000392 /* Not interested with further events for this buddy */
393 if (pjsua_var.buddy[buddy_id].sub) {
394 pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub,
395 pjsua_var.mod.id, NULL);
396 }
397
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000398 /* Remove buddy */
399 pjsua_var.buddy[buddy_id].uri.slen = 0;
400 pjsua_var.buddy_cnt--;
401
402 /* Reset buddy struct */
403 reset_buddy(buddy_id);
404
405 PJSUA_UNLOCK();
406 return PJ_SUCCESS;
407}
408
409
410/*
411 * Enable/disable buddy's presence monitoring.
412 */
413PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
414 pj_bool_t subscribe)
415{
416 pjsua_buddy *buddy;
417
418 PJ_ASSERT_RETURN(buddy_id>=0 &&
419 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
420 PJ_EINVAL);
421
422 PJSUA_LOCK();
423
424 buddy = &pjsua_var.buddy[buddy_id];
425 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000426
427 PJSUA_UNLOCK();
428
Benny Prijonof9c40c32007-06-28 07:20:26 +0000429 pjsua_pres_refresh();
430
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 return PJ_SUCCESS;
432}
433
434
435/*
Benny Prijono10861432007-10-31 10:54:53 +0000436 * Update buddy's presence.
437 */
438PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
439{
440 pjsua_buddy *buddy;
441
442 PJ_ASSERT_RETURN(buddy_id>=0 &&
443 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
444 PJ_EINVAL);
445
446 PJSUA_LOCK();
447
448 buddy = &pjsua_var.buddy[buddy_id];
449
450 /* Return error if buddy's presence monitoring is not enabled */
451 if (!buddy->monitor) {
452 PJSUA_UNLOCK();
453 return PJ_EINVALIDOP;
454 }
455
456 /* Ignore if presence is already active for the buddy */
457 if (buddy->sub) {
458 PJSUA_UNLOCK();
459 return PJ_SUCCESS;
460 }
461
462 /* Initiate presence subscription */
463 subscribe_buddy_presence(buddy_id);
464
465 PJSUA_UNLOCK();
466
467 return PJ_SUCCESS;
468}
469
470
471/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000472 * Dump presence subscriptions to log file.
473 */
474PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
475{
476 unsigned acc_id;
477 unsigned i;
478
479
480 PJSUA_LOCK();
481
482 /*
483 * When no detail is required, just dump number of server and client
484 * subscriptions.
485 */
486 if (verbose == PJ_FALSE) {
487
488 int count = 0;
489
490 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
491
492 if (!pjsua_var.acc[acc_id].valid)
493 continue;
494
495 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
496 struct pjsua_srv_pres *uapres;
497
498 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
499 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
500 ++count;
501 uapres = uapres->next;
502 }
503 }
504 }
505
506 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
507 count));
508
509 count = 0;
510
511 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
512 if (pjsua_var.buddy[i].uri.slen == 0)
513 continue;
514 if (pjsua_var.buddy[i].sub) {
515 ++count;
516 }
517 }
518
519 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
520 count));
521 PJSUA_UNLOCK();
522 return;
523 }
524
525
526 /*
527 * Dumping all server (UAS) subscriptions
528 */
529 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
530
531 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
532
533 if (!pjsua_var.acc[acc_id].valid)
534 continue;
535
536 PJ_LOG(3,(THIS_FILE, " %.*s",
537 (int)pjsua_var.acc[acc_id].cfg.id.slen,
538 pjsua_var.acc[acc_id].cfg.id.ptr));
539
540 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
541
542 PJ_LOG(3,(THIS_FILE, " - none - "));
543
544 } else {
545 struct pjsua_srv_pres *uapres;
546
547 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
548 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
549
550 PJ_LOG(3,(THIS_FILE, " %10s %s",
551 pjsip_evsub_get_state_name(uapres->sub),
552 uapres->remote));
553
554 uapres = uapres->next;
555 }
556 }
557 }
558
559 /*
560 * Dumping all client (UAC) subscriptions
561 */
562 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
563
564 if (pjsua_var.buddy_cnt == 0) {
565
566 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
567
568 } else {
569 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
570
571 if (pjsua_var.buddy[i].uri.slen == 0)
572 continue;
573
574 if (pjsua_var.buddy[i].sub) {
575 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
576 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
577 (int)pjsua_var.buddy[i].uri.slen,
578 pjsua_var.buddy[i].uri.ptr));
579 } else {
580 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
581 "(null)",
582 (int)pjsua_var.buddy[i].uri.slen,
583 pjsua_var.buddy[i].uri.ptr));
584 }
585 }
586 }
587
588 PJSUA_UNLOCK();
589}
590
591
592/***************************************************************************
593 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000594 */
595
596/* Proto */
597static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
598
599/* The module instance. */
600static pjsip_module mod_pjsua_pres =
601{
602 NULL, NULL, /* prev, next. */
603 { "mod-pjsua-pres", 14 }, /* Name. */
604 -1, /* Id */
605 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000606 NULL, /* load() */
607 NULL, /* start() */
608 NULL, /* stop() */
609 NULL, /* unload() */
610 &pres_on_rx_request, /* on_rx_request() */
611 NULL, /* on_rx_response() */
612 NULL, /* on_tx_request. */
613 NULL, /* on_tx_response() */
614 NULL, /* on_tsx_state() */
615
616};
617
618
619/* Callback called when *server* subscription state has changed. */
620static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
621{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000622 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000623
624 PJ_UNUSED_ARG(event);
625
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000626 PJSUA_LOCK();
627
Benny Prijonoa1e69682007-05-11 15:14:34 +0000628 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000629 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000630 pjsip_evsub_state state;
631
Benny Prijonoba736c42008-07-10 20:45:03 +0000632 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000633 uapres->remote, pjsip_evsub_get_state_name(sub)));
634
Benny Prijono63fba012008-07-17 14:19:10 +0000635 state = pjsip_evsub_get_state(sub);
636
637 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
638 pj_str_t from;
639
640 from = uapres->dlg->remote.info_str;
641 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
642 uapres, &from,
643 state, event);
644 }
645
646 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000647 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000648 pj_list_erase(uapres);
649 }
650 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000651
652 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000653}
654
655/* This is called when request is received.
656 * We need to check for incoming SUBSCRIBE request.
657 */
658static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
659{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000660 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000661 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000662 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000663 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
664 pjsua_srv_pres *uapres;
665 pjsip_evsub *sub;
666 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000667 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000668 pjsip_status_code st_code;
669 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000670 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000671 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000672 pj_status_t status;
673
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000674 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000675 return PJ_FALSE;
676
677 /* Incoming SUBSCRIBE: */
678
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000679 PJSUA_LOCK();
680
Benny Prijonoa91a0032006-02-26 21:23:45 +0000681 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000683 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000684
Benny Prijono6f979412006-06-15 12:25:46 +0000685 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
686 acc_id));
687
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000688 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000689 if (acc->contact.slen) {
690 contact = acc->contact;
691 } else {
692 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
693 acc_id, rdata);
694 if (status != PJ_SUCCESS) {
695 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
696 status);
697 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000698 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
699 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000700 return PJ_TRUE;
701 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000702 }
703
Benny Prijono834aee32006-02-19 01:38:06 +0000704 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000705 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000706 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000707 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000708 pjsua_perror(THIS_FILE,
709 "Unable to create UAS dialog for subscription",
710 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000711 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000712 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
713 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000714 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000715 }
716
Benny Prijono48ab2b72007-11-08 09:24:30 +0000717 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000718 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000719 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000720
Benny Prijono834aee32006-02-19 01:38:06 +0000721 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000722 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000723 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
724
725 /* Create server presence subscription: */
726 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
727 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000728 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
729 pjsip_tx_data *tdata;
730
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000731 pjsua_perror(THIS_FILE, "Unable to create server subscription",
732 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000733
734 if (code==599 || code > 699 || code < 300) {
735 code = 400;
736 }
737
738 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
739 if (status == PJ_SUCCESS) {
740 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
741 tdata);
742 }
743
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000744 PJSUA_UNLOCK();
745 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000746 }
747
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000748 /* If account is locked to specific transport, then lock dialog
749 * to this transport too.
750 */
751 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
752 pjsip_tpselector tp_sel;
753
754 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
755 pjsip_dlg_set_transport(dlg, &tp_sel);
756 }
757
Benny Prijono834aee32006-02-19 01:38:06 +0000758 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000759 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000760 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000761 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000762 uapres->acc_id = acc_id;
763 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000764 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
765 uapres->remote, PJSIP_MAX_URL_SIZE);
766 if (status < 1)
767 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
768 else
769 uapres->remote[status] = '\0';
770
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000771 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000772
773 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000774 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000775
776
Benny Prijono63fba012008-07-17 14:19:10 +0000777 /* Capture the value of Expires header. */
778 expires_hdr = (pjsip_expires_hdr*)
779 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
780 NULL);
781 if (expires_hdr)
782 uapres->expires = expires_hdr->ivalue;
783 else
784 uapres->expires = -1;
785
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000786 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000787 reason = pj_str("OK");
788 pjsua_msg_data_init(&msg_data);
789
790 /* Notify application callback, if any */
791 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
792 pjsua_buddy_id buddy_id;
793
794 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
795
796 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
797 &dlg->remote.info_str,
798 rdata, &st_code, &reason,
799 &msg_data);
800 }
801
802 /* Handle rejection case */
803 if (st_code >= 300) {
804 pjsip_tx_data *tdata;
805
806 /* Create response */
807 status = pjsip_dlg_create_response(dlg, rdata, st_code,
808 &reason, &tdata);
809 if (status != PJ_SUCCESS) {
810 pjsua_perror(THIS_FILE, "Error creating response", status);
811 pj_list_erase(uapres);
812 pjsip_pres_terminate(sub, PJ_FALSE);
813 PJSUA_UNLOCK();
814 return PJ_FALSE;
815 }
816
817 /* Add header list, if any */
818 pjsua_process_msg_data(tdata, &msg_data);
819
820 /* Send the response */
821 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
822 tdata);
823 if (status != PJ_SUCCESS) {
824 pjsua_perror(THIS_FILE, "Error sending response", status);
825 /* This is not fatal */
826 }
827
828 /* Terminate presence subscription */
829 pj_list_erase(uapres);
830 pjsip_pres_terminate(sub, PJ_FALSE);
831 PJSUA_UNLOCK();
832 return PJ_TRUE;
833 }
834
835 /* Create and send 2xx response to the SUBSCRIBE request: */
836 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000837 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000838 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
839 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000840 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000841 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000842 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000843 return PJ_FALSE;
844 }
845
Benny Prijono63fba012008-07-17 14:19:10 +0000846 /* If code is 200, send NOTIFY now */
847 if (st_code == 200) {
848 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
849 NULL, NULL, PJ_TRUE, &msg_data);
850 }
851
852 /* Done: */
853
854 PJSUA_UNLOCK();
855
856 return PJ_TRUE;
857}
858
859
860/*
861 * Send NOTIFY.
862 */
863PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
864 pjsua_srv_pres *srv_pres,
865 pjsip_evsub_state ev_state,
866 const pj_str_t *state_str,
867 const pj_str_t *reason,
868 pj_bool_t with_body,
869 const pjsua_msg_data *msg_data)
870{
871 pjsua_acc *acc;
872 pjsip_pres_status pres_status;
873 pjsua_buddy_id buddy_id;
874 pjsip_tx_data *tdata;
875 pj_status_t status;
876
877 /* Check parameters */
878 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
879
880 /* Check that account ID is valid */
881 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
882 PJ_EINVAL);
883 /* Check that account is valid */
884 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
885
886 PJSUA_LOCK();
887
888 acc = &pjsua_var.acc[acc_id];
889
890 /* Check that the server presence subscription is still valid */
891 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
892 /* Subscription has been terminated */
893 PJSUA_UNLOCK();
894 return PJ_EINVALIDOP;
895 }
Benny Prijono834aee32006-02-19 01:38:06 +0000896
897 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000898 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000899 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000900 pres_status.info[0].basic_open = acc->online_status;
901 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000902 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000903 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000904 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000905 /* add RPID information */
906 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
907 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000908
Benny Prijono63fba012008-07-17 14:19:10 +0000909 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000910
Benny Prijonoc61cc042007-06-27 13:01:59 +0000911 /* Check expires value. If it's zero, send our presense state but
912 * set subscription state to TERMINATED.
913 */
Benny Prijono63fba012008-07-17 14:19:10 +0000914 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000915 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000916
Benny Prijono63fba012008-07-17 14:19:10 +0000917 /* Create and send the NOTIFY to active subscription: */
918 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
919 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000920 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000921 /* Force removal of message body if msg_body==FALSE */
922 if (!with_body) {
923 tdata->msg->body = NULL;
924 }
925 pjsua_process_msg_data(tdata, msg_data);
926 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000927 }
Benny Prijono834aee32006-02-19 01:38:06 +0000928
929 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000930 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
931 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000932 pj_list_erase(srv_pres);
933 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000934 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000935 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000936 }
937
938
Benny Prijonoa17496a2007-10-31 10:20:31 +0000939 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000940 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000941 if (buddy_id != PJSUA_INVALID_ID) {
942 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
943 if (b->monitor && b->sub == NULL) {
944 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
945 "activating outgoing subscription", buddy_id));
946 subscribe_buddy_presence(buddy_id);
947 }
948 }
949
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000950 PJSUA_UNLOCK();
951
Benny Prijono63fba012008-07-17 14:19:10 +0000952 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000953}
954
955
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000956/*
957 * Client presence publication callback.
958 */
959static void publish_cb(struct pjsip_publishc_cbparam *param)
960{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000961 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000962
963 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
964 if (param->status != PJ_SUCCESS) {
965 char errmsg[PJ_ERR_MSG_SIZE];
966
967 pj_strerror(param->status, errmsg, sizeof(errmsg));
968 PJ_LOG(1,(THIS_FILE,
969 "Client publication (PUBLISH) failed, status=%d, msg=%s",
970 param->status, errmsg));
971 } else {
972 PJ_LOG(1,(THIS_FILE,
973 "Client publication (PUBLISH) failed (%d/%.*s)",
974 param->code, (int)param->reason.slen,
975 param->reason.ptr));
976 }
977
978 pjsip_publishc_destroy(param->pubc);
979 acc->publish_sess = NULL;
980 }
981}
982
983
984/*
985 * Send PUBLISH request.
986 */
987static pj_status_t send_publish(int acc_id, pj_bool_t active)
988{
989 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
990 pjsua_acc *acc = &pjsua_var.acc[acc_id];
991 pjsip_pres_status pres_status;
992 pjsip_tx_data *tdata;
993 pj_status_t status;
994
995
996 /* Create PUBLISH request */
997 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000998 char *bpos;
999 pj_str_t entity;
1000
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001001 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1002 if (status != PJ_SUCCESS) {
1003 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1004 goto on_error;
1005 }
1006
1007 /* Set our online status: */
1008 pj_bzero(&pres_status, sizeof(pres_status));
1009 pres_status.info_cnt = 1;
1010 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001011 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001012 /* .. including RPID information */
1013 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1014 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001015
Benny Prijono8c6e8842007-02-24 15:33:54 +00001016 /* Be careful not to send PIDF with presence entity ID containing
1017 * "<" character.
1018 */
1019 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1020 char *epos = pj_strchr(&acc_cfg->id, '>');
1021 if (epos - bpos < 2) {
1022 pj_assert(!"Unexpected invalid URI");
1023 status = PJSIP_EINVALIDURI;
1024 goto on_error;
1025 }
1026 entity.ptr = bpos+1;
1027 entity.slen = epos - bpos - 1;
1028 } else {
1029 entity = acc_cfg->id;
1030 }
1031
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001032 /* Create and add PIDF message body */
1033 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001034 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001035 if (status != PJ_SUCCESS) {
1036 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1037 status);
1038 pjsip_tx_data_dec_ref(tdata);
1039 goto on_error;
1040 }
1041 } else {
1042 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1043 if (status != PJ_SUCCESS) {
1044 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1045 goto on_error;
1046 }
1047 }
1048
1049 /* Add headers etc */
1050 pjsua_process_msg_data(tdata, NULL);
1051
1052 /* Send the PUBLISH request */
1053 status = pjsip_publishc_send(acc->publish_sess, tdata);
1054 if (status != PJ_SUCCESS) {
1055 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1056 goto on_error;
1057 }
1058
1059 acc->publish_state = acc->online_status;
1060 return PJ_SUCCESS;
1061
1062on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001063 if (acc->publish_sess) {
1064 pjsip_publishc_destroy(acc->publish_sess);
1065 acc->publish_sess = NULL;
1066 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001067 return status;
1068}
1069
1070
1071/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001072pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001073{
1074 const pj_str_t STR_PRESENCE = { "presence", 8 };
1075 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1076 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1077 pj_status_t status;
1078
1079 /* Create and init client publication session */
1080 if (acc_cfg->publish_enabled) {
1081
1082 /* Create client publication */
1083 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
1084 &acc->publish_sess);
1085 if (status != PJ_SUCCESS) {
1086 acc->publish_sess = NULL;
1087 return status;
1088 }
1089
1090 /* Initialize client publication */
1091 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1092 &acc_cfg->id, &acc_cfg->id,
1093 &acc_cfg->id,
Benny Prijono32767ec2007-11-07 03:45:03 +00001094 PJSUA_PRES_TIMER);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001095 if (status != PJ_SUCCESS) {
1096 acc->publish_sess = NULL;
1097 return status;
1098 }
1099
Benny Prijono703b7d72007-03-20 09:13:24 +00001100 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001101 if (acc->cred_cnt) {
1102 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1103 acc->cred);
1104 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001105
1106 /* Set route-set */
1107 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1108
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001109 /* Send initial PUBLISH request */
1110 if (acc->online_status != 0) {
1111 status = send_publish(acc_id, PJ_TRUE);
1112 if (status != PJ_SUCCESS)
1113 return status;
1114 }
1115
1116 } else {
1117 acc->publish_sess = NULL;
1118 }
1119
1120 return PJ_SUCCESS;
1121}
1122
1123
1124/* Init presence for account */
1125pj_status_t pjsua_pres_init_acc(int acc_id)
1126{
1127 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1128
1129 /* Init presence subscription */
1130 pj_list_init(&acc->pres_srv_list);
1131
Benny Prijono8b6834f2007-02-24 13:29:22 +00001132 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001133}
1134
1135
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001136/* Terminate server subscription for the account */
1137void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001138{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001139 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1140 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001141 pjsua_srv_pres *uapres;
1142
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001143 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001144
Benny Prijono922933b2007-01-21 16:23:56 +00001145 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001146 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001147
1148 pjsip_pres_status pres_status;
1149 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001150 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001151 pjsip_tx_data *tdata;
1152
Benny Prijono5516f912008-05-05 12:06:08 +00001153 next = uapres->next;
1154
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155 pjsip_pres_get_status(uapres->sub, &pres_status);
1156
1157 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1158 pjsip_pres_set_status(uapres->sub, &pres_status);
1159
1160 if (pjsip_pres_notify(uapres->sub,
1161 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1162 &reason, &tdata)==PJ_SUCCESS)
1163 {
1164 pjsip_pres_send_request(uapres->sub, tdata);
1165 }
1166
Benny Prijono5516f912008-05-05 12:06:08 +00001167 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001168 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001169
Benny Prijono922933b2007-01-21 16:23:56 +00001170 /* Clear server presence subscription list because account might be reused
1171 * later. */
1172 pj_list_init(&acc->pres_srv_list);
1173
1174 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001175 if (acc->publish_sess) {
1176 acc->online_status = PJ_FALSE;
1177 send_publish(acc_id, PJ_FALSE);
1178 if (acc->publish_sess) {
1179 pjsip_publishc_destroy(acc->publish_sess);
1180 acc->publish_sess = NULL;
1181 }
1182 acc_cfg->publish_enabled = PJ_FALSE;
1183 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001184}
1185
1186
Benny Prijono4461c7d2007-08-25 13:36:15 +00001187/* Update server subscription (e.g. when our online status has changed) */
1188void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001189{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001190 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1191 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001192 pjsua_srv_pres *uapres;
1193
1194 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1195
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001196 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001197
1198 pjsip_pres_status pres_status;
1199 pjsip_tx_data *tdata;
1200
1201 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001202
1203 /* Only send NOTIFY once subscription is active. Some subscriptions
1204 * may still be in NULL (when app is adding a new buddy while in the
1205 * on_incoming_subscribe() callback) or PENDING (when user approval is
1206 * being requested) state and we don't send NOTIFY to these subs until
1207 * the user accepted the request.
1208 */
1209 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1210 (force || pres_status.info[0].basic_open != acc->online_status))
1211 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001212
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001213 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001214 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1215 sizeof(pjrpid_element));
1216
Benny Prijono834aee32006-02-19 01:38:06 +00001217 pjsip_pres_set_status(uapres->sub, &pres_status);
1218
Benny Prijono21b9ad92006-08-15 13:11:22 +00001219 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1220 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001222 }
Benny Prijono834aee32006-02-19 01:38:06 +00001223 }
1224
1225 uapres = uapres->next;
1226 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001227
Benny Prijono8b6834f2007-02-24 13:29:22 +00001228 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1229 * session. If we don't have a PUBLISH session, then it could be
1230 * that we're waiting until registration has completed before we
1231 * send the first PUBLISH.
1232 */
1233 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001234 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001235 send_publish(acc_id, PJ_TRUE);
1236 }
1237 }
Benny Prijono834aee32006-02-19 01:38:06 +00001238}
1239
1240
1241
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242/***************************************************************************
1243 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001244 */
1245
1246/* Callback called when *client* subscription state has changed. */
1247static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1248{
1249 pjsua_buddy *buddy;
1250
1251 PJ_UNUSED_ARG(event);
1252
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253 PJSUA_LOCK();
1254
Benny Prijonoa1e69682007-05-11 15:14:34 +00001255 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001256 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001257 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001258 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001259 (int)pjsua_var.buddy[buddy->index].uri.slen,
1260 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001261 pjsip_evsub_get_state_name(sub)));
1262
1263 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001264 if (buddy->term_reason.ptr == NULL) {
1265 buddy->term_reason.ptr = (char*)
1266 pj_pool_alloc(buddy->pool,
1267 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1268 }
1269 pj_strncpy(&buddy->term_reason,
1270 pjsip_evsub_get_termination_reason(sub),
1271 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1272 } else {
1273 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001274 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001275
1276 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001277 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1278 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001279
1280 /* Clear subscription */
1281 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1282 buddy->sub = NULL;
1283 buddy->status.info_cnt = 0;
1284 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1285 }
Benny Prijono834aee32006-02-19 01:38:06 +00001286 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001287
1288 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001289}
1290
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001291
1292/* Callback when transaction state has changed. */
1293static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1294 pjsip_transaction *tsx,
1295 pjsip_event *event)
1296{
1297 pjsua_buddy *buddy;
1298 pjsip_contact_hdr *contact_hdr;
1299
1300 PJSUA_LOCK();
1301
Benny Prijonoa1e69682007-05-11 15:14:34 +00001302 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001303 if (!buddy) {
1304 PJSUA_UNLOCK();
1305 return;
1306 }
1307
1308 /* We only use this to update buddy's Contact, when it's not
1309 * set.
1310 */
1311 if (buddy->contact.slen != 0) {
1312 /* Contact already set */
1313 PJSUA_UNLOCK();
1314 return;
1315 }
1316
1317 /* Only care about 2xx response to outgoing SUBSCRIBE */
1318 if (tsx->status_code/100 != 2 ||
1319 tsx->role != PJSIP_UAC_ROLE ||
1320 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001321 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 {
1323 PJSUA_UNLOCK();
1324 return;
1325 }
1326
1327 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001328 contact_hdr = (pjsip_contact_hdr*)
1329 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001330 PJSIP_H_CONTACT, NULL);
1331 if (!contact_hdr) {
1332 PJSUA_UNLOCK();
1333 return;
1334 }
1335
Benny Prijonoa1e69682007-05-11 15:14:34 +00001336 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001337 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001338 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1339 contact_hdr->uri,
1340 buddy->contact.ptr,
1341 PJSIP_MAX_URL_SIZE);
1342 if (buddy->contact.slen < 0)
1343 buddy->contact.slen = 0;
1344
1345 PJSUA_UNLOCK();
1346}
1347
1348
Benny Prijono834aee32006-02-19 01:38:06 +00001349/* Callback called when we receive NOTIFY */
1350static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1351 pjsip_rx_data *rdata,
1352 int *p_st_code,
1353 pj_str_t **p_st_text,
1354 pjsip_hdr *res_hdr,
1355 pjsip_msg_body **p_body)
1356{
1357 pjsua_buddy *buddy;
1358
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 PJSUA_LOCK();
1360
Benny Prijonoa1e69682007-05-11 15:14:34 +00001361 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001362 if (buddy) {
1363 /* Update our info. */
1364 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001365 }
1366
1367 /* The default is to send 200 response to NOTIFY.
1368 * Just leave it there..
1369 */
1370 PJ_UNUSED_ARG(rdata);
1371 PJ_UNUSED_ARG(p_st_code);
1372 PJ_UNUSED_ARG(p_st_text);
1373 PJ_UNUSED_ARG(res_hdr);
1374 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375
1376 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001377}
1378
1379
1380/* Event subscription callback. */
1381static pjsip_evsub_user pres_callback =
1382{
1383 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001385
1386 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1387 * we want to authenticate
1388 */
1389
1390 &pjsua_evsub_on_rx_notify,
1391
1392 NULL, /* on_client_refresh: Use default behaviour, which is to
1393 * refresh client subscription. */
1394
1395 NULL, /* on_server_timeout: Use default behaviour, which is to send
1396 * NOTIFY to terminate.
1397 */
1398};
1399
1400
1401/* It does what it says.. */
1402static void subscribe_buddy_presence(unsigned index)
1403{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001404 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405 pjsua_buddy *buddy;
1406 int acc_id;
1407 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001408 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001409 pjsip_tx_data *tdata;
1410 pj_status_t status;
1411
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412 buddy = &pjsua_var.buddy[index];
1413 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001414
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001415 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001416
Benny Prijonob4a17c92006-07-10 14:40:21 +00001417 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1418 acc_id, index));
1419
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001420 /* Generate suitable Contact header unless one is already set in
1421 * the account
1422 */
1423 if (acc->contact.slen) {
1424 contact = acc->contact;
1425 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001426 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1427
1428 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001429 acc_id, &buddy->uri);
1430 if (status != PJ_SUCCESS) {
1431 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1432 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001433 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001434 return;
1435 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001436 }
1437
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001439 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001441 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001443 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001444 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001445 pjsua_perror(THIS_FILE, "Unable to create dialog",
1446 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001447 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001448 return;
1449 }
1450
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001451 /* Increment the dialog's lock otherwise when presence session creation
1452 * fails the dialog will be destroyed prematurely.
1453 */
1454 pjsip_dlg_inc_lock(buddy->dlg);
1455
Benny Prijonof9c40c32007-06-28 07:20:26 +00001456 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001457 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1458 if (status != PJ_SUCCESS) {
1459 pjsua_var.buddy[index].sub = NULL;
1460 pjsua_perror(THIS_FILE, "Unable to create presence client",
1461 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001462 /* This should destroy the dialog since there's no session
1463 * referencing it
1464 */
1465 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001466 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001467 return;
1468 }
1469
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001470 /* If account is locked to specific transport, then lock dialog
1471 * to this transport too.
1472 */
1473 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1474 pjsip_tpselector tp_sel;
1475
1476 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001477 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001478 }
1479
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001480 /* Set route-set */
1481 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001482 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001483 }
1484
1485 /* Set credentials */
1486 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001487 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001488 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001489 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001490
Benny Prijono48ab2b72007-11-08 09:24:30 +00001491 /* Set authentication preference */
1492 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1493
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001494 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001495
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001497 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001498 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001499 if (buddy->sub) {
1500 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1501 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001503 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1504 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001505 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001506 return;
1507 }
1508
Benny Prijono21b9ad92006-08-15 13:11:22 +00001509 pjsua_process_msg_data(tdata, NULL);
1510
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001511 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001512 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001513 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001514 if (buddy->sub) {
1515 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1516 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001517 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001518 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1519 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001520 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001521 return;
1522 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001523
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001524 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001525 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001526}
1527
1528
1529/* It does what it says... */
1530static void unsubscribe_buddy_presence(unsigned index)
1531{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001532 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001533 pjsip_tx_data *tdata;
1534 pj_status_t status;
1535
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001536 buddy = &pjsua_var.buddy[index];
1537
1538 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001539 return;
1540
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001541 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1542 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001543 return;
1544 }
1545
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001547 if (status == PJ_SUCCESS) {
1548 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001549 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001550 }
Benny Prijono834aee32006-02-19 01:38:06 +00001551
Benny Prijono48da92e2007-05-16 08:56:30 +00001552 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001553 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1554 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001555 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1556 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001557 }
1558}
1559
1560
Benny Prijonof9c40c32007-06-28 07:20:26 +00001561/* Lock all buddies */
1562#define LOCK_BUDDIES unsigned cnt_ = 0; \
1563 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1564 unsigned i_; \
1565 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1566 if (pjsua_var.buddy[i_].sub) { \
1567 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1568 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1569 } \
1570 } \
1571 PJSUA_LOCK();
1572
1573/* Unlock all buddies */
1574#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1575 for (i_=0; i_<cnt_; ++i_) { \
1576 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1577 }
1578
1579
1580
Benny Prijono834aee32006-02-19 01:38:06 +00001581/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001583{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001584 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001585
Benny Prijonof9c40c32007-06-28 07:20:26 +00001586 LOCK_BUDDIES;
1587
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001588 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001589
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001590 if (!pjsua_var.buddy[i].uri.slen)
1591 continue;
1592
1593 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001594 subscribe_buddy_presence(i);
1595
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001597 unsubscribe_buddy_presence(i);
1598
1599 }
1600 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001601
1602 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001603}
1604
Benny Prijono7a5f5102007-05-29 00:33:09 +00001605/* Timer callback to re-create client subscription */
1606static void pres_timer_cb(pj_timer_heap_t *th,
1607 pj_timer_entry *entry)
1608{
1609 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1610
Benny Prijono7a5f5102007-05-29 00:33:09 +00001611 entry->id = PJ_FALSE;
1612 refresh_client_subscriptions();
1613
1614 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1615 entry->id = PJ_TRUE;
1616
Benny Prijonof9c40c32007-06-28 07:20:26 +00001617 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001618}
1619
Benny Prijono834aee32006-02-19 01:38:06 +00001620
1621/*
1622 * Init presence
1623 */
1624pj_status_t pjsua_pres_init()
1625{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001626 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001627 pj_status_t status;
1628
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001629 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001630 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001631 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1632 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001633 }
1634
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1636 reset_buddy(i);
1637 }
1638
Benny Prijono834aee32006-02-19 01:38:06 +00001639 return status;
1640}
1641
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001642
Benny Prijono834aee32006-02-19 01:38:06 +00001643/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001645 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001646pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001647{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001648 /* Start presence timer to re-subscribe to buddy's presence when
1649 * subscription has failed.
1650 */
1651 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1652 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1653
1654 pjsua_var.pres_timer.cb = &pres_timer_cb;
1655 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1656 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001657 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001658 }
1659
Benny Prijono9fc735d2006-05-28 14:58:12 +00001660 return PJ_SUCCESS;
1661}
1662
1663
1664/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001665 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001666 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001668{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001669 unsigned i;
1670
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001671 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001672
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001673 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1674 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001675 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001676 }
Benny Prijono834aee32006-02-19 01:38:06 +00001677}
1678
1679
1680/*
1681 * Shutdown presence.
1682 */
1683void pjsua_pres_shutdown(void)
1684{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001685 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001686
Benny Prijono7a5f5102007-05-29 00:33:09 +00001687 if (pjsua_var.pres_timer.id != 0) {
1688 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1689 pjsua_var.pres_timer.id = PJ_FALSE;
1690 }
1691
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001692 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1693 if (!pjsua_var.acc[i].valid)
1694 continue;
1695 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001696 }
1697
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001698 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1699 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001700 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001701
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001702 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001703}