blob: d9676594123e8b3379320d79a5f73a213fe0d1f5 [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono834aee32006-02-19 01:38:06 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijono834aee32006-02-19 01:38:06 +000021
Benny Prijono834aee32006-02-19 01:38:06 +000022
23#define THIS_FILE "pjsua_pres.c"
24
Benny Prijonoa17496a2007-10-31 10:20:31 +000025
26static void subscribe_buddy_presence(unsigned index);
27
28
29/*
30 * Find buddy.
31 */
32static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
33{
34 const pjsip_sip_uri *sip_uri;
35 unsigned i;
36
Benny Prijonof0f8fd12007-11-10 12:05:59 +000037 uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +000038
39 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
40 return PJSUA_INVALID_ID;
41
42 sip_uri = (const pjsip_sip_uri*) uri;
43
44 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
45 const pjsua_buddy *b = &pjsua_var.buddy[i];
46
47 if (!pjsua_buddy_is_valid(i))
48 continue;
49
50 if (pj_stricmp(&sip_uri->user, &b->name)==0 &&
51 pj_stricmp(&sip_uri->host, &b->host)==0 &&
52 (sip_uri->port==(int)b->port || (sip_uri->port==0 && b->port==5060)))
53 {
54 /* Match */
55 return i;
56 }
57 }
58
59 return PJSUA_INVALID_ID;
60}
Benny Prijono7a5f5102007-05-29 00:33:09 +000061
Benny Prijono834aee32006-02-19 01:38:06 +000062
Benny Prijonoeebe9af2006-06-13 22:57:13 +000063/*
64 * Get total number of buddies.
65 */
66PJ_DEF(unsigned) pjsua_get_buddy_count(void)
67{
68 return pjsua_var.buddy_cnt;
69}
Benny Prijono834aee32006-02-19 01:38:06 +000070
Benny Prijonoeebe9af2006-06-13 22:57:13 +000071
72/*
Benny Prijono705e7842008-07-21 18:12:51 +000073 * Find buddy.
74 */
75PJ_DEF(pjsua_buddy_id) pjsua_buddy_find(const pj_str_t *uri_str)
76{
77 pj_str_t input;
78 pj_pool_t *pool;
79 pjsip_uri *uri;
80 pjsua_buddy_id buddy_id;
81
82 pool = pjsua_pool_create("buddyfind", 512, 512);
83 pj_strdup_with_null(pool, &input, uri_str);
84
85 uri = pjsip_parse_uri(pool, input.ptr, input.slen, 0);
86 if (!uri)
87 buddy_id = PJSUA_INVALID_ID;
88 else
89 buddy_id = pjsua_find_buddy(uri);
90
91 pj_pool_release(pool);
92
93 return buddy_id;
94}
95
96
97/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000098 * Check if buddy ID is valid.
99 */
100PJ_DEF(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id)
101{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000102 return buddy_id>=0 && buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy) &&
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 pjsua_var.buddy[buddy_id].uri.slen != 0;
104}
105
106
107/*
108 * Enum buddy IDs.
109 */
110PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[],
111 unsigned *count)
112{
113 unsigned i, c;
114
115 PJ_ASSERT_RETURN(ids && count, PJ_EINVAL);
116
117 PJSUA_LOCK();
118
119 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
120 if (!pjsua_var.buddy[i].uri.slen)
121 continue;
122 ids[c] = i;
123 ++c;
124 }
125
126 *count = c;
127
128 PJSUA_UNLOCK();
129
130 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131}
132
133
134/*
135 * Get detailed buddy info.
136 */
137PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
138 pjsua_buddy_info *info)
139{
Benny Prijono20da7992008-12-18 16:48:43 +0000140 unsigned total=0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000141 pjsua_buddy *buddy;
142
143 PJ_ASSERT_RETURN(buddy_id>=0 &&
144 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
145 PJ_EINVAL);
146
147 PJSUA_LOCK();
148
Benny Prijonoac623b32006-07-03 15:19:31 +0000149 pj_bzero(info, sizeof(pjsua_buddy_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150
151 buddy = &pjsua_var.buddy[buddy_id];
152 info->id = buddy->index;
153 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
154 PJSUA_UNLOCK();
155 return PJ_SUCCESS;
156 }
157
158 /* uri */
159 info->uri.ptr = info->buf_ + total;
160 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
161 total += info->uri.slen;
162
163 /* contact */
164 info->contact.ptr = info->buf_ + total;
165 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
166 total += info->contact.slen;
Benny Prijono97276602007-06-23 01:07:08 +0000167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 /* status and status text */
169 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
170 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
171 info->status_text = pj_str("?");
172 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
173 info->status = PJSUA_BUDDY_STATUS_ONLINE;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000174
175 /* copy RPID information */
176 info->rpid = buddy->status.info[0].rpid;
177
178 if (info->rpid.note.slen)
179 info->status_text = info->rpid.note;
180 else
181 info->status_text = pj_str("Online");
182
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183 } else {
184 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
185 info->status_text = pj_str("Offline");
186 }
187
188 /* monitor pres */
189 info->monitor_pres = buddy->monitor;
190
Benny Prijono63fba012008-07-17 14:19:10 +0000191 /* subscription state and termination reason */
192 if (buddy->sub) {
193 info->sub_state = pjsip_evsub_get_state(buddy->sub);
194 if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
195 total < sizeof(info->buf_))
196 {
197 info->sub_term_reason.ptr = info->buf_ + total;
198 pj_strncpy(&info->sub_term_reason,
199 pjsip_evsub_get_termination_reason(buddy->sub),
200 sizeof(info->buf_) - total);
201 total += info->sub_term_reason.slen;
202 } else {
203 info->sub_term_reason = pj_str("");
204 }
205 } else if (total < sizeof(info->buf_)) {
206 info->sub_term_reason.ptr = info->buf_ + total;
207 pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
208 sizeof(info->buf_) - total);
209 total += info->sub_term_reason.slen;
210 } else {
211 info->sub_term_reason = pj_str("");
212 }
213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 PJSUA_UNLOCK();
215 return PJ_SUCCESS;
216}
217
Benny Prijono705e7842008-07-21 18:12:51 +0000218/*
219 * Set the user data associated with the buddy object.
220 */
221PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
222 void *user_data)
223{
224 PJ_ASSERT_RETURN(buddy_id>=0 &&
225 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
226 PJ_EINVAL);
227
228 PJSUA_LOCK();
229
230 pjsua_var.buddy[buddy_id].user_data = user_data;
231
232 PJSUA_UNLOCK();
233
234 return PJ_SUCCESS;
235}
236
237
238/*
239 * Get the user data associated with the budy object.
240 */
241PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
242{
243 void *user_data;
244
245 PJ_ASSERT_RETURN(buddy_id>=0 &&
246 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
247 NULL);
248
249 PJSUA_LOCK();
250
251 user_data = pjsua_var.buddy[buddy_id].user_data;
252
253 PJSUA_UNLOCK();
254
255 return user_data;
256}
257
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000258
259/*
260 * Reset buddy descriptor.
261 */
262static void reset_buddy(pjsua_buddy_id id)
263{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000264 pj_pool_t *pool = pjsua_var.buddy[id].pool;
Benny Prijonoac623b32006-07-03 15:19:31 +0000265 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000266 pjsua_var.buddy[id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000267 pjsua_var.buddy[id].index = id;
268}
269
270
271/*
272 * Add new buddy.
273 */
274PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
275 pjsua_buddy_id *p_buddy_id)
276{
277 pjsip_name_addr *url;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000278 pjsua_buddy *buddy;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000279 pjsip_sip_uri *sip_uri;
280 int index;
281 pj_str_t tmp;
282
283 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
284 PJ_ARRAY_SIZE(pjsua_var.buddy),
285 PJ_ETOOMANY);
286
287 PJSUA_LOCK();
288
289 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000290 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000291 if (pjsua_var.buddy[index].uri.slen == 0)
292 break;
293 }
294
295 /* Expect to find an empty slot */
296 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
297 PJSUA_UNLOCK();
298 /* This shouldn't happen */
299 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
300 return PJ_ETOOMANY;
301 }
302
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000303 buddy = &pjsua_var.buddy[index];
304
305 /* Create pool for this buddy */
306 if (buddy->pool) {
307 pj_pool_reset(buddy->pool);
308 } else {
309 char name[PJ_MAX_OBJ_NAME];
310 pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
311 buddy->pool = pjsua_pool_create(name, 512, 256);
312 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000313
Benny Prijono63fba012008-07-17 14:19:10 +0000314 /* Init buffers for presence subscription status */
315 buddy->term_reason.ptr = (char*)
316 pj_pool_alloc(buddy->pool,
317 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
318
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000319 /* Get name and display name for buddy */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000320 pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
321 url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000322 PJSIP_PARSE_URI_AS_NAMEADDR);
323
324 if (url == NULL) {
325 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000326 pj_pool_release(buddy->pool);
327 buddy->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000328 PJSUA_UNLOCK();
329 return PJSIP_EINVALIDURI;
330 }
331
Benny Prijonofc493592007-02-18 20:56:32 +0000332 /* Only support SIP schemes */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000333 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
334 pj_pool_release(buddy->pool);
335 buddy->pool = NULL;
336 PJSUA_UNLOCK();
Benny Prijonofc493592007-02-18 20:56:32 +0000337 return PJSIP_EINVALIDSCHEME;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000338 }
Benny Prijonofc493592007-02-18 20:56:32 +0000339
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000340 /* Reset buddy, to make sure everything is cleared with default
341 * values
342 */
343 reset_buddy(index);
344
345 /* Save URI */
346 pjsua_var.buddy[index].uri = tmp;
347
Benny Prijono9c1528f2007-02-10 19:22:25 +0000348 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 pjsua_var.buddy[index].name = sip_uri->user;
350 pjsua_var.buddy[index].display = url->display;
351 pjsua_var.buddy[index].host = sip_uri->host;
352 pjsua_var.buddy[index].port = sip_uri->port;
353 pjsua_var.buddy[index].monitor = cfg->subscribe;
354 if (pjsua_var.buddy[index].port == 0)
355 pjsua_var.buddy[index].port = 5060;
356
Benny Prijono705e7842008-07-21 18:12:51 +0000357 /* Save user data */
358 pjsua_var.buddy[index].user_data = (void*)cfg->user_data;
359
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000360 if (p_buddy_id)
361 *p_buddy_id = index;
362
363 pjsua_var.buddy_cnt++;
364
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365 PJSUA_UNLOCK();
366
Benny Prijonof9c40c32007-06-28 07:20:26 +0000367 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
368
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000369 return PJ_SUCCESS;
370}
371
372
373/*
374 * Delete buddy.
375 */
376PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
377{
378 PJ_ASSERT_RETURN(buddy_id>=0 &&
379 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
380 PJ_EINVAL);
381
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000382 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 return PJ_SUCCESS;
384 }
385
386 /* Unsubscribe presence */
387 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
388
Benny Prijonof9c40c32007-06-28 07:20:26 +0000389 PJSUA_LOCK();
390
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000391 /* Remove buddy */
392 pjsua_var.buddy[buddy_id].uri.slen = 0;
393 pjsua_var.buddy_cnt--;
394
395 /* Reset buddy struct */
396 reset_buddy(buddy_id);
397
398 PJSUA_UNLOCK();
399 return PJ_SUCCESS;
400}
401
402
403/*
404 * Enable/disable buddy's presence monitoring.
405 */
406PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
407 pj_bool_t subscribe)
408{
409 pjsua_buddy *buddy;
410
411 PJ_ASSERT_RETURN(buddy_id>=0 &&
412 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
413 PJ_EINVAL);
414
415 PJSUA_LOCK();
416
417 buddy = &pjsua_var.buddy[buddy_id];
418 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000419
420 PJSUA_UNLOCK();
421
Benny Prijonof9c40c32007-06-28 07:20:26 +0000422 pjsua_pres_refresh();
423
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 return PJ_SUCCESS;
425}
426
427
428/*
Benny Prijono10861432007-10-31 10:54:53 +0000429 * Update buddy's presence.
430 */
431PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
432{
433 pjsua_buddy *buddy;
434
435 PJ_ASSERT_RETURN(buddy_id>=0 &&
436 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
437 PJ_EINVAL);
438
439 PJSUA_LOCK();
440
441 buddy = &pjsua_var.buddy[buddy_id];
442
443 /* Return error if buddy's presence monitoring is not enabled */
444 if (!buddy->monitor) {
445 PJSUA_UNLOCK();
446 return PJ_EINVALIDOP;
447 }
448
449 /* Ignore if presence is already active for the buddy */
450 if (buddy->sub) {
451 PJSUA_UNLOCK();
452 return PJ_SUCCESS;
453 }
454
455 /* Initiate presence subscription */
456 subscribe_buddy_presence(buddy_id);
457
458 PJSUA_UNLOCK();
459
460 return PJ_SUCCESS;
461}
462
463
464/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 * Dump presence subscriptions to log file.
466 */
467PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
468{
469 unsigned acc_id;
470 unsigned i;
471
472
473 PJSUA_LOCK();
474
475 /*
476 * When no detail is required, just dump number of server and client
477 * subscriptions.
478 */
479 if (verbose == PJ_FALSE) {
480
481 int count = 0;
482
483 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
484
485 if (!pjsua_var.acc[acc_id].valid)
486 continue;
487
488 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
489 struct pjsua_srv_pres *uapres;
490
491 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
492 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
493 ++count;
494 uapres = uapres->next;
495 }
496 }
497 }
498
499 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
500 count));
501
502 count = 0;
503
504 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
505 if (pjsua_var.buddy[i].uri.slen == 0)
506 continue;
507 if (pjsua_var.buddy[i].sub) {
508 ++count;
509 }
510 }
511
512 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
513 count));
514 PJSUA_UNLOCK();
515 return;
516 }
517
518
519 /*
520 * Dumping all server (UAS) subscriptions
521 */
522 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
523
524 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
525
526 if (!pjsua_var.acc[acc_id].valid)
527 continue;
528
529 PJ_LOG(3,(THIS_FILE, " %.*s",
530 (int)pjsua_var.acc[acc_id].cfg.id.slen,
531 pjsua_var.acc[acc_id].cfg.id.ptr));
532
533 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
534
535 PJ_LOG(3,(THIS_FILE, " - none - "));
536
537 } else {
538 struct pjsua_srv_pres *uapres;
539
540 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
541 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
542
543 PJ_LOG(3,(THIS_FILE, " %10s %s",
544 pjsip_evsub_get_state_name(uapres->sub),
545 uapres->remote));
546
547 uapres = uapres->next;
548 }
549 }
550 }
551
552 /*
553 * Dumping all client (UAC) subscriptions
554 */
555 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
556
557 if (pjsua_var.buddy_cnt == 0) {
558
559 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
560
561 } else {
562 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
563
564 if (pjsua_var.buddy[i].uri.slen == 0)
565 continue;
566
567 if (pjsua_var.buddy[i].sub) {
568 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
569 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
570 (int)pjsua_var.buddy[i].uri.slen,
571 pjsua_var.buddy[i].uri.ptr));
572 } else {
573 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
574 "(null)",
575 (int)pjsua_var.buddy[i].uri.slen,
576 pjsua_var.buddy[i].uri.ptr));
577 }
578 }
579 }
580
581 PJSUA_UNLOCK();
582}
583
584
585/***************************************************************************
586 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000587 */
588
589/* Proto */
590static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
591
592/* The module instance. */
593static pjsip_module mod_pjsua_pres =
594{
595 NULL, NULL, /* prev, next. */
596 { "mod-pjsua-pres", 14 }, /* Name. */
597 -1, /* Id */
598 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000599 NULL, /* load() */
600 NULL, /* start() */
601 NULL, /* stop() */
602 NULL, /* unload() */
603 &pres_on_rx_request, /* on_rx_request() */
604 NULL, /* on_rx_response() */
605 NULL, /* on_tx_request. */
606 NULL, /* on_tx_response() */
607 NULL, /* on_tsx_state() */
608
609};
610
611
612/* Callback called when *server* subscription state has changed. */
613static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
614{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000615 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000616
617 PJ_UNUSED_ARG(event);
618
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 PJSUA_LOCK();
620
Benny Prijonoa1e69682007-05-11 15:14:34 +0000621 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000622 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000623 pjsip_evsub_state state;
624
Benny Prijonoba736c42008-07-10 20:45:03 +0000625 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000626 uapres->remote, pjsip_evsub_get_state_name(sub)));
627
Benny Prijono63fba012008-07-17 14:19:10 +0000628 state = pjsip_evsub_get_state(sub);
629
630 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
631 pj_str_t from;
632
633 from = uapres->dlg->remote.info_str;
634 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
635 uapres, &from,
636 state, event);
637 }
638
639 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000640 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000641 pj_list_erase(uapres);
642 }
643 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644
645 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000646}
647
648/* This is called when request is received.
649 * We need to check for incoming SUBSCRIBE request.
650 */
651static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
652{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000653 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000654 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000655 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000656 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
657 pjsua_srv_pres *uapres;
658 pjsip_evsub *sub;
659 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000660 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000661 pjsip_status_code st_code;
662 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000663 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000664 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000665 pj_status_t status;
666
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000667 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000668 return PJ_FALSE;
669
670 /* Incoming SUBSCRIBE: */
671
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000672 PJSUA_LOCK();
673
Benny Prijonoa91a0032006-02-26 21:23:45 +0000674 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000675 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000676 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000677
Benny Prijono6f979412006-06-15 12:25:46 +0000678 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
679 acc_id));
680
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000681 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000682 if (acc->contact.slen) {
683 contact = acc->contact;
684 } else {
685 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
686 acc_id, rdata);
687 if (status != PJ_SUCCESS) {
688 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
689 status);
690 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000691 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
692 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000693 return PJ_TRUE;
694 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000695 }
696
Benny Prijono834aee32006-02-19 01:38:06 +0000697 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000698 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000699 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000700 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000701 pjsua_perror(THIS_FILE,
702 "Unable to create UAS dialog for subscription",
703 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000704 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000705 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
706 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000707 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000708 }
709
Benny Prijono48ab2b72007-11-08 09:24:30 +0000710 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000711 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000712 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000713
Benny Prijono834aee32006-02-19 01:38:06 +0000714 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000715 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000716 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
717
718 /* Create server presence subscription: */
719 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
720 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000721 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
722 pjsip_tx_data *tdata;
723
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000724 pjsua_perror(THIS_FILE, "Unable to create server subscription",
725 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000726
727 if (code==599 || code > 699 || code < 300) {
728 code = 400;
729 }
730
731 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
732 if (status == PJ_SUCCESS) {
733 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
734 tdata);
735 }
736
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000737 PJSUA_UNLOCK();
738 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000739 }
740
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000741 /* If account is locked to specific transport, then lock dialog
742 * to this transport too.
743 */
744 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
745 pjsip_tpselector tp_sel;
746
747 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
748 pjsip_dlg_set_transport(dlg, &tp_sel);
749 }
750
Benny Prijono834aee32006-02-19 01:38:06 +0000751 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000752 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000753 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000754 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000755 uapres->acc_id = acc_id;
756 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000757 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
758 uapres->remote, PJSIP_MAX_URL_SIZE);
759 if (status < 1)
760 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
761 else
762 uapres->remote[status] = '\0';
763
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000764 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000765
766 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000767 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000768
769
Benny Prijono63fba012008-07-17 14:19:10 +0000770 /* Capture the value of Expires header. */
771 expires_hdr = (pjsip_expires_hdr*)
772 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
773 NULL);
774 if (expires_hdr)
775 uapres->expires = expires_hdr->ivalue;
776 else
777 uapres->expires = -1;
778
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000779 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000780 reason = pj_str("OK");
781 pjsua_msg_data_init(&msg_data);
782
783 /* Notify application callback, if any */
784 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
785 pjsua_buddy_id buddy_id;
786
787 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
788
789 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
790 &dlg->remote.info_str,
791 rdata, &st_code, &reason,
792 &msg_data);
793 }
794
795 /* Handle rejection case */
796 if (st_code >= 300) {
797 pjsip_tx_data *tdata;
798
799 /* Create response */
800 status = pjsip_dlg_create_response(dlg, rdata, st_code,
801 &reason, &tdata);
802 if (status != PJ_SUCCESS) {
803 pjsua_perror(THIS_FILE, "Error creating response", status);
804 pj_list_erase(uapres);
805 pjsip_pres_terminate(sub, PJ_FALSE);
806 PJSUA_UNLOCK();
807 return PJ_FALSE;
808 }
809
810 /* Add header list, if any */
811 pjsua_process_msg_data(tdata, &msg_data);
812
813 /* Send the response */
814 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
815 tdata);
816 if (status != PJ_SUCCESS) {
817 pjsua_perror(THIS_FILE, "Error sending response", status);
818 /* This is not fatal */
819 }
820
821 /* Terminate presence subscription */
822 pj_list_erase(uapres);
823 pjsip_pres_terminate(sub, PJ_FALSE);
824 PJSUA_UNLOCK();
825 return PJ_TRUE;
826 }
827
828 /* Create and send 2xx response to the SUBSCRIBE request: */
829 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000830 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000831 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
832 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000833 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000834 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000835 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000836 return PJ_FALSE;
837 }
838
Benny Prijono63fba012008-07-17 14:19:10 +0000839 /* If code is 200, send NOTIFY now */
840 if (st_code == 200) {
841 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
842 NULL, NULL, PJ_TRUE, &msg_data);
843 }
844
845 /* Done: */
846
847 PJSUA_UNLOCK();
848
849 return PJ_TRUE;
850}
851
852
853/*
854 * Send NOTIFY.
855 */
856PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
857 pjsua_srv_pres *srv_pres,
858 pjsip_evsub_state ev_state,
859 const pj_str_t *state_str,
860 const pj_str_t *reason,
861 pj_bool_t with_body,
862 const pjsua_msg_data *msg_data)
863{
864 pjsua_acc *acc;
865 pjsip_pres_status pres_status;
866 pjsua_buddy_id buddy_id;
867 pjsip_tx_data *tdata;
868 pj_status_t status;
869
870 /* Check parameters */
871 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
872
873 /* Check that account ID is valid */
874 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
875 PJ_EINVAL);
876 /* Check that account is valid */
877 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
878
879 PJSUA_LOCK();
880
881 acc = &pjsua_var.acc[acc_id];
882
883 /* Check that the server presence subscription is still valid */
884 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
885 /* Subscription has been terminated */
886 PJSUA_UNLOCK();
887 return PJ_EINVALIDOP;
888 }
Benny Prijono834aee32006-02-19 01:38:06 +0000889
890 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000891 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000892 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000893 pres_status.info[0].basic_open = acc->online_status;
894 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000895 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000896 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000897 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000898 /* add RPID information */
899 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
900 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000901
Benny Prijono63fba012008-07-17 14:19:10 +0000902 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000903
Benny Prijonoc61cc042007-06-27 13:01:59 +0000904 /* Check expires value. If it's zero, send our presense state but
905 * set subscription state to TERMINATED.
906 */
Benny Prijono63fba012008-07-17 14:19:10 +0000907 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000908 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000909
Benny Prijono63fba012008-07-17 14:19:10 +0000910 /* Create and send the NOTIFY to active subscription: */
911 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
912 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000913 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000914 /* Force removal of message body if msg_body==FALSE */
915 if (!with_body) {
916 tdata->msg->body = NULL;
917 }
918 pjsua_process_msg_data(tdata, msg_data);
919 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000920 }
Benny Prijono834aee32006-02-19 01:38:06 +0000921
922 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000923 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
924 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000925 pj_list_erase(srv_pres);
926 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000927 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000928 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000929 }
930
931
Benny Prijonoa17496a2007-10-31 10:20:31 +0000932 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000933 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000934 if (buddy_id != PJSUA_INVALID_ID) {
935 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
936 if (b->monitor && b->sub == NULL) {
937 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
938 "activating outgoing subscription", buddy_id));
939 subscribe_buddy_presence(buddy_id);
940 }
941 }
942
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000943 PJSUA_UNLOCK();
944
Benny Prijono63fba012008-07-17 14:19:10 +0000945 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000946}
947
948
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000949/*
950 * Client presence publication callback.
951 */
952static void publish_cb(struct pjsip_publishc_cbparam *param)
953{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000954 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000955
956 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
957 if (param->status != PJ_SUCCESS) {
958 char errmsg[PJ_ERR_MSG_SIZE];
959
960 pj_strerror(param->status, errmsg, sizeof(errmsg));
961 PJ_LOG(1,(THIS_FILE,
962 "Client publication (PUBLISH) failed, status=%d, msg=%s",
963 param->status, errmsg));
964 } else {
965 PJ_LOG(1,(THIS_FILE,
966 "Client publication (PUBLISH) failed (%d/%.*s)",
967 param->code, (int)param->reason.slen,
968 param->reason.ptr));
969 }
970
971 pjsip_publishc_destroy(param->pubc);
972 acc->publish_sess = NULL;
973 }
974}
975
976
977/*
978 * Send PUBLISH request.
979 */
980static pj_status_t send_publish(int acc_id, pj_bool_t active)
981{
982 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
983 pjsua_acc *acc = &pjsua_var.acc[acc_id];
984 pjsip_pres_status pres_status;
985 pjsip_tx_data *tdata;
986 pj_status_t status;
987
988
989 /* Create PUBLISH request */
990 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000991 char *bpos;
992 pj_str_t entity;
993
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000994 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
995 if (status != PJ_SUCCESS) {
996 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
997 goto on_error;
998 }
999
1000 /* Set our online status: */
1001 pj_bzero(&pres_status, sizeof(pres_status));
1002 pres_status.info_cnt = 1;
1003 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001004 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001005 /* .. including RPID information */
1006 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1007 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001008
Benny Prijono8c6e8842007-02-24 15:33:54 +00001009 /* Be careful not to send PIDF with presence entity ID containing
1010 * "<" character.
1011 */
1012 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1013 char *epos = pj_strchr(&acc_cfg->id, '>');
1014 if (epos - bpos < 2) {
1015 pj_assert(!"Unexpected invalid URI");
1016 status = PJSIP_EINVALIDURI;
1017 goto on_error;
1018 }
1019 entity.ptr = bpos+1;
1020 entity.slen = epos - bpos - 1;
1021 } else {
1022 entity = acc_cfg->id;
1023 }
1024
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001025 /* Create and add PIDF message body */
1026 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001027 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001028 if (status != PJ_SUCCESS) {
1029 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1030 status);
1031 pjsip_tx_data_dec_ref(tdata);
1032 goto on_error;
1033 }
1034 } else {
1035 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1036 if (status != PJ_SUCCESS) {
1037 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1038 goto on_error;
1039 }
1040 }
1041
1042 /* Add headers etc */
1043 pjsua_process_msg_data(tdata, NULL);
1044
1045 /* Send the PUBLISH request */
1046 status = pjsip_publishc_send(acc->publish_sess, tdata);
1047 if (status != PJ_SUCCESS) {
1048 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1049 goto on_error;
1050 }
1051
1052 acc->publish_state = acc->online_status;
1053 return PJ_SUCCESS;
1054
1055on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001056 if (acc->publish_sess) {
1057 pjsip_publishc_destroy(acc->publish_sess);
1058 acc->publish_sess = NULL;
1059 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001060 return status;
1061}
1062
1063
1064/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001065pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001066{
1067 const pj_str_t STR_PRESENCE = { "presence", 8 };
1068 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1069 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1070 pj_status_t status;
1071
1072 /* Create and init client publication session */
1073 if (acc_cfg->publish_enabled) {
1074
1075 /* Create client publication */
1076 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
1077 &acc->publish_sess);
1078 if (status != PJ_SUCCESS) {
1079 acc->publish_sess = NULL;
1080 return status;
1081 }
1082
1083 /* Initialize client publication */
1084 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1085 &acc_cfg->id, &acc_cfg->id,
1086 &acc_cfg->id,
Benny Prijono32767ec2007-11-07 03:45:03 +00001087 PJSUA_PRES_TIMER);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001088 if (status != PJ_SUCCESS) {
1089 acc->publish_sess = NULL;
1090 return status;
1091 }
1092
Benny Prijono703b7d72007-03-20 09:13:24 +00001093 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001094 if (acc->cred_cnt) {
1095 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1096 acc->cred);
1097 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001098
1099 /* Set route-set */
1100 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1101
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001102 /* Send initial PUBLISH request */
1103 if (acc->online_status != 0) {
1104 status = send_publish(acc_id, PJ_TRUE);
1105 if (status != PJ_SUCCESS)
1106 return status;
1107 }
1108
1109 } else {
1110 acc->publish_sess = NULL;
1111 }
1112
1113 return PJ_SUCCESS;
1114}
1115
1116
1117/* Init presence for account */
1118pj_status_t pjsua_pres_init_acc(int acc_id)
1119{
1120 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1121
1122 /* Init presence subscription */
1123 pj_list_init(&acc->pres_srv_list);
1124
Benny Prijono8b6834f2007-02-24 13:29:22 +00001125 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001126}
1127
1128
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001129/* Terminate server subscription for the account */
1130void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001131{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001132 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1133 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001134 pjsua_srv_pres *uapres;
1135
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001136 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001137
Benny Prijono922933b2007-01-21 16:23:56 +00001138 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001139 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001140
1141 pjsip_pres_status pres_status;
1142 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001143 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001144 pjsip_tx_data *tdata;
1145
Benny Prijono5516f912008-05-05 12:06:08 +00001146 next = uapres->next;
1147
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001148 pjsip_pres_get_status(uapres->sub, &pres_status);
1149
1150 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1151 pjsip_pres_set_status(uapres->sub, &pres_status);
1152
1153 if (pjsip_pres_notify(uapres->sub,
1154 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1155 &reason, &tdata)==PJ_SUCCESS)
1156 {
1157 pjsip_pres_send_request(uapres->sub, tdata);
1158 }
1159
Benny Prijono5516f912008-05-05 12:06:08 +00001160 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001162
Benny Prijono922933b2007-01-21 16:23:56 +00001163 /* Clear server presence subscription list because account might be reused
1164 * later. */
1165 pj_list_init(&acc->pres_srv_list);
1166
1167 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001168 if (acc->publish_sess) {
1169 acc->online_status = PJ_FALSE;
1170 send_publish(acc_id, PJ_FALSE);
1171 if (acc->publish_sess) {
1172 pjsip_publishc_destroy(acc->publish_sess);
1173 acc->publish_sess = NULL;
1174 }
1175 acc_cfg->publish_enabled = PJ_FALSE;
1176 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001177}
1178
1179
Benny Prijono4461c7d2007-08-25 13:36:15 +00001180/* Update server subscription (e.g. when our online status has changed) */
1181void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001182{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001183 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1184 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 pjsua_srv_pres *uapres;
1186
1187 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1188
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001189 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001190
1191 pjsip_pres_status pres_status;
1192 pjsip_tx_data *tdata;
1193
1194 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001195
1196 /* Only send NOTIFY once subscription is active. Some subscriptions
1197 * may still be in NULL (when app is adding a new buddy while in the
1198 * on_incoming_subscribe() callback) or PENDING (when user approval is
1199 * being requested) state and we don't send NOTIFY to these subs until
1200 * the user accepted the request.
1201 */
1202 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1203 (force || pres_status.info[0].basic_open != acc->online_status))
1204 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001205
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001206 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001207 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1208 sizeof(pjrpid_element));
1209
Benny Prijono834aee32006-02-19 01:38:06 +00001210 pjsip_pres_set_status(uapres->sub, &pres_status);
1211
Benny Prijono21b9ad92006-08-15 13:11:22 +00001212 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1213 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001214 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001215 }
Benny Prijono834aee32006-02-19 01:38:06 +00001216 }
1217
1218 uapres = uapres->next;
1219 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001220
Benny Prijono8b6834f2007-02-24 13:29:22 +00001221 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1222 * session. If we don't have a PUBLISH session, then it could be
1223 * that we're waiting until registration has completed before we
1224 * send the first PUBLISH.
1225 */
1226 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001227 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001228 send_publish(acc_id, PJ_TRUE);
1229 }
1230 }
Benny Prijono834aee32006-02-19 01:38:06 +00001231}
1232
1233
1234
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001235/***************************************************************************
1236 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001237 */
1238
1239/* Callback called when *client* subscription state has changed. */
1240static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1241{
1242 pjsua_buddy *buddy;
1243
1244 PJ_UNUSED_ARG(event);
1245
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246 PJSUA_LOCK();
1247
Benny Prijonoa1e69682007-05-11 15:14:34 +00001248 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001249 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001250 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001251 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 (int)pjsua_var.buddy[buddy->index].uri.slen,
1253 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001254 pjsip_evsub_get_state_name(sub)));
1255
1256 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001257 if (buddy->term_reason.ptr == NULL) {
1258 buddy->term_reason.ptr = (char*)
1259 pj_pool_alloc(buddy->pool,
1260 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1261 }
1262 pj_strncpy(&buddy->term_reason,
1263 pjsip_evsub_get_termination_reason(sub),
1264 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1265 } else {
1266 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001267 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001268
1269 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001270 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1271 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001272
1273 /* Clear subscription */
1274 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1275 buddy->sub = NULL;
1276 buddy->status.info_cnt = 0;
1277 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1278 }
Benny Prijono834aee32006-02-19 01:38:06 +00001279 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280
1281 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001282}
1283
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001284
1285/* Callback when transaction state has changed. */
1286static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1287 pjsip_transaction *tsx,
1288 pjsip_event *event)
1289{
1290 pjsua_buddy *buddy;
1291 pjsip_contact_hdr *contact_hdr;
1292
1293 PJSUA_LOCK();
1294
Benny Prijonoa1e69682007-05-11 15:14:34 +00001295 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001296 if (!buddy) {
1297 PJSUA_UNLOCK();
1298 return;
1299 }
1300
1301 /* We only use this to update buddy's Contact, when it's not
1302 * set.
1303 */
1304 if (buddy->contact.slen != 0) {
1305 /* Contact already set */
1306 PJSUA_UNLOCK();
1307 return;
1308 }
1309
1310 /* Only care about 2xx response to outgoing SUBSCRIBE */
1311 if (tsx->status_code/100 != 2 ||
1312 tsx->role != PJSIP_UAC_ROLE ||
1313 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001314 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001315 {
1316 PJSUA_UNLOCK();
1317 return;
1318 }
1319
1320 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001321 contact_hdr = (pjsip_contact_hdr*)
1322 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001323 PJSIP_H_CONTACT, NULL);
1324 if (!contact_hdr) {
1325 PJSUA_UNLOCK();
1326 return;
1327 }
1328
Benny Prijonoa1e69682007-05-11 15:14:34 +00001329 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001330 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001331 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1332 contact_hdr->uri,
1333 buddy->contact.ptr,
1334 PJSIP_MAX_URL_SIZE);
1335 if (buddy->contact.slen < 0)
1336 buddy->contact.slen = 0;
1337
1338 PJSUA_UNLOCK();
1339}
1340
1341
Benny Prijono834aee32006-02-19 01:38:06 +00001342/* Callback called when we receive NOTIFY */
1343static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1344 pjsip_rx_data *rdata,
1345 int *p_st_code,
1346 pj_str_t **p_st_text,
1347 pjsip_hdr *res_hdr,
1348 pjsip_msg_body **p_body)
1349{
1350 pjsua_buddy *buddy;
1351
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001352 PJSUA_LOCK();
1353
Benny Prijonoa1e69682007-05-11 15:14:34 +00001354 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001355 if (buddy) {
1356 /* Update our info. */
1357 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001358 }
1359
1360 /* The default is to send 200 response to NOTIFY.
1361 * Just leave it there..
1362 */
1363 PJ_UNUSED_ARG(rdata);
1364 PJ_UNUSED_ARG(p_st_code);
1365 PJ_UNUSED_ARG(p_st_text);
1366 PJ_UNUSED_ARG(res_hdr);
1367 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001368
1369 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001370}
1371
1372
1373/* Event subscription callback. */
1374static pjsip_evsub_user pres_callback =
1375{
1376 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001378
1379 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1380 * we want to authenticate
1381 */
1382
1383 &pjsua_evsub_on_rx_notify,
1384
1385 NULL, /* on_client_refresh: Use default behaviour, which is to
1386 * refresh client subscription. */
1387
1388 NULL, /* on_server_timeout: Use default behaviour, which is to send
1389 * NOTIFY to terminate.
1390 */
1391};
1392
1393
1394/* It does what it says.. */
1395static void subscribe_buddy_presence(unsigned index)
1396{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001397 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001398 pjsua_buddy *buddy;
1399 int acc_id;
1400 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001401 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001402 pjsip_tx_data *tdata;
1403 pj_status_t status;
1404
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405 buddy = &pjsua_var.buddy[index];
1406 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001407
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001408 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001409
Benny Prijonob4a17c92006-07-10 14:40:21 +00001410 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1411 acc_id, index));
1412
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001413 /* Generate suitable Contact header unless one is already set in
1414 * the account
1415 */
1416 if (acc->contact.slen) {
1417 contact = acc->contact;
1418 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001419 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1420
1421 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001422 acc_id, &buddy->uri);
1423 if (status != PJ_SUCCESS) {
1424 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1425 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001426 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001427 return;
1428 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001429 }
1430
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001432 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001433 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001434 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001435 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001436 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001437 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001438 pjsua_perror(THIS_FILE, "Unable to create dialog",
1439 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001440 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001441 return;
1442 }
1443
Benny Prijonof9c40c32007-06-28 07:20:26 +00001444 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001445 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1446 if (status != PJ_SUCCESS) {
1447 pjsua_var.buddy[index].sub = NULL;
1448 pjsua_perror(THIS_FILE, "Unable to create presence client",
1449 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001450 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001451 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001452 return;
1453 }
1454
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001455 /* If account is locked to specific transport, then lock dialog
1456 * to this transport too.
1457 */
1458 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1459 pjsip_tpselector tp_sel;
1460
1461 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001462 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001463 }
1464
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001465 /* Set route-set */
1466 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001467 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001468 }
1469
1470 /* Set credentials */
1471 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001472 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001474 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001475
Benny Prijono48ab2b72007-11-08 09:24:30 +00001476 /* Set authentication preference */
1477 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1478
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001479 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001480
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001482 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001483 if (buddy->sub) {
1484 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1485 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001487 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1488 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001489 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001490 return;
1491 }
1492
Benny Prijono21b9ad92006-08-15 13:11:22 +00001493 pjsua_process_msg_data(tdata, NULL);
1494
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001495 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001496 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001497 if (buddy->sub) {
1498 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1499 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001500 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001501 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1502 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001503 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001504 return;
1505 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001506
1507 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001508}
1509
1510
1511/* It does what it says... */
1512static void unsubscribe_buddy_presence(unsigned index)
1513{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001514 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001515 pjsip_tx_data *tdata;
1516 pj_status_t status;
1517
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001518 buddy = &pjsua_var.buddy[index];
1519
1520 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001521 return;
1522
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001523 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1524 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001525 return;
1526 }
1527
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001528 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001529 if (status == PJ_SUCCESS) {
1530 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001531 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001532 }
Benny Prijono834aee32006-02-19 01:38:06 +00001533
Benny Prijono48da92e2007-05-16 08:56:30 +00001534 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001535 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1536 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001537 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1538 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001539 }
1540}
1541
1542
Benny Prijonof9c40c32007-06-28 07:20:26 +00001543/* Lock all buddies */
1544#define LOCK_BUDDIES unsigned cnt_ = 0; \
1545 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1546 unsigned i_; \
1547 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1548 if (pjsua_var.buddy[i_].sub) { \
1549 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1550 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1551 } \
1552 } \
1553 PJSUA_LOCK();
1554
1555/* Unlock all buddies */
1556#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1557 for (i_=0; i_<cnt_; ++i_) { \
1558 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1559 }
1560
1561
1562
Benny Prijono834aee32006-02-19 01:38:06 +00001563/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001564static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001565{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001566 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001567
Benny Prijonof9c40c32007-06-28 07:20:26 +00001568 LOCK_BUDDIES;
1569
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001570 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001571
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572 if (!pjsua_var.buddy[i].uri.slen)
1573 continue;
1574
1575 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001576 subscribe_buddy_presence(i);
1577
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001579 unsubscribe_buddy_presence(i);
1580
1581 }
1582 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001583
1584 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001585}
1586
Benny Prijono7a5f5102007-05-29 00:33:09 +00001587/* Timer callback to re-create client subscription */
1588static void pres_timer_cb(pj_timer_heap_t *th,
1589 pj_timer_entry *entry)
1590{
1591 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1592
Benny Prijono7a5f5102007-05-29 00:33:09 +00001593 entry->id = PJ_FALSE;
1594 refresh_client_subscriptions();
1595
1596 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1597 entry->id = PJ_TRUE;
1598
Benny Prijonof9c40c32007-06-28 07:20:26 +00001599 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001600}
1601
Benny Prijono834aee32006-02-19 01:38:06 +00001602
1603/*
1604 * Init presence
1605 */
1606pj_status_t pjsua_pres_init()
1607{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001608 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001609 pj_status_t status;
1610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001612 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001613 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1614 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001615 }
1616
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1618 reset_buddy(i);
1619 }
1620
Benny Prijono834aee32006-02-19 01:38:06 +00001621 return status;
1622}
1623
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624
Benny Prijono834aee32006-02-19 01:38:06 +00001625/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001626 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001627 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001628pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001629{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001630 /* Start presence timer to re-subscribe to buddy's presence when
1631 * subscription has failed.
1632 */
1633 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1634 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1635
1636 pjsua_var.pres_timer.cb = &pres_timer_cb;
1637 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1638 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001639 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001640 }
1641
Benny Prijono9fc735d2006-05-28 14:58:12 +00001642 return PJ_SUCCESS;
1643}
1644
1645
1646/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001647 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001648 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001650{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001651 unsigned i;
1652
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001654
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001655 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1656 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001657 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001658 }
Benny Prijono834aee32006-02-19 01:38:06 +00001659}
1660
1661
1662/*
1663 * Shutdown presence.
1664 */
1665void pjsua_pres_shutdown(void)
1666{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001667 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001668
Benny Prijono7a5f5102007-05-29 00:33:09 +00001669 if (pjsua_var.pres_timer.id != 0) {
1670 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1671 pjsua_var.pres_timer.id = PJ_FALSE;
1672 }
1673
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001674 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1675 if (!pjsua_var.acc[i].valid)
1676 continue;
1677 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001678 }
1679
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1681 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001682 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001683
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001684 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001685}