blob: f0a2100fbbf9ce0ef98ac32b4a550e6e716f7e73 [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{
140 int total=0;
141 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 Prijono1c2bf462006-03-05 11:54:02 +0000721 pjsip_dlg_terminate(dlg);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000722 pjsua_perror(THIS_FILE, "Unable to create server subscription",
723 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000724 PJSUA_UNLOCK();
725 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000726 }
727
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000728 /* If account is locked to specific transport, then lock dialog
729 * to this transport too.
730 */
731 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
732 pjsip_tpselector tp_sel;
733
734 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
735 pjsip_dlg_set_transport(dlg, &tp_sel);
736 }
737
Benny Prijono834aee32006-02-19 01:38:06 +0000738 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000739 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000740 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000741 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000742 uapres->acc_id = acc_id;
743 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000744 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
745 uapres->remote, PJSIP_MAX_URL_SIZE);
746 if (status < 1)
747 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
748 else
749 uapres->remote[status] = '\0';
750
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000751 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000752
753 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000754 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000755
756
Benny Prijono63fba012008-07-17 14:19:10 +0000757 /* Capture the value of Expires header. */
758 expires_hdr = (pjsip_expires_hdr*)
759 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
760 NULL);
761 if (expires_hdr)
762 uapres->expires = expires_hdr->ivalue;
763 else
764 uapres->expires = -1;
765
766 st_code = 200;
767 reason = pj_str("OK");
768 pjsua_msg_data_init(&msg_data);
769
770 /* Notify application callback, if any */
771 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
772 pjsua_buddy_id buddy_id;
773
774 buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
775
776 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
777 &dlg->remote.info_str,
778 rdata, &st_code, &reason,
779 &msg_data);
780 }
781
782 /* Handle rejection case */
783 if (st_code >= 300) {
784 pjsip_tx_data *tdata;
785
786 /* Create response */
787 status = pjsip_dlg_create_response(dlg, rdata, st_code,
788 &reason, &tdata);
789 if (status != PJ_SUCCESS) {
790 pjsua_perror(THIS_FILE, "Error creating response", status);
791 pj_list_erase(uapres);
792 pjsip_pres_terminate(sub, PJ_FALSE);
793 PJSUA_UNLOCK();
794 return PJ_FALSE;
795 }
796
797 /* Add header list, if any */
798 pjsua_process_msg_data(tdata, &msg_data);
799
800 /* Send the response */
801 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
802 tdata);
803 if (status != PJ_SUCCESS) {
804 pjsua_perror(THIS_FILE, "Error sending response", status);
805 /* This is not fatal */
806 }
807
808 /* Terminate presence subscription */
809 pj_list_erase(uapres);
810 pjsip_pres_terminate(sub, PJ_FALSE);
811 PJSUA_UNLOCK();
812 return PJ_TRUE;
813 }
814
815 /* Create and send 2xx response to the SUBSCRIBE request: */
816 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000817 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000818 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
819 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000820 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000821 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000822 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000823 return PJ_FALSE;
824 }
825
Benny Prijono63fba012008-07-17 14:19:10 +0000826 /* If code is 200, send NOTIFY now */
827 if (st_code == 200) {
828 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
829 NULL, NULL, PJ_TRUE, &msg_data);
830 }
831
832 /* Done: */
833
834 PJSUA_UNLOCK();
835
836 return PJ_TRUE;
837}
838
839
840/*
841 * Send NOTIFY.
842 */
843PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
844 pjsua_srv_pres *srv_pres,
845 pjsip_evsub_state ev_state,
846 const pj_str_t *state_str,
847 const pj_str_t *reason,
848 pj_bool_t with_body,
849 const pjsua_msg_data *msg_data)
850{
851 pjsua_acc *acc;
852 pjsip_pres_status pres_status;
853 pjsua_buddy_id buddy_id;
854 pjsip_tx_data *tdata;
855 pj_status_t status;
856
857 /* Check parameters */
858 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
859
860 /* Check that account ID is valid */
861 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
862 PJ_EINVAL);
863 /* Check that account is valid */
864 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
865
866 PJSUA_LOCK();
867
868 acc = &pjsua_var.acc[acc_id];
869
870 /* Check that the server presence subscription is still valid */
871 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
872 /* Subscription has been terminated */
873 PJSUA_UNLOCK();
874 return PJ_EINVALIDOP;
875 }
Benny Prijono834aee32006-02-19 01:38:06 +0000876
877 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000878 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000879 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +0000880 pres_status.info[0].basic_open = acc->online_status;
881 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000882 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000883 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000884 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +0000885 /* add RPID information */
886 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
887 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +0000888
Benny Prijono63fba012008-07-17 14:19:10 +0000889 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000890
Benny Prijonoc61cc042007-06-27 13:01:59 +0000891 /* Check expires value. If it's zero, send our presense state but
892 * set subscription state to TERMINATED.
893 */
Benny Prijono63fba012008-07-17 14:19:10 +0000894 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +0000895 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000896
Benny Prijono63fba012008-07-17 14:19:10 +0000897 /* Create and send the NOTIFY to active subscription: */
898 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
899 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000900 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +0000901 /* Force removal of message body if msg_body==FALSE */
902 if (!with_body) {
903 tdata->msg->body = NULL;
904 }
905 pjsua_process_msg_data(tdata, msg_data);
906 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000907 }
Benny Prijono834aee32006-02-19 01:38:06 +0000908
909 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000910 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
911 status);
Benny Prijono63fba012008-07-17 14:19:10 +0000912 pj_list_erase(srv_pres);
913 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000914 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +0000915 return status;
Benny Prijono834aee32006-02-19 01:38:06 +0000916 }
917
918
Benny Prijonoa17496a2007-10-31 10:20:31 +0000919 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono63fba012008-07-17 14:19:10 +0000920 buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +0000921 if (buddy_id != PJSUA_INVALID_ID) {
922 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
923 if (b->monitor && b->sub == NULL) {
924 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
925 "activating outgoing subscription", buddy_id));
926 subscribe_buddy_presence(buddy_id);
927 }
928 }
929
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000930 PJSUA_UNLOCK();
931
Benny Prijono63fba012008-07-17 14:19:10 +0000932 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +0000933}
934
935
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000936/*
937 * Client presence publication callback.
938 */
939static void publish_cb(struct pjsip_publishc_cbparam *param)
940{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000941 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000942
943 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
944 if (param->status != PJ_SUCCESS) {
945 char errmsg[PJ_ERR_MSG_SIZE];
946
947 pj_strerror(param->status, errmsg, sizeof(errmsg));
948 PJ_LOG(1,(THIS_FILE,
949 "Client publication (PUBLISH) failed, status=%d, msg=%s",
950 param->status, errmsg));
951 } else {
952 PJ_LOG(1,(THIS_FILE,
953 "Client publication (PUBLISH) failed (%d/%.*s)",
954 param->code, (int)param->reason.slen,
955 param->reason.ptr));
956 }
957
958 pjsip_publishc_destroy(param->pubc);
959 acc->publish_sess = NULL;
960 }
961}
962
963
964/*
965 * Send PUBLISH request.
966 */
967static pj_status_t send_publish(int acc_id, pj_bool_t active)
968{
969 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
970 pjsua_acc *acc = &pjsua_var.acc[acc_id];
971 pjsip_pres_status pres_status;
972 pjsip_tx_data *tdata;
973 pj_status_t status;
974
975
976 /* Create PUBLISH request */
977 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000978 char *bpos;
979 pj_str_t entity;
980
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000981 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
982 if (status != PJ_SUCCESS) {
983 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
984 goto on_error;
985 }
986
987 /* Set our online status: */
988 pj_bzero(&pres_status, sizeof(pres_status));
989 pres_status.info_cnt = 1;
990 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +0000991 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000992 /* .. including RPID information */
993 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
994 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000995
Benny Prijono8c6e8842007-02-24 15:33:54 +0000996 /* Be careful not to send PIDF with presence entity ID containing
997 * "<" character.
998 */
999 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1000 char *epos = pj_strchr(&acc_cfg->id, '>');
1001 if (epos - bpos < 2) {
1002 pj_assert(!"Unexpected invalid URI");
1003 status = PJSIP_EINVALIDURI;
1004 goto on_error;
1005 }
1006 entity.ptr = bpos+1;
1007 entity.slen = epos - bpos - 1;
1008 } else {
1009 entity = acc_cfg->id;
1010 }
1011
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001012 /* Create and add PIDF message body */
1013 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001014 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001015 if (status != PJ_SUCCESS) {
1016 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1017 status);
1018 pjsip_tx_data_dec_ref(tdata);
1019 goto on_error;
1020 }
1021 } else {
1022 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1023 if (status != PJ_SUCCESS) {
1024 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1025 goto on_error;
1026 }
1027 }
1028
1029 /* Add headers etc */
1030 pjsua_process_msg_data(tdata, NULL);
1031
1032 /* Send the PUBLISH request */
1033 status = pjsip_publishc_send(acc->publish_sess, tdata);
1034 if (status != PJ_SUCCESS) {
1035 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1036 goto on_error;
1037 }
1038
1039 acc->publish_state = acc->online_status;
1040 return PJ_SUCCESS;
1041
1042on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001043 if (acc->publish_sess) {
1044 pjsip_publishc_destroy(acc->publish_sess);
1045 acc->publish_sess = NULL;
1046 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001047 return status;
1048}
1049
1050
1051/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001052pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001053{
1054 const pj_str_t STR_PRESENCE = { "presence", 8 };
1055 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1056 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1057 pj_status_t status;
1058
1059 /* Create and init client publication session */
1060 if (acc_cfg->publish_enabled) {
1061
1062 /* Create client publication */
1063 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
1064 &acc->publish_sess);
1065 if (status != PJ_SUCCESS) {
1066 acc->publish_sess = NULL;
1067 return status;
1068 }
1069
1070 /* Initialize client publication */
1071 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1072 &acc_cfg->id, &acc_cfg->id,
1073 &acc_cfg->id,
Benny Prijono32767ec2007-11-07 03:45:03 +00001074 PJSUA_PRES_TIMER);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001075 if (status != PJ_SUCCESS) {
1076 acc->publish_sess = NULL;
1077 return status;
1078 }
1079
Benny Prijono703b7d72007-03-20 09:13:24 +00001080 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001081 if (acc->cred_cnt) {
1082 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1083 acc->cred);
1084 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001085
1086 /* Set route-set */
1087 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1088
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001089 /* Send initial PUBLISH request */
1090 if (acc->online_status != 0) {
1091 status = send_publish(acc_id, PJ_TRUE);
1092 if (status != PJ_SUCCESS)
1093 return status;
1094 }
1095
1096 } else {
1097 acc->publish_sess = NULL;
1098 }
1099
1100 return PJ_SUCCESS;
1101}
1102
1103
1104/* Init presence for account */
1105pj_status_t pjsua_pres_init_acc(int acc_id)
1106{
1107 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1108
1109 /* Init presence subscription */
1110 pj_list_init(&acc->pres_srv_list);
1111
Benny Prijono8b6834f2007-02-24 13:29:22 +00001112 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001113}
1114
1115
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001116/* Terminate server subscription for the account */
1117void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001118{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001119 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1120 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +00001121 pjsua_srv_pres *uapres;
1122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001123 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001124
Benny Prijono922933b2007-01-21 16:23:56 +00001125 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001126 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001127
1128 pjsip_pres_status pres_status;
1129 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001130 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001131 pjsip_tx_data *tdata;
1132
Benny Prijono5516f912008-05-05 12:06:08 +00001133 next = uapres->next;
1134
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001135 pjsip_pres_get_status(uapres->sub, &pres_status);
1136
1137 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1138 pjsip_pres_set_status(uapres->sub, &pres_status);
1139
1140 if (pjsip_pres_notify(uapres->sub,
1141 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1142 &reason, &tdata)==PJ_SUCCESS)
1143 {
1144 pjsip_pres_send_request(uapres->sub, tdata);
1145 }
1146
Benny Prijono5516f912008-05-05 12:06:08 +00001147 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001148 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001149
Benny Prijono922933b2007-01-21 16:23:56 +00001150 /* Clear server presence subscription list because account might be reused
1151 * later. */
1152 pj_list_init(&acc->pres_srv_list);
1153
1154 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001155 if (acc->publish_sess) {
1156 acc->online_status = PJ_FALSE;
1157 send_publish(acc_id, PJ_FALSE);
1158 if (acc->publish_sess) {
1159 pjsip_publishc_destroy(acc->publish_sess);
1160 acc->publish_sess = NULL;
1161 }
1162 acc_cfg->publish_enabled = PJ_FALSE;
1163 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001164}
1165
1166
Benny Prijono4461c7d2007-08-25 13:36:15 +00001167/* Update server subscription (e.g. when our online status has changed) */
1168void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001169{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001170 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1171 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172 pjsua_srv_pres *uapres;
1173
1174 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1175
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001176 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001177
1178 pjsip_pres_status pres_status;
1179 pjsip_tx_data *tdata;
1180
1181 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001182
1183 /* Only send NOTIFY once subscription is active. Some subscriptions
1184 * may still be in NULL (when app is adding a new buddy while in the
1185 * on_incoming_subscribe() callback) or PENDING (when user approval is
1186 * being requested) state and we don't send NOTIFY to these subs until
1187 * the user accepted the request.
1188 */
1189 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1190 (force || pres_status.info[0].basic_open != acc->online_status))
1191 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001192
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001193 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001194 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1195 sizeof(pjrpid_element));
1196
Benny Prijono834aee32006-02-19 01:38:06 +00001197 pjsip_pres_set_status(uapres->sub, &pres_status);
1198
Benny Prijono21b9ad92006-08-15 13:11:22 +00001199 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1200 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001201 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001202 }
Benny Prijono834aee32006-02-19 01:38:06 +00001203 }
1204
1205 uapres = uapres->next;
1206 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001207
Benny Prijono8b6834f2007-02-24 13:29:22 +00001208 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1209 * session. If we don't have a PUBLISH session, then it could be
1210 * that we're waiting until registration has completed before we
1211 * send the first PUBLISH.
1212 */
1213 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001214 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001215 send_publish(acc_id, PJ_TRUE);
1216 }
1217 }
Benny Prijono834aee32006-02-19 01:38:06 +00001218}
1219
1220
1221
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001222/***************************************************************************
1223 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001224 */
1225
1226/* Callback called when *client* subscription state has changed. */
1227static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1228{
1229 pjsua_buddy *buddy;
1230
1231 PJ_UNUSED_ARG(event);
1232
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001233 PJSUA_LOCK();
1234
Benny Prijonoa1e69682007-05-11 15:14:34 +00001235 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001236 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001237 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001238 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239 (int)pjsua_var.buddy[buddy->index].uri.slen,
1240 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001241 pjsip_evsub_get_state_name(sub)));
1242
1243 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001244 if (buddy->term_reason.ptr == NULL) {
1245 buddy->term_reason.ptr = (char*)
1246 pj_pool_alloc(buddy->pool,
1247 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1248 }
1249 pj_strncpy(&buddy->term_reason,
1250 pjsip_evsub_get_termination_reason(sub),
1251 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1252 } else {
1253 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001254 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001255
1256 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001257 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1258 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001259
1260 /* Clear subscription */
1261 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1262 buddy->sub = NULL;
1263 buddy->status.info_cnt = 0;
1264 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1265 }
Benny Prijono834aee32006-02-19 01:38:06 +00001266 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267
1268 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001269}
1270
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271
1272/* Callback when transaction state has changed. */
1273static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1274 pjsip_transaction *tsx,
1275 pjsip_event *event)
1276{
1277 pjsua_buddy *buddy;
1278 pjsip_contact_hdr *contact_hdr;
1279
1280 PJSUA_LOCK();
1281
Benny Prijonoa1e69682007-05-11 15:14:34 +00001282 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001283 if (!buddy) {
1284 PJSUA_UNLOCK();
1285 return;
1286 }
1287
1288 /* We only use this to update buddy's Contact, when it's not
1289 * set.
1290 */
1291 if (buddy->contact.slen != 0) {
1292 /* Contact already set */
1293 PJSUA_UNLOCK();
1294 return;
1295 }
1296
1297 /* Only care about 2xx response to outgoing SUBSCRIBE */
1298 if (tsx->status_code/100 != 2 ||
1299 tsx->role != PJSIP_UAC_ROLE ||
1300 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001301 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001302 {
1303 PJSUA_UNLOCK();
1304 return;
1305 }
1306
1307 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001308 contact_hdr = (pjsip_contact_hdr*)
1309 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001310 PJSIP_H_CONTACT, NULL);
1311 if (!contact_hdr) {
1312 PJSUA_UNLOCK();
1313 return;
1314 }
1315
Benny Prijonoa1e69682007-05-11 15:14:34 +00001316 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001317 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001318 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1319 contact_hdr->uri,
1320 buddy->contact.ptr,
1321 PJSIP_MAX_URL_SIZE);
1322 if (buddy->contact.slen < 0)
1323 buddy->contact.slen = 0;
1324
1325 PJSUA_UNLOCK();
1326}
1327
1328
Benny Prijono834aee32006-02-19 01:38:06 +00001329/* Callback called when we receive NOTIFY */
1330static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1331 pjsip_rx_data *rdata,
1332 int *p_st_code,
1333 pj_str_t **p_st_text,
1334 pjsip_hdr *res_hdr,
1335 pjsip_msg_body **p_body)
1336{
1337 pjsua_buddy *buddy;
1338
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001339 PJSUA_LOCK();
1340
Benny Prijonoa1e69682007-05-11 15:14:34 +00001341 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001342 if (buddy) {
1343 /* Update our info. */
1344 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001345 }
1346
1347 /* The default is to send 200 response to NOTIFY.
1348 * Just leave it there..
1349 */
1350 PJ_UNUSED_ARG(rdata);
1351 PJ_UNUSED_ARG(p_st_code);
1352 PJ_UNUSED_ARG(p_st_text);
1353 PJ_UNUSED_ARG(res_hdr);
1354 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355
1356 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001357}
1358
1359
1360/* Event subscription callback. */
1361static pjsip_evsub_user pres_callback =
1362{
1363 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001364 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001365
1366 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1367 * we want to authenticate
1368 */
1369
1370 &pjsua_evsub_on_rx_notify,
1371
1372 NULL, /* on_client_refresh: Use default behaviour, which is to
1373 * refresh client subscription. */
1374
1375 NULL, /* on_server_timeout: Use default behaviour, which is to send
1376 * NOTIFY to terminate.
1377 */
1378};
1379
1380
1381/* It does what it says.. */
1382static void subscribe_buddy_presence(unsigned index)
1383{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001384 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385 pjsua_buddy *buddy;
1386 int acc_id;
1387 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001388 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001389 pjsip_tx_data *tdata;
1390 pj_status_t status;
1391
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001392 buddy = &pjsua_var.buddy[index];
1393 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001394
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001396
Benny Prijonob4a17c92006-07-10 14:40:21 +00001397 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1398 acc_id, index));
1399
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001400 /* Generate suitable Contact header unless one is already set in
1401 * the account
1402 */
1403 if (acc->contact.slen) {
1404 contact = acc->contact;
1405 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001406 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1407
1408 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001409 acc_id, &buddy->uri);
1410 if (status != PJ_SUCCESS) {
1411 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1412 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001413 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001414 return;
1415 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001416 }
1417
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001418 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001419 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001420 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001421 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001422 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001423 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001424 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001425 pjsua_perror(THIS_FILE, "Unable to create dialog",
1426 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001427 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001428 return;
1429 }
1430
Benny Prijonof9c40c32007-06-28 07:20:26 +00001431 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001432 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1433 if (status != PJ_SUCCESS) {
1434 pjsua_var.buddy[index].sub = NULL;
1435 pjsua_perror(THIS_FILE, "Unable to create presence client",
1436 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001437 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001438 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001439 return;
1440 }
1441
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001442 /* If account is locked to specific transport, then lock dialog
1443 * to this transport too.
1444 */
1445 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1446 pjsip_tpselector tp_sel;
1447
1448 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001449 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001450 }
1451
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001452 /* Set route-set */
1453 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001454 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001455 }
1456
1457 /* Set credentials */
1458 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001459 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001460 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001461 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001462
Benny Prijono48ab2b72007-11-08 09:24:30 +00001463 /* Set authentication preference */
1464 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1465
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001466 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001467
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001468 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001469 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001470 if (buddy->sub) {
1471 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1472 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001474 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1475 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001476 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001477 return;
1478 }
1479
Benny Prijono21b9ad92006-08-15 13:11:22 +00001480 pjsua_process_msg_data(tdata, NULL);
1481
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001482 status = pjsip_pres_send_request(buddy->sub, 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 send 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 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001493
1494 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001495}
1496
1497
1498/* It does what it says... */
1499static void unsubscribe_buddy_presence(unsigned index)
1500{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001501 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001502 pjsip_tx_data *tdata;
1503 pj_status_t status;
1504
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001505 buddy = &pjsua_var.buddy[index];
1506
1507 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001508 return;
1509
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001510 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1511 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001512 return;
1513 }
1514
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001515 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001516 if (status == PJ_SUCCESS) {
1517 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001518 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001519 }
Benny Prijono834aee32006-02-19 01:38:06 +00001520
Benny Prijono48da92e2007-05-16 08:56:30 +00001521 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001522 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1523 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001524 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1525 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001526 }
1527}
1528
1529
Benny Prijonof9c40c32007-06-28 07:20:26 +00001530/* Lock all buddies */
1531#define LOCK_BUDDIES unsigned cnt_ = 0; \
1532 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1533 unsigned i_; \
1534 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1535 if (pjsua_var.buddy[i_].sub) { \
1536 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1537 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1538 } \
1539 } \
1540 PJSUA_LOCK();
1541
1542/* Unlock all buddies */
1543#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1544 for (i_=0; i_<cnt_; ++i_) { \
1545 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1546 }
1547
1548
1549
Benny Prijono834aee32006-02-19 01:38:06 +00001550/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001551static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001552{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001553 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001554
Benny Prijonof9c40c32007-06-28 07:20:26 +00001555 LOCK_BUDDIES;
1556
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001557 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001558
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001559 if (!pjsua_var.buddy[i].uri.slen)
1560 continue;
1561
1562 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001563 subscribe_buddy_presence(i);
1564
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001566 unsubscribe_buddy_presence(i);
1567
1568 }
1569 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001570
1571 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001572}
1573
Benny Prijono7a5f5102007-05-29 00:33:09 +00001574/* Timer callback to re-create client subscription */
1575static void pres_timer_cb(pj_timer_heap_t *th,
1576 pj_timer_entry *entry)
1577{
1578 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1579
Benny Prijono7a5f5102007-05-29 00:33:09 +00001580 entry->id = PJ_FALSE;
1581 refresh_client_subscriptions();
1582
1583 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1584 entry->id = PJ_TRUE;
1585
Benny Prijonof9c40c32007-06-28 07:20:26 +00001586 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001587}
1588
Benny Prijono834aee32006-02-19 01:38:06 +00001589
1590/*
1591 * Init presence
1592 */
1593pj_status_t pjsua_pres_init()
1594{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001596 pj_status_t status;
1597
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001598 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001599 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001600 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1601 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001602 }
1603
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1605 reset_buddy(i);
1606 }
1607
Benny Prijono834aee32006-02-19 01:38:06 +00001608 return status;
1609}
1610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611
Benny Prijono834aee32006-02-19 01:38:06 +00001612/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001614 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001615pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001616{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001617 /* Start presence timer to re-subscribe to buddy's presence when
1618 * subscription has failed.
1619 */
1620 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1621 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1622
1623 pjsua_var.pres_timer.cb = &pres_timer_cb;
1624 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1625 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001626 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001627 }
1628
Benny Prijono9fc735d2006-05-28 14:58:12 +00001629 return PJ_SUCCESS;
1630}
1631
1632
1633/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001634 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001635 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001636void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001637{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001638 unsigned i;
1639
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001641
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001642 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1643 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001644 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001645 }
Benny Prijono834aee32006-02-19 01:38:06 +00001646}
1647
1648
1649/*
1650 * Shutdown presence.
1651 */
1652void pjsua_pres_shutdown(void)
1653{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001654 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001655
Benny Prijono7a5f5102007-05-29 00:33:09 +00001656 if (pjsua_var.pres_timer.id != 0) {
1657 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1658 pjsua_var.pres_timer.id = PJ_FALSE;
1659 }
1660
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001661 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1662 if (!pjsua_var.acc[i].valid)
1663 continue;
1664 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001665 }
1666
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1668 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001669 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001670
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001671 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001672}