blob: c0461bd40e8416a9b8bed6c86b738db97d01df23 [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 Prijonoeebe9af2006-06-13 22:57:13 +0000392 /* Remove buddy */
393 pjsua_var.buddy[buddy_id].uri.slen = 0;
394 pjsua_var.buddy_cnt--;
395
396 /* Reset buddy struct */
397 reset_buddy(buddy_id);
398
399 PJSUA_UNLOCK();
400 return PJ_SUCCESS;
401}
402
403
404/*
405 * Enable/disable buddy's presence monitoring.
406 */
407PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
408 pj_bool_t subscribe)
409{
410 pjsua_buddy *buddy;
411
412 PJ_ASSERT_RETURN(buddy_id>=0 &&
413 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
414 PJ_EINVAL);
415
416 PJSUA_LOCK();
417
418 buddy = &pjsua_var.buddy[buddy_id];
419 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420
421 PJSUA_UNLOCK();
422
Benny Prijonof9c40c32007-06-28 07:20:26 +0000423 pjsua_pres_refresh();
424
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 return PJ_SUCCESS;
426}
427
428
429/*
Benny Prijono10861432007-10-31 10:54:53 +0000430 * Update buddy's presence.
431 */
432PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
433{
434 pjsua_buddy *buddy;
435
436 PJ_ASSERT_RETURN(buddy_id>=0 &&
437 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
438 PJ_EINVAL);
439
440 PJSUA_LOCK();
441
442 buddy = &pjsua_var.buddy[buddy_id];
443
444 /* Return error if buddy's presence monitoring is not enabled */
445 if (!buddy->monitor) {
446 PJSUA_UNLOCK();
447 return PJ_EINVALIDOP;
448 }
449
450 /* Ignore if presence is already active for the buddy */
451 if (buddy->sub) {
452 PJSUA_UNLOCK();
453 return PJ_SUCCESS;
454 }
455
456 /* Initiate presence subscription */
457 subscribe_buddy_presence(buddy_id);
458
459 PJSUA_UNLOCK();
460
461 return PJ_SUCCESS;
462}
463
464
465/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 * Dump presence subscriptions to log file.
467 */
468PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
469{
470 unsigned acc_id;
471 unsigned i;
472
473
474 PJSUA_LOCK();
475
476 /*
477 * When no detail is required, just dump number of server and client
478 * subscriptions.
479 */
480 if (verbose == PJ_FALSE) {
481
482 int count = 0;
483
484 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
485
486 if (!pjsua_var.acc[acc_id].valid)
487 continue;
488
489 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
490 struct pjsua_srv_pres *uapres;
491
492 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
493 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
494 ++count;
495 uapres = uapres->next;
496 }
497 }
498 }
499
500 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
501 count));
502
503 count = 0;
504
505 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
506 if (pjsua_var.buddy[i].uri.slen == 0)
507 continue;
508 if (pjsua_var.buddy[i].sub) {
509 ++count;
510 }
511 }
512
513 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
514 count));
515 PJSUA_UNLOCK();
516 return;
517 }
518
519
520 /*
521 * Dumping all server (UAS) subscriptions
522 */
523 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
524
525 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
526
527 if (!pjsua_var.acc[acc_id].valid)
528 continue;
529
530 PJ_LOG(3,(THIS_FILE, " %.*s",
531 (int)pjsua_var.acc[acc_id].cfg.id.slen,
532 pjsua_var.acc[acc_id].cfg.id.ptr));
533
534 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
535
536 PJ_LOG(3,(THIS_FILE, " - none - "));
537
538 } else {
539 struct pjsua_srv_pres *uapres;
540
541 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
542 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
543
544 PJ_LOG(3,(THIS_FILE, " %10s %s",
545 pjsip_evsub_get_state_name(uapres->sub),
546 uapres->remote));
547
548 uapres = uapres->next;
549 }
550 }
551 }
552
553 /*
554 * Dumping all client (UAC) subscriptions
555 */
556 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
557
558 if (pjsua_var.buddy_cnt == 0) {
559
560 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
561
562 } else {
563 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
564
565 if (pjsua_var.buddy[i].uri.slen == 0)
566 continue;
567
568 if (pjsua_var.buddy[i].sub) {
569 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
570 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
571 (int)pjsua_var.buddy[i].uri.slen,
572 pjsua_var.buddy[i].uri.ptr));
573 } else {
574 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
575 "(null)",
576 (int)pjsua_var.buddy[i].uri.slen,
577 pjsua_var.buddy[i].uri.ptr));
578 }
579 }
580 }
581
582 PJSUA_UNLOCK();
583}
584
585
586/***************************************************************************
587 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000588 */
589
590/* Proto */
591static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
592
593/* The module instance. */
594static pjsip_module mod_pjsua_pres =
595{
596 NULL, NULL, /* prev, next. */
597 { "mod-pjsua-pres", 14 }, /* Name. */
598 -1, /* Id */
599 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000600 NULL, /* load() */
601 NULL, /* start() */
602 NULL, /* stop() */
603 NULL, /* unload() */
604 &pres_on_rx_request, /* on_rx_request() */
605 NULL, /* on_rx_response() */
606 NULL, /* on_tx_request. */
607 NULL, /* on_tx_response() */
608 NULL, /* on_tsx_state() */
609
610};
611
612
613/* Callback called when *server* subscription state has changed. */
614static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
615{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000616 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000617
618 PJ_UNUSED_ARG(event);
619
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000620 PJSUA_LOCK();
621
Benny Prijonoa1e69682007-05-11 15:14:34 +0000622 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000623 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000624 pjsip_evsub_state state;
625
Benny Prijonoba736c42008-07-10 20:45:03 +0000626 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000627 uapres->remote, pjsip_evsub_get_state_name(sub)));
628
Benny Prijono63fba012008-07-17 14:19:10 +0000629 state = pjsip_evsub_get_state(sub);
630
631 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
632 pj_str_t from;
633
634 from = uapres->dlg->remote.info_str;
635 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
636 uapres, &from,
637 state, event);
638 }
639
640 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000641 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000642 pj_list_erase(uapres);
643 }
644 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000645
646 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000647}
648
649/* This is called when request is received.
650 * We need to check for incoming SUBSCRIBE request.
651 */
652static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
653{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000655 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000656 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000657 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
658 pjsua_srv_pres *uapres;
659 pjsip_evsub *sub;
660 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000661 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000662 pjsip_status_code st_code;
663 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000664 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000665 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000666 pj_status_t status;
667
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000668 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000669 return PJ_FALSE;
670
671 /* Incoming SUBSCRIBE: */
672
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000673 PJSUA_LOCK();
674
Benny Prijonoa91a0032006-02-26 21:23:45 +0000675 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000676 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000677 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000678
Benny Prijono6f979412006-06-15 12:25:46 +0000679 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
680 acc_id));
681
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000682 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000683 if (acc->contact.slen) {
684 contact = acc->contact;
685 } else {
686 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
687 acc_id, rdata);
688 if (status != PJ_SUCCESS) {
689 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
690 status);
691 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000692 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
693 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000694 return PJ_TRUE;
695 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000696 }
697
Benny Prijono834aee32006-02-19 01:38:06 +0000698 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000699 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000700 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000701 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000702 pjsua_perror(THIS_FILE,
703 "Unable to create UAS dialog for subscription",
704 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000705 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000706 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
707 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000708 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000709 }
710
Benny Prijono48ab2b72007-11-08 09:24:30 +0000711 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000712 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000713 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000714
Benny Prijono834aee32006-02-19 01:38:06 +0000715 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000716 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000717 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
718
719 /* Create server presence subscription: */
720 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
721 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000722 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
723 pjsip_tx_data *tdata;
724
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000725 pjsua_perror(THIS_FILE, "Unable to create server subscription",
726 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000727
728 if (code==599 || code > 699 || code < 300) {
729 code = 400;
730 }
731
732 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
733 if (status == PJ_SUCCESS) {
734 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
735 tdata);
736 }
737
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000738 PJSUA_UNLOCK();
739 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000740 }
741
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000742 /* If account is locked to specific transport, then lock dialog
743 * to this transport too.
744 */
745 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
746 pjsip_tpselector tp_sel;
747
748 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
749 pjsip_dlg_set_transport(dlg, &tp_sel);
750 }
751
Benny Prijono834aee32006-02-19 01:38:06 +0000752 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000753 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000754 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000755 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000756 uapres->acc_id = acc_id;
757 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000758 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
759 uapres->remote, PJSIP_MAX_URL_SIZE);
760 if (status < 1)
761 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
762 else
763 uapres->remote[status] = '\0';
764
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000765 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000766
767 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000768 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000769
770
Benny Prijono63fba012008-07-17 14:19:10 +0000771 /* Capture the value of Expires header. */
772 expires_hdr = (pjsip_expires_hdr*)
773 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
774 NULL);
775 if (expires_hdr)
776 uapres->expires = expires_hdr->ivalue;
777 else
778 uapres->expires = -1;
779
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000780 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000781 reason = pj_str("OK");
782 pjsua_msg_data_init(&msg_data);
783
784 /* Notify application callback, if any */
785 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
786 pjsua_buddy_id buddy_id;
787
788 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
789
790 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
791 &dlg->remote.info_str,
792 rdata, &st_code, &reason,
793 &msg_data);
794 }
795
796 /* Handle rejection case */
797 if (st_code >= 300) {
798 pjsip_tx_data *tdata;
799
800 /* Create response */
801 status = pjsip_dlg_create_response(dlg, rdata, st_code,
802 &reason, &tdata);
803 if (status != PJ_SUCCESS) {
804 pjsua_perror(THIS_FILE, "Error creating response", status);
805 pj_list_erase(uapres);
806 pjsip_pres_terminate(sub, PJ_FALSE);
807 PJSUA_UNLOCK();
808 return PJ_FALSE;
809 }
810
811 /* Add header list, if any */
812 pjsua_process_msg_data(tdata, &msg_data);
813
814 /* Send the response */
815 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
816 tdata);
817 if (status != PJ_SUCCESS) {
818 pjsua_perror(THIS_FILE, "Error sending response", status);
819 /* This is not fatal */
820 }
821
822 /* Terminate presence subscription */
823 pj_list_erase(uapres);
824 pjsip_pres_terminate(sub, PJ_FALSE);
825 PJSUA_UNLOCK();
826 return PJ_TRUE;
827 }
828
829 /* Create and send 2xx response to the SUBSCRIBE request: */
830 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000831 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000832 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
833 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000834 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000835 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000836 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000837 return PJ_FALSE;
838 }
839
Benny Prijono63fba012008-07-17 14:19:10 +0000840 /* If code is 200, send NOTIFY now */
841 if (st_code == 200) {
842 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
843 NULL, NULL, PJ_TRUE, &msg_data);
844 }
845
846 /* Done: */
847
848 PJSUA_UNLOCK();
849
850 return PJ_TRUE;
851}
852
853
854/*
855 * Send NOTIFY.
856 */
857PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
858 pjsua_srv_pres *srv_pres,
859 pjsip_evsub_state ev_state,
860 const pj_str_t *state_str,
861 const pj_str_t *reason,
862 pj_bool_t with_body,
863 const pjsua_msg_data *msg_data)
864{
865 pjsua_acc *acc;
866 pjsip_pres_status pres_status;
867 pjsua_buddy_id buddy_id;
868 pjsip_tx_data *tdata;
869 pj_status_t status;
870
871 /* Check parameters */
872 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
873
874 /* Check that account ID is valid */
875 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
876 PJ_EINVAL);
877 /* Check that account is valid */
878 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
879
880 PJSUA_LOCK();
881
882 acc = &pjsua_var.acc[acc_id];
883
884 /* Check that the server presence subscription is still valid */
885 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
886 /* Subscription has been terminated */
887 PJSUA_UNLOCK();
888 return PJ_EINVALIDOP;
889 }
Benny Prijono834aee32006-02-19 01:38:06 +0000890
891 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000892 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000893 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000894 pres_status.info[0].basic_open = acc->online_status;
895 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000896 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000897 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000898 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000899 /* add RPID information */
900 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
901 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000902
Benny Prijono63fba012008-07-17 14:19:10 +0000903 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000904
Benny Prijonoc61cc042007-06-27 13:01:59 +0000905 /* Check expires value. If it's zero, send our presense state but
906 * set subscription state to TERMINATED.
907 */
Benny Prijono63fba012008-07-17 14:19:10 +0000908 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000909 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000910
Benny Prijono63fba012008-07-17 14:19:10 +0000911 /* Create and send the NOTIFY to active subscription: */
912 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
913 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000914 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000915 /* Force removal of message body if msg_body==FALSE */
916 if (!with_body) {
917 tdata->msg->body = NULL;
918 }
919 pjsua_process_msg_data(tdata, msg_data);
920 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000921 }
Benny Prijono834aee32006-02-19 01:38:06 +0000922
923 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000924 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
925 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000926 pj_list_erase(srv_pres);
927 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000928 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000929 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000930 }
931
932
Benny Prijonoa17496a2007-10-31 10:20:31 +0000933 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000934 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000935 if (buddy_id != PJSUA_INVALID_ID) {
936 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
937 if (b->monitor && b->sub == NULL) {
938 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
939 "activating outgoing subscription", buddy_id));
940 subscribe_buddy_presence(buddy_id);
941 }
942 }
943
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000944 PJSUA_UNLOCK();
945
Benny Prijono63fba012008-07-17 14:19:10 +0000946 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000947}
948
949
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000950/*
951 * Client presence publication callback.
952 */
953static void publish_cb(struct pjsip_publishc_cbparam *param)
954{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000955 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000956
957 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
958 if (param->status != PJ_SUCCESS) {
959 char errmsg[PJ_ERR_MSG_SIZE];
960
961 pj_strerror(param->status, errmsg, sizeof(errmsg));
962 PJ_LOG(1,(THIS_FILE,
963 "Client publication (PUBLISH) failed, status=%d, msg=%s",
964 param->status, errmsg));
965 } else {
966 PJ_LOG(1,(THIS_FILE,
967 "Client publication (PUBLISH) failed (%d/%.*s)",
968 param->code, (int)param->reason.slen,
969 param->reason.ptr));
970 }
971
972 pjsip_publishc_destroy(param->pubc);
973 acc->publish_sess = NULL;
974 }
975}
976
977
978/*
979 * Send PUBLISH request.
980 */
981static pj_status_t send_publish(int acc_id, pj_bool_t active)
982{
983 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
984 pjsua_acc *acc = &pjsua_var.acc[acc_id];
985 pjsip_pres_status pres_status;
986 pjsip_tx_data *tdata;
987 pj_status_t status;
988
989
990 /* Create PUBLISH request */
991 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000992 char *bpos;
993 pj_str_t entity;
994
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000995 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
996 if (status != PJ_SUCCESS) {
997 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
998 goto on_error;
999 }
1000
1001 /* Set our online status: */
1002 pj_bzero(&pres_status, sizeof(pres_status));
1003 pres_status.info_cnt = 1;
1004 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001005 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001006 /* .. including RPID information */
1007 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1008 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001009
Benny Prijono8c6e8842007-02-24 15:33:54 +00001010 /* Be careful not to send PIDF with presence entity ID containing
1011 * "<" character.
1012 */
1013 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1014 char *epos = pj_strchr(&acc_cfg->id, '>');
1015 if (epos - bpos < 2) {
1016 pj_assert(!"Unexpected invalid URI");
1017 status = PJSIP_EINVALIDURI;
1018 goto on_error;
1019 }
1020 entity.ptr = bpos+1;
1021 entity.slen = epos - bpos - 1;
1022 } else {
1023 entity = acc_cfg->id;
1024 }
1025
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001026 /* Create and add PIDF message body */
1027 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001028 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001029 if (status != PJ_SUCCESS) {
1030 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1031 status);
1032 pjsip_tx_data_dec_ref(tdata);
1033 goto on_error;
1034 }
1035 } else {
1036 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1037 if (status != PJ_SUCCESS) {
1038 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1039 goto on_error;
1040 }
1041 }
1042
1043 /* Add headers etc */
1044 pjsua_process_msg_data(tdata, NULL);
1045
1046 /* Send the PUBLISH request */
1047 status = pjsip_publishc_send(acc->publish_sess, tdata);
1048 if (status != PJ_SUCCESS) {
1049 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1050 goto on_error;
1051 }
1052
1053 acc->publish_state = acc->online_status;
1054 return PJ_SUCCESS;
1055
1056on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001057 if (acc->publish_sess) {
1058 pjsip_publishc_destroy(acc->publish_sess);
1059 acc->publish_sess = NULL;
1060 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001061 return status;
1062}
1063
1064
1065/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001066pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001067{
1068 const pj_str_t STR_PRESENCE = { "presence", 8 };
1069 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1070 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1071 pj_status_t status;
1072
1073 /* Create and init client publication session */
1074 if (acc_cfg->publish_enabled) {
1075
1076 /* Create client publication */
1077 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
1078 &acc->publish_sess);
1079 if (status != PJ_SUCCESS) {
1080 acc->publish_sess = NULL;
1081 return status;
1082 }
1083
1084 /* Initialize client publication */
1085 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1086 &acc_cfg->id, &acc_cfg->id,
1087 &acc_cfg->id,
Benny Prijono32767ec2007-11-07 03:45:03 +00001088 PJSUA_PRES_TIMER);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001089 if (status != PJ_SUCCESS) {
1090 acc->publish_sess = NULL;
1091 return status;
1092 }
1093
Benny Prijono703b7d72007-03-20 09:13:24 +00001094 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001095 if (acc->cred_cnt) {
1096 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1097 acc->cred);
1098 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001099
1100 /* Set route-set */
1101 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1102
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001103 /* Send initial PUBLISH request */
1104 if (acc->online_status != 0) {
1105 status = send_publish(acc_id, PJ_TRUE);
1106 if (status != PJ_SUCCESS)
1107 return status;
1108 }
1109
1110 } else {
1111 acc->publish_sess = NULL;
1112 }
1113
1114 return PJ_SUCCESS;
1115}
1116
1117
1118/* Init presence for account */
1119pj_status_t pjsua_pres_init_acc(int acc_id)
1120{
1121 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1122
1123 /* Init presence subscription */
1124 pj_list_init(&acc->pres_srv_list);
1125
Benny Prijono8b6834f2007-02-24 13:29:22 +00001126 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001127}
1128
1129
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130/* Terminate server subscription for the account */
1131void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001132{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001133 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1134 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001135 pjsua_srv_pres *uapres;
1136
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001137 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001138
Benny Prijono922933b2007-01-21 16:23:56 +00001139 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001140 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001141
1142 pjsip_pres_status pres_status;
1143 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001144 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001145 pjsip_tx_data *tdata;
1146
Benny Prijono5516f912008-05-05 12:06:08 +00001147 next = uapres->next;
1148
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001149 pjsip_pres_get_status(uapres->sub, &pres_status);
1150
1151 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1152 pjsip_pres_set_status(uapres->sub, &pres_status);
1153
1154 if (pjsip_pres_notify(uapres->sub,
1155 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1156 &reason, &tdata)==PJ_SUCCESS)
1157 {
1158 pjsip_pres_send_request(uapres->sub, tdata);
1159 }
1160
Benny Prijono5516f912008-05-05 12:06:08 +00001161 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001162 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001163
Benny Prijono922933b2007-01-21 16:23:56 +00001164 /* Clear server presence subscription list because account might be reused
1165 * later. */
1166 pj_list_init(&acc->pres_srv_list);
1167
1168 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001169 if (acc->publish_sess) {
1170 acc->online_status = PJ_FALSE;
1171 send_publish(acc_id, PJ_FALSE);
1172 if (acc->publish_sess) {
1173 pjsip_publishc_destroy(acc->publish_sess);
1174 acc->publish_sess = NULL;
1175 }
1176 acc_cfg->publish_enabled = PJ_FALSE;
1177 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001178}
1179
1180
Benny Prijono4461c7d2007-08-25 13:36:15 +00001181/* Update server subscription (e.g. when our online status has changed) */
1182void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001183{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001184 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1185 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001186 pjsua_srv_pres *uapres;
1187
1188 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1189
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001190 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001191
1192 pjsip_pres_status pres_status;
1193 pjsip_tx_data *tdata;
1194
1195 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001196
1197 /* Only send NOTIFY once subscription is active. Some subscriptions
1198 * may still be in NULL (when app is adding a new buddy while in the
1199 * on_incoming_subscribe() callback) or PENDING (when user approval is
1200 * being requested) state and we don't send NOTIFY to these subs until
1201 * the user accepted the request.
1202 */
1203 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1204 (force || pres_status.info[0].basic_open != acc->online_status))
1205 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001206
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001207 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001208 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1209 sizeof(pjrpid_element));
1210
Benny Prijono834aee32006-02-19 01:38:06 +00001211 pjsip_pres_set_status(uapres->sub, &pres_status);
1212
Benny Prijono21b9ad92006-08-15 13:11:22 +00001213 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1214 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001216 }
Benny Prijono834aee32006-02-19 01:38:06 +00001217 }
1218
1219 uapres = uapres->next;
1220 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001221
Benny Prijono8b6834f2007-02-24 13:29:22 +00001222 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1223 * session. If we don't have a PUBLISH session, then it could be
1224 * that we're waiting until registration has completed before we
1225 * send the first PUBLISH.
1226 */
1227 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001228 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001229 send_publish(acc_id, PJ_TRUE);
1230 }
1231 }
Benny Prijono834aee32006-02-19 01:38:06 +00001232}
1233
1234
1235
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236/***************************************************************************
1237 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001238 */
1239
1240/* Callback called when *client* subscription state has changed. */
1241static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1242{
1243 pjsua_buddy *buddy;
1244
1245 PJ_UNUSED_ARG(event);
1246
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247 PJSUA_LOCK();
1248
Benny Prijonoa1e69682007-05-11 15:14:34 +00001249 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001250 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001251 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001252 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253 (int)pjsua_var.buddy[buddy->index].uri.slen,
1254 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001255 pjsip_evsub_get_state_name(sub)));
1256
1257 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001258 if (buddy->term_reason.ptr == NULL) {
1259 buddy->term_reason.ptr = (char*)
1260 pj_pool_alloc(buddy->pool,
1261 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1262 }
1263 pj_strncpy(&buddy->term_reason,
1264 pjsip_evsub_get_termination_reason(sub),
1265 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1266 } else {
1267 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001268 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001269
1270 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1272 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001273
1274 /* Clear subscription */
1275 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1276 buddy->sub = NULL;
1277 buddy->status.info_cnt = 0;
1278 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1279 }
Benny Prijono834aee32006-02-19 01:38:06 +00001280 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001281
1282 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001283}
1284
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001285
1286/* Callback when transaction state has changed. */
1287static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1288 pjsip_transaction *tsx,
1289 pjsip_event *event)
1290{
1291 pjsua_buddy *buddy;
1292 pjsip_contact_hdr *contact_hdr;
1293
1294 PJSUA_LOCK();
1295
Benny Prijonoa1e69682007-05-11 15:14:34 +00001296 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001297 if (!buddy) {
1298 PJSUA_UNLOCK();
1299 return;
1300 }
1301
1302 /* We only use this to update buddy's Contact, when it's not
1303 * set.
1304 */
1305 if (buddy->contact.slen != 0) {
1306 /* Contact already set */
1307 PJSUA_UNLOCK();
1308 return;
1309 }
1310
1311 /* Only care about 2xx response to outgoing SUBSCRIBE */
1312 if (tsx->status_code/100 != 2 ||
1313 tsx->role != PJSIP_UAC_ROLE ||
1314 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001315 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316 {
1317 PJSUA_UNLOCK();
1318 return;
1319 }
1320
1321 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001322 contact_hdr = (pjsip_contact_hdr*)
1323 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001324 PJSIP_H_CONTACT, NULL);
1325 if (!contact_hdr) {
1326 PJSUA_UNLOCK();
1327 return;
1328 }
1329
Benny Prijonoa1e69682007-05-11 15:14:34 +00001330 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001331 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001332 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1333 contact_hdr->uri,
1334 buddy->contact.ptr,
1335 PJSIP_MAX_URL_SIZE);
1336 if (buddy->contact.slen < 0)
1337 buddy->contact.slen = 0;
1338
1339 PJSUA_UNLOCK();
1340}
1341
1342
Benny Prijono834aee32006-02-19 01:38:06 +00001343/* Callback called when we receive NOTIFY */
1344static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1345 pjsip_rx_data *rdata,
1346 int *p_st_code,
1347 pj_str_t **p_st_text,
1348 pjsip_hdr *res_hdr,
1349 pjsip_msg_body **p_body)
1350{
1351 pjsua_buddy *buddy;
1352
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001353 PJSUA_LOCK();
1354
Benny Prijonoa1e69682007-05-11 15:14:34 +00001355 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001356 if (buddy) {
1357 /* Update our info. */
1358 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001359 }
1360
1361 /* The default is to send 200 response to NOTIFY.
1362 * Just leave it there..
1363 */
1364 PJ_UNUSED_ARG(rdata);
1365 PJ_UNUSED_ARG(p_st_code);
1366 PJ_UNUSED_ARG(p_st_text);
1367 PJ_UNUSED_ARG(res_hdr);
1368 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001369
1370 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001371}
1372
1373
1374/* Event subscription callback. */
1375static pjsip_evsub_user pres_callback =
1376{
1377 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001378 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001379
1380 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1381 * we want to authenticate
1382 */
1383
1384 &pjsua_evsub_on_rx_notify,
1385
1386 NULL, /* on_client_refresh: Use default behaviour, which is to
1387 * refresh client subscription. */
1388
1389 NULL, /* on_server_timeout: Use default behaviour, which is to send
1390 * NOTIFY to terminate.
1391 */
1392};
1393
1394
1395/* It does what it says.. */
1396static void subscribe_buddy_presence(unsigned index)
1397{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001398 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001399 pjsua_buddy *buddy;
1400 int acc_id;
1401 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001402 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001403 pjsip_tx_data *tdata;
1404 pj_status_t status;
1405
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 buddy = &pjsua_var.buddy[index];
1407 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001408
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001409 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001410
Benny Prijonob4a17c92006-07-10 14:40:21 +00001411 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1412 acc_id, index));
1413
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001414 /* Generate suitable Contact header unless one is already set in
1415 * the account
1416 */
1417 if (acc->contact.slen) {
1418 contact = acc->contact;
1419 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001420 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1421
1422 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001423 acc_id, &buddy->uri);
1424 if (status != PJ_SUCCESS) {
1425 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1426 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001427 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001428 return;
1429 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001430 }
1431
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001432 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001433 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001434 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001435 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001436 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001437 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001438 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001439 pjsua_perror(THIS_FILE, "Unable to create dialog",
1440 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001441 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001442 return;
1443 }
1444
Benny Prijonof9c40c32007-06-28 07:20:26 +00001445 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001446 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1447 if (status != PJ_SUCCESS) {
1448 pjsua_var.buddy[index].sub = NULL;
1449 pjsua_perror(THIS_FILE, "Unable to create presence client",
1450 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001451 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001452 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001453 return;
1454 }
1455
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001456 /* If account is locked to specific transport, then lock dialog
1457 * to this transport too.
1458 */
1459 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1460 pjsip_tpselector tp_sel;
1461
1462 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001463 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001464 }
1465
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001466 /* Set route-set */
1467 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001468 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 }
1470
1471 /* Set credentials */
1472 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001473 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001474 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001475 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001476
Benny Prijono48ab2b72007-11-08 09:24:30 +00001477 /* Set authentication preference */
1478 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1479
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001480 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001481
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001482 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001483 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001484 if (buddy->sub) {
1485 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1486 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001488 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1489 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001490 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001491 return;
1492 }
1493
Benny Prijono21b9ad92006-08-15 13:11:22 +00001494 pjsua_process_msg_data(tdata, NULL);
1495
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001497 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001498 if (buddy->sub) {
1499 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1500 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001501 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001502 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1503 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001504 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001505 return;
1506 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001507
1508 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001509}
1510
1511
1512/* It does what it says... */
1513static void unsubscribe_buddy_presence(unsigned index)
1514{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001515 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001516 pjsip_tx_data *tdata;
1517 pj_status_t status;
1518
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001519 buddy = &pjsua_var.buddy[index];
1520
1521 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001522 return;
1523
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001524 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1525 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001526 return;
1527 }
1528
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001529 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001530 if (status == PJ_SUCCESS) {
1531 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001532 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001533 }
Benny Prijono834aee32006-02-19 01:38:06 +00001534
Benny Prijono48da92e2007-05-16 08:56:30 +00001535 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001536 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1537 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001538 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1539 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001540 }
1541}
1542
1543
Benny Prijonof9c40c32007-06-28 07:20:26 +00001544/* Lock all buddies */
1545#define LOCK_BUDDIES unsigned cnt_ = 0; \
1546 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1547 unsigned i_; \
1548 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1549 if (pjsua_var.buddy[i_].sub) { \
1550 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1551 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1552 } \
1553 } \
1554 PJSUA_LOCK();
1555
1556/* Unlock all buddies */
1557#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1558 for (i_=0; i_<cnt_; ++i_) { \
1559 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1560 }
1561
1562
1563
Benny Prijono834aee32006-02-19 01:38:06 +00001564/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001566{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001567 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001568
Benny Prijonof9c40c32007-06-28 07:20:26 +00001569 LOCK_BUDDIES;
1570
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001572
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573 if (!pjsua_var.buddy[i].uri.slen)
1574 continue;
1575
1576 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001577 subscribe_buddy_presence(i);
1578
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001579 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001580 unsubscribe_buddy_presence(i);
1581
1582 }
1583 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001584
1585 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001586}
1587
Benny Prijono7a5f5102007-05-29 00:33:09 +00001588/* Timer callback to re-create client subscription */
1589static void pres_timer_cb(pj_timer_heap_t *th,
1590 pj_timer_entry *entry)
1591{
1592 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1593
Benny Prijono7a5f5102007-05-29 00:33:09 +00001594 entry->id = PJ_FALSE;
1595 refresh_client_subscriptions();
1596
1597 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1598 entry->id = PJ_TRUE;
1599
Benny Prijonof9c40c32007-06-28 07:20:26 +00001600 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001601}
1602
Benny Prijono834aee32006-02-19 01:38:06 +00001603
1604/*
1605 * Init presence
1606 */
1607pj_status_t pjsua_pres_init()
1608{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001609 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001610 pj_status_t status;
1611
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001613 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001614 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1615 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001616 }
1617
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1619 reset_buddy(i);
1620 }
1621
Benny Prijono834aee32006-02-19 01:38:06 +00001622 return status;
1623}
1624
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625
Benny Prijono834aee32006-02-19 01:38:06 +00001626/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001628 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001629pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001630{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001631 /* Start presence timer to re-subscribe to buddy's presence when
1632 * subscription has failed.
1633 */
1634 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1635 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1636
1637 pjsua_var.pres_timer.cb = &pres_timer_cb;
1638 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1639 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001640 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001641 }
1642
Benny Prijono9fc735d2006-05-28 14:58:12 +00001643 return PJ_SUCCESS;
1644}
1645
1646
1647/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001648 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001649 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001650void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001651{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001652 unsigned i;
1653
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001654 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001655
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1657 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001658 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659 }
Benny Prijono834aee32006-02-19 01:38:06 +00001660}
1661
1662
1663/*
1664 * Shutdown presence.
1665 */
1666void pjsua_pres_shutdown(void)
1667{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001668 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001669
Benny Prijono7a5f5102007-05-29 00:33:09 +00001670 if (pjsua_var.pres_timer.id != 0) {
1671 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1672 pjsua_var.pres_timer.id = PJ_FALSE;
1673 }
1674
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1676 if (!pjsua_var.acc[i].valid)
1677 continue;
1678 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001679 }
1680
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001681 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1682 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001683 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001684
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001685 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001686}