blob: d980e617c86b1e9528187e3b6cb45b4d7d0456f3 [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) {
Benny Prijono53984d12009-04-28 22:19:49 +0000964
965 pjsip_publishc_destroy(param->pubc);
966 acc->publish_sess = NULL;
967
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000968 if (param->status != PJ_SUCCESS) {
969 char errmsg[PJ_ERR_MSG_SIZE];
970
971 pj_strerror(param->status, errmsg, sizeof(errmsg));
972 PJ_LOG(1,(THIS_FILE,
973 "Client publication (PUBLISH) failed, status=%d, msg=%s",
974 param->status, errmsg));
Benny Prijono53984d12009-04-28 22:19:49 +0000975 } else if (param->code == 412) {
976 /* 412 (Conditional Request Failed)
977 * The PUBLISH refresh has failed, retry with new one.
978 */
979 pjsua_pres_init_publish_acc(acc->index);
980
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000981 } else {
982 PJ_LOG(1,(THIS_FILE,
983 "Client publication (PUBLISH) failed (%d/%.*s)",
984 param->code, (int)param->reason.slen,
985 param->reason.ptr));
986 }
987
Benny Prijono53984d12009-04-28 22:19:49 +0000988 } else {
989 if (param->expiration == -1) {
990 /* Could happen if server "forgot" to include Expires header
991 * in the response. We will not renew, so destroy the pubc.
992 */
993 pjsip_publishc_destroy(param->pubc);
994 acc->publish_sess = NULL;
995 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000996 }
997}
998
999
1000/*
1001 * Send PUBLISH request.
1002 */
1003static pj_status_t send_publish(int acc_id, pj_bool_t active)
1004{
1005 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1006 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1007 pjsip_pres_status pres_status;
1008 pjsip_tx_data *tdata;
1009 pj_status_t status;
1010
1011
1012 /* Create PUBLISH request */
1013 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +00001014 char *bpos;
1015 pj_str_t entity;
1016
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001017 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1018 if (status != PJ_SUCCESS) {
1019 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1020 goto on_error;
1021 }
1022
1023 /* Set our online status: */
1024 pj_bzero(&pres_status, sizeof(pres_status));
1025 pres_status.info_cnt = 1;
1026 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001027 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001028 /* .. including RPID information */
1029 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1030 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001031
Benny Prijono8c6e8842007-02-24 15:33:54 +00001032 /* Be careful not to send PIDF with presence entity ID containing
1033 * "<" character.
1034 */
1035 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1036 char *epos = pj_strchr(&acc_cfg->id, '>');
1037 if (epos - bpos < 2) {
1038 pj_assert(!"Unexpected invalid URI");
1039 status = PJSIP_EINVALIDURI;
1040 goto on_error;
1041 }
1042 entity.ptr = bpos+1;
1043 entity.slen = epos - bpos - 1;
1044 } else {
1045 entity = acc_cfg->id;
1046 }
1047
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001048 /* Create and add PIDF message body */
1049 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001050 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001051 if (status != PJ_SUCCESS) {
1052 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1053 status);
1054 pjsip_tx_data_dec_ref(tdata);
1055 goto on_error;
1056 }
1057 } else {
1058 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1059 if (status != PJ_SUCCESS) {
1060 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1061 goto on_error;
1062 }
1063 }
1064
1065 /* Add headers etc */
1066 pjsua_process_msg_data(tdata, NULL);
1067
1068 /* Send the PUBLISH request */
1069 status = pjsip_publishc_send(acc->publish_sess, tdata);
1070 if (status != PJ_SUCCESS) {
1071 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1072 goto on_error;
1073 }
1074
1075 acc->publish_state = acc->online_status;
1076 return PJ_SUCCESS;
1077
1078on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001079 if (acc->publish_sess) {
1080 pjsip_publishc_destroy(acc->publish_sess);
1081 acc->publish_sess = NULL;
1082 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001083 return status;
1084}
1085
1086
1087/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001088pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001089{
1090 const pj_str_t STR_PRESENCE = { "presence", 8 };
1091 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1092 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1093 pj_status_t status;
1094
1095 /* Create and init client publication session */
1096 if (acc_cfg->publish_enabled) {
1097
1098 /* Create client publication */
1099 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
1100 &acc->publish_sess);
1101 if (status != PJ_SUCCESS) {
1102 acc->publish_sess = NULL;
1103 return status;
1104 }
1105
1106 /* Initialize client publication */
1107 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1108 &acc_cfg->id, &acc_cfg->id,
1109 &acc_cfg->id,
Benny Prijono53984d12009-04-28 22:19:49 +00001110 PJSUA_PUBLISH_EXPIRATION);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001111 if (status != PJ_SUCCESS) {
1112 acc->publish_sess = NULL;
1113 return status;
1114 }
1115
Benny Prijono703b7d72007-03-20 09:13:24 +00001116 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001117 if (acc->cred_cnt) {
1118 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1119 acc->cred);
1120 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001121
1122 /* Set route-set */
1123 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1124
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001125 /* Send initial PUBLISH request */
1126 if (acc->online_status != 0) {
1127 status = send_publish(acc_id, PJ_TRUE);
1128 if (status != PJ_SUCCESS)
1129 return status;
1130 }
1131
1132 } else {
1133 acc->publish_sess = NULL;
1134 }
1135
1136 return PJ_SUCCESS;
1137}
1138
1139
1140/* Init presence for account */
1141pj_status_t pjsua_pres_init_acc(int acc_id)
1142{
1143 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1144
1145 /* Init presence subscription */
1146 pj_list_init(&acc->pres_srv_list);
1147
Benny Prijono8b6834f2007-02-24 13:29:22 +00001148 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001149}
1150
1151
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001152/* Terminate server subscription for the account */
1153void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001154{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001155 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1156 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001157 pjsua_srv_pres *uapres;
1158
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001160
Benny Prijono922933b2007-01-21 16:23:56 +00001161 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001162 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001163
1164 pjsip_pres_status pres_status;
1165 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001166 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001167 pjsip_tx_data *tdata;
1168
Benny Prijono5516f912008-05-05 12:06:08 +00001169 next = uapres->next;
1170
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001171 pjsip_pres_get_status(uapres->sub, &pres_status);
1172
1173 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1174 pjsip_pres_set_status(uapres->sub, &pres_status);
1175
1176 if (pjsip_pres_notify(uapres->sub,
1177 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1178 &reason, &tdata)==PJ_SUCCESS)
1179 {
1180 pjsip_pres_send_request(uapres->sub, tdata);
1181 }
1182
Benny Prijono5516f912008-05-05 12:06:08 +00001183 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001184 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001185
Benny Prijono922933b2007-01-21 16:23:56 +00001186 /* Clear server presence subscription list because account might be reused
1187 * later. */
1188 pj_list_init(&acc->pres_srv_list);
1189
1190 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001191 if (acc->publish_sess) {
1192 acc->online_status = PJ_FALSE;
1193 send_publish(acc_id, PJ_FALSE);
1194 if (acc->publish_sess) {
1195 pjsip_publishc_destroy(acc->publish_sess);
1196 acc->publish_sess = NULL;
1197 }
1198 acc_cfg->publish_enabled = PJ_FALSE;
1199 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001200}
1201
1202
Benny Prijono4461c7d2007-08-25 13:36:15 +00001203/* Update server subscription (e.g. when our online status has changed) */
1204void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001205{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001206 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1207 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208 pjsua_srv_pres *uapres;
1209
1210 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1211
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001212 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001213
1214 pjsip_pres_status pres_status;
1215 pjsip_tx_data *tdata;
1216
1217 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001218
1219 /* Only send NOTIFY once subscription is active. Some subscriptions
1220 * may still be in NULL (when app is adding a new buddy while in the
1221 * on_incoming_subscribe() callback) or PENDING (when user approval is
1222 * being requested) state and we don't send NOTIFY to these subs until
1223 * the user accepted the request.
1224 */
1225 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1226 (force || pres_status.info[0].basic_open != acc->online_status))
1227 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001228
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001229 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001230 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1231 sizeof(pjrpid_element));
1232
Benny Prijono834aee32006-02-19 01:38:06 +00001233 pjsip_pres_set_status(uapres->sub, &pres_status);
1234
Benny Prijono21b9ad92006-08-15 13:11:22 +00001235 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1236 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001238 }
Benny Prijono834aee32006-02-19 01:38:06 +00001239 }
1240
1241 uapres = uapres->next;
1242 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001243
Benny Prijono8b6834f2007-02-24 13:29:22 +00001244 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1245 * session. If we don't have a PUBLISH session, then it could be
1246 * that we're waiting until registration has completed before we
1247 * send the first PUBLISH.
1248 */
1249 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001250 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001251 send_publish(acc_id, PJ_TRUE);
1252 }
1253 }
Benny Prijono834aee32006-02-19 01:38:06 +00001254}
1255
1256
1257
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258/***************************************************************************
1259 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001260 */
1261
1262/* Callback called when *client* subscription state has changed. */
1263static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1264{
1265 pjsua_buddy *buddy;
1266
1267 PJ_UNUSED_ARG(event);
1268
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269 PJSUA_LOCK();
1270
Benny Prijonoa1e69682007-05-11 15:14:34 +00001271 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001272 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001273 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001274 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001275 (int)pjsua_var.buddy[buddy->index].uri.slen,
1276 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001277 pjsip_evsub_get_state_name(sub)));
1278
1279 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001280 if (buddy->term_reason.ptr == NULL) {
1281 buddy->term_reason.ptr = (char*)
1282 pj_pool_alloc(buddy->pool,
1283 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1284 }
1285 pj_strncpy(&buddy->term_reason,
1286 pjsip_evsub_get_termination_reason(sub),
1287 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1288 } else {
1289 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001290 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001291
1292 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001293 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1294 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001295
1296 /* Clear subscription */
1297 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1298 buddy->sub = NULL;
1299 buddy->status.info_cnt = 0;
1300 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1301 }
Benny Prijono834aee32006-02-19 01:38:06 +00001302 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001303
1304 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001305}
1306
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307
1308/* Callback when transaction state has changed. */
1309static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1310 pjsip_transaction *tsx,
1311 pjsip_event *event)
1312{
1313 pjsua_buddy *buddy;
1314 pjsip_contact_hdr *contact_hdr;
1315
1316 PJSUA_LOCK();
1317
Benny Prijonoa1e69682007-05-11 15:14:34 +00001318 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001319 if (!buddy) {
1320 PJSUA_UNLOCK();
1321 return;
1322 }
1323
1324 /* We only use this to update buddy's Contact, when it's not
1325 * set.
1326 */
1327 if (buddy->contact.slen != 0) {
1328 /* Contact already set */
1329 PJSUA_UNLOCK();
1330 return;
1331 }
1332
1333 /* Only care about 2xx response to outgoing SUBSCRIBE */
1334 if (tsx->status_code/100 != 2 ||
1335 tsx->role != PJSIP_UAC_ROLE ||
1336 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001337 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001338 {
1339 PJSUA_UNLOCK();
1340 return;
1341 }
1342
1343 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001344 contact_hdr = (pjsip_contact_hdr*)
1345 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001346 PJSIP_H_CONTACT, NULL);
1347 if (!contact_hdr) {
1348 PJSUA_UNLOCK();
1349 return;
1350 }
1351
Benny Prijonoa1e69682007-05-11 15:14:34 +00001352 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001353 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001354 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1355 contact_hdr->uri,
1356 buddy->contact.ptr,
1357 PJSIP_MAX_URL_SIZE);
1358 if (buddy->contact.slen < 0)
1359 buddy->contact.slen = 0;
1360
1361 PJSUA_UNLOCK();
1362}
1363
1364
Benny Prijono834aee32006-02-19 01:38:06 +00001365/* Callback called when we receive NOTIFY */
1366static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1367 pjsip_rx_data *rdata,
1368 int *p_st_code,
1369 pj_str_t **p_st_text,
1370 pjsip_hdr *res_hdr,
1371 pjsip_msg_body **p_body)
1372{
1373 pjsua_buddy *buddy;
1374
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 PJSUA_LOCK();
1376
Benny Prijonoa1e69682007-05-11 15:14:34 +00001377 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001378 if (buddy) {
1379 /* Update our info. */
1380 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001381 }
1382
1383 /* The default is to send 200 response to NOTIFY.
1384 * Just leave it there..
1385 */
1386 PJ_UNUSED_ARG(rdata);
1387 PJ_UNUSED_ARG(p_st_code);
1388 PJ_UNUSED_ARG(p_st_text);
1389 PJ_UNUSED_ARG(res_hdr);
1390 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001391
1392 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001393}
1394
1395
1396/* Event subscription callback. */
1397static pjsip_evsub_user pres_callback =
1398{
1399 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001401
1402 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1403 * we want to authenticate
1404 */
1405
1406 &pjsua_evsub_on_rx_notify,
1407
1408 NULL, /* on_client_refresh: Use default behaviour, which is to
1409 * refresh client subscription. */
1410
1411 NULL, /* on_server_timeout: Use default behaviour, which is to send
1412 * NOTIFY to terminate.
1413 */
1414};
1415
1416
1417/* It does what it says.. */
1418static void subscribe_buddy_presence(unsigned index)
1419{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001420 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001421 pjsua_buddy *buddy;
1422 int acc_id;
1423 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001424 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001425 pjsip_tx_data *tdata;
1426 pj_status_t status;
1427
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001428 buddy = &pjsua_var.buddy[index];
1429 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001430
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001432
Benny Prijonob4a17c92006-07-10 14:40:21 +00001433 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1434 acc_id, index));
1435
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001436 /* Generate suitable Contact header unless one is already set in
1437 * the account
1438 */
1439 if (acc->contact.slen) {
1440 contact = acc->contact;
1441 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001442 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1443
1444 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001445 acc_id, &buddy->uri);
1446 if (status != PJ_SUCCESS) {
1447 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1448 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001449 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001450 return;
1451 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001452 }
1453
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001454 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001455 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001456 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001457 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001458 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001459 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001460 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001461 pjsua_perror(THIS_FILE, "Unable to create dialog",
1462 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001463 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001464 return;
1465 }
1466
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001467 /* Increment the dialog's lock otherwise when presence session creation
1468 * fails the dialog will be destroyed prematurely.
1469 */
1470 pjsip_dlg_inc_lock(buddy->dlg);
1471
Benny Prijonof9c40c32007-06-28 07:20:26 +00001472 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001473 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1474 if (status != PJ_SUCCESS) {
1475 pjsua_var.buddy[index].sub = NULL;
1476 pjsua_perror(THIS_FILE, "Unable to create presence client",
1477 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001478 /* This should destroy the dialog since there's no session
1479 * referencing it
1480 */
1481 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001482 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001483 return;
1484 }
1485
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001486 /* If account is locked to specific transport, then lock dialog
1487 * to this transport too.
1488 */
1489 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1490 pjsip_tpselector tp_sel;
1491
1492 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001493 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001494 }
1495
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 /* Set route-set */
1497 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001498 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001499 }
1500
1501 /* Set credentials */
1502 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001503 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001504 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001505 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001506
Benny Prijono48ab2b72007-11-08 09:24:30 +00001507 /* Set authentication preference */
1508 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1509
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001510 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001511
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001513 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001514 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001515 if (buddy->sub) {
1516 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1517 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001518 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001519 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1520 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001521 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001522 return;
1523 }
1524
Benny Prijono21b9ad92006-08-15 13:11:22 +00001525 pjsua_process_msg_data(tdata, NULL);
1526
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001527 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001528 if (status != PJ_SUCCESS) {
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001529 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001530 if (buddy->sub) {
1531 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1532 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001533 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001534 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1535 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001536 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001537 return;
1538 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001539
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001540 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001541 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001542}
1543
1544
1545/* It does what it says... */
1546static void unsubscribe_buddy_presence(unsigned index)
1547{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001548 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001549 pjsip_tx_data *tdata;
1550 pj_status_t status;
1551
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001552 buddy = &pjsua_var.buddy[index];
1553
1554 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001555 return;
1556
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001557 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1558 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001559 return;
1560 }
1561
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001562 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001563 if (status == PJ_SUCCESS) {
1564 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001566 }
Benny Prijono834aee32006-02-19 01:38:06 +00001567
Benny Prijono48da92e2007-05-16 08:56:30 +00001568 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001569 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1570 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001571 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1572 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001573 }
1574}
1575
1576
Benny Prijonof9c40c32007-06-28 07:20:26 +00001577/* Lock all buddies */
1578#define LOCK_BUDDIES unsigned cnt_ = 0; \
1579 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1580 unsigned i_; \
1581 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1582 if (pjsua_var.buddy[i_].sub) { \
1583 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1584 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1585 } \
1586 } \
1587 PJSUA_LOCK();
1588
1589/* Unlock all buddies */
1590#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1591 for (i_=0; i_<cnt_; ++i_) { \
1592 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1593 }
1594
1595
1596
Benny Prijono834aee32006-02-19 01:38:06 +00001597/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001598static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001599{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001600 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001601
Benny Prijonof9c40c32007-06-28 07:20:26 +00001602 LOCK_BUDDIES;
1603
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001605
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001606 if (!pjsua_var.buddy[i].uri.slen)
1607 continue;
1608
1609 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001610 subscribe_buddy_presence(i);
1611
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001613 unsubscribe_buddy_presence(i);
1614
1615 }
1616 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001617
1618 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001619}
1620
Benny Prijono7a5f5102007-05-29 00:33:09 +00001621/* Timer callback to re-create client subscription */
1622static void pres_timer_cb(pj_timer_heap_t *th,
1623 pj_timer_entry *entry)
1624{
Benny Prijono53984d12009-04-28 22:19:49 +00001625 unsigned i;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001626 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1627
Benny Prijono53984d12009-04-28 22:19:49 +00001628 /* Retry failed PUBLISH requests */
1629 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1630 pjsua_acc *acc = &pjsua_var.acc[i];
1631 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
1632 pjsua_pres_init_publish_acc(acc->index);
1633 }
1634
Benny Prijono7a5f5102007-05-29 00:33:09 +00001635 entry->id = PJ_FALSE;
1636 refresh_client_subscriptions();
1637
1638 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1639 entry->id = PJ_TRUE;
1640
Benny Prijonof9c40c32007-06-28 07:20:26 +00001641 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001642}
1643
Benny Prijono834aee32006-02-19 01:38:06 +00001644
1645/*
1646 * Init presence
1647 */
1648pj_status_t pjsua_pres_init()
1649{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001650 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001651 pj_status_t status;
1652
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001654 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001655 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1656 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001657 }
1658
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1660 reset_buddy(i);
1661 }
1662
Benny Prijono834aee32006-02-19 01:38:06 +00001663 return status;
1664}
1665
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001666
Benny Prijono834aee32006-02-19 01:38:06 +00001667/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001668 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001669 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001670pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001671{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001672 /* Start presence timer to re-subscribe to buddy's presence when
1673 * subscription has failed.
1674 */
1675 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1676 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1677
1678 pjsua_var.pres_timer.cb = &pres_timer_cb;
1679 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1680 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001681 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001682 }
1683
Benny Prijono9fc735d2006-05-28 14:58:12 +00001684 return PJ_SUCCESS;
1685}
1686
1687
1688/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001689 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001690 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001691void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001692{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001693 unsigned i;
1694
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001695 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001696
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001697 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1698 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001699 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 }
Benny Prijono834aee32006-02-19 01:38:06 +00001701}
1702
1703
1704/*
1705 * Shutdown presence.
1706 */
1707void pjsua_pres_shutdown(void)
1708{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001709 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001710
Benny Prijono7a5f5102007-05-29 00:33:09 +00001711 if (pjsua_var.pres_timer.id != 0) {
1712 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1713 pjsua_var.pres_timer.id = PJ_FALSE;
1714 }
1715
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1717 if (!pjsua_var.acc[i].valid)
1718 continue;
1719 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001720 }
1721
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001722 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1723 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001724 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001725
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001726 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001727}