blob: 23050ff22483b24beecc445a17e3fdada9ff6cfa [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 Prijono4461c7d2007-08-25 13:36:15 +00001182 if (force || pres_status.info[0].basic_open != acc->online_status) {
1183
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001184 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001185 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1186 sizeof(pjrpid_element));
1187
Benny Prijono834aee32006-02-19 01:38:06 +00001188 pjsip_pres_set_status(uapres->sub, &pres_status);
1189
Benny Prijono21b9ad92006-08-15 13:11:22 +00001190 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1191 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001192 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001193 }
Benny Prijono834aee32006-02-19 01:38:06 +00001194 }
1195
1196 uapres = uapres->next;
1197 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001198
Benny Prijono8b6834f2007-02-24 13:29:22 +00001199 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1200 * session. If we don't have a PUBLISH session, then it could be
1201 * that we're waiting until registration has completed before we
1202 * send the first PUBLISH.
1203 */
1204 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001205 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001206 send_publish(acc_id, PJ_TRUE);
1207 }
1208 }
Benny Prijono834aee32006-02-19 01:38:06 +00001209}
1210
1211
1212
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001213/***************************************************************************
1214 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001215 */
1216
1217/* Callback called when *client* subscription state has changed. */
1218static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1219{
1220 pjsua_buddy *buddy;
1221
1222 PJ_UNUSED_ARG(event);
1223
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224 PJSUA_LOCK();
1225
Benny Prijonoa1e69682007-05-11 15:14:34 +00001226 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001227 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001228 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001229 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001230 (int)pjsua_var.buddy[buddy->index].uri.slen,
1231 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001232 pjsip_evsub_get_state_name(sub)));
1233
1234 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono63fba012008-07-17 14:19:10 +00001235 if (buddy->term_reason.ptr == NULL) {
1236 buddy->term_reason.ptr = (char*)
1237 pj_pool_alloc(buddy->pool,
1238 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1239 }
1240 pj_strncpy(&buddy->term_reason,
1241 pjsip_evsub_get_termination_reason(sub),
1242 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1243 } else {
1244 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001245 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001246
1247 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1249 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001250
1251 /* Clear subscription */
1252 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1253 buddy->sub = NULL;
1254 buddy->status.info_cnt = 0;
1255 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1256 }
Benny Prijono834aee32006-02-19 01:38:06 +00001257 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258
1259 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001260}
1261
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001262
1263/* Callback when transaction state has changed. */
1264static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1265 pjsip_transaction *tsx,
1266 pjsip_event *event)
1267{
1268 pjsua_buddy *buddy;
1269 pjsip_contact_hdr *contact_hdr;
1270
1271 PJSUA_LOCK();
1272
Benny Prijonoa1e69682007-05-11 15:14:34 +00001273 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274 if (!buddy) {
1275 PJSUA_UNLOCK();
1276 return;
1277 }
1278
1279 /* We only use this to update buddy's Contact, when it's not
1280 * set.
1281 */
1282 if (buddy->contact.slen != 0) {
1283 /* Contact already set */
1284 PJSUA_UNLOCK();
1285 return;
1286 }
1287
1288 /* Only care about 2xx response to outgoing SUBSCRIBE */
1289 if (tsx->status_code/100 != 2 ||
1290 tsx->role != PJSIP_UAC_ROLE ||
1291 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001292 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001293 {
1294 PJSUA_UNLOCK();
1295 return;
1296 }
1297
1298 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001299 contact_hdr = (pjsip_contact_hdr*)
1300 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001301 PJSIP_H_CONTACT, NULL);
1302 if (!contact_hdr) {
1303 PJSUA_UNLOCK();
1304 return;
1305 }
1306
Benny Prijonoa1e69682007-05-11 15:14:34 +00001307 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001308 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001309 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1310 contact_hdr->uri,
1311 buddy->contact.ptr,
1312 PJSIP_MAX_URL_SIZE);
1313 if (buddy->contact.slen < 0)
1314 buddy->contact.slen = 0;
1315
1316 PJSUA_UNLOCK();
1317}
1318
1319
Benny Prijono834aee32006-02-19 01:38:06 +00001320/* Callback called when we receive NOTIFY */
1321static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1322 pjsip_rx_data *rdata,
1323 int *p_st_code,
1324 pj_str_t **p_st_text,
1325 pjsip_hdr *res_hdr,
1326 pjsip_msg_body **p_body)
1327{
1328 pjsua_buddy *buddy;
1329
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001330 PJSUA_LOCK();
1331
Benny Prijonoa1e69682007-05-11 15:14:34 +00001332 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001333 if (buddy) {
1334 /* Update our info. */
1335 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001336 }
1337
1338 /* The default is to send 200 response to NOTIFY.
1339 * Just leave it there..
1340 */
1341 PJ_UNUSED_ARG(rdata);
1342 PJ_UNUSED_ARG(p_st_code);
1343 PJ_UNUSED_ARG(p_st_text);
1344 PJ_UNUSED_ARG(res_hdr);
1345 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001346
1347 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001348}
1349
1350
1351/* Event subscription callback. */
1352static pjsip_evsub_user pres_callback =
1353{
1354 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001356
1357 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1358 * we want to authenticate
1359 */
1360
1361 &pjsua_evsub_on_rx_notify,
1362
1363 NULL, /* on_client_refresh: Use default behaviour, which is to
1364 * refresh client subscription. */
1365
1366 NULL, /* on_server_timeout: Use default behaviour, which is to send
1367 * NOTIFY to terminate.
1368 */
1369};
1370
1371
1372/* It does what it says.. */
1373static void subscribe_buddy_presence(unsigned index)
1374{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001375 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001376 pjsua_buddy *buddy;
1377 int acc_id;
1378 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001379 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001380 pjsip_tx_data *tdata;
1381 pj_status_t status;
1382
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383 buddy = &pjsua_var.buddy[index];
1384 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001385
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001386 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001387
Benny Prijonob4a17c92006-07-10 14:40:21 +00001388 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1389 acc_id, index));
1390
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001391 /* Generate suitable Contact header unless one is already set in
1392 * the account
1393 */
1394 if (acc->contact.slen) {
1395 contact = acc->contact;
1396 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001397 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1398
1399 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001400 acc_id, &buddy->uri);
1401 if (status != PJ_SUCCESS) {
1402 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1403 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001404 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001405 return;
1406 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001407 }
1408
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001409 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001410 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001411 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001412 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001413 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001414 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001415 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001416 pjsua_perror(THIS_FILE, "Unable to create dialog",
1417 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001418 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001419 return;
1420 }
1421
Benny Prijonof9c40c32007-06-28 07:20:26 +00001422 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001423 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1424 if (status != PJ_SUCCESS) {
1425 pjsua_var.buddy[index].sub = NULL;
1426 pjsua_perror(THIS_FILE, "Unable to create presence client",
1427 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001428 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001429 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001430 return;
1431 }
1432
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001433 /* If account is locked to specific transport, then lock dialog
1434 * to this transport too.
1435 */
1436 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1437 pjsip_tpselector tp_sel;
1438
1439 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001440 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001441 }
1442
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443 /* Set route-set */
1444 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001445 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001446 }
1447
1448 /* Set credentials */
1449 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001450 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001451 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001452 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001453
Benny Prijono48ab2b72007-11-08 09:24:30 +00001454 /* Set authentication preference */
1455 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1456
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001457 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001458
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001459 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001460 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001461 if (buddy->sub) {
1462 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1463 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001464 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001465 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1466 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001467 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001468 return;
1469 }
1470
Benny Prijono21b9ad92006-08-15 13:11:22 +00001471 pjsua_process_msg_data(tdata, NULL);
1472
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001474 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001475 if (buddy->sub) {
1476 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1477 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001479 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1480 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001481 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001482 return;
1483 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001484
1485 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001486}
1487
1488
1489/* It does what it says... */
1490static void unsubscribe_buddy_presence(unsigned index)
1491{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001492 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001493 pjsip_tx_data *tdata;
1494 pj_status_t status;
1495
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 buddy = &pjsua_var.buddy[index];
1497
1498 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001499 return;
1500
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001501 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1502 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001503 return;
1504 }
1505
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001507 if (status == PJ_SUCCESS) {
1508 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001510 }
Benny Prijono834aee32006-02-19 01:38:06 +00001511
Benny Prijono48da92e2007-05-16 08:56:30 +00001512 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1514 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001515 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1516 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001517 }
1518}
1519
1520
Benny Prijonof9c40c32007-06-28 07:20:26 +00001521/* Lock all buddies */
1522#define LOCK_BUDDIES unsigned cnt_ = 0; \
1523 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1524 unsigned i_; \
1525 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1526 if (pjsua_var.buddy[i_].sub) { \
1527 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1528 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1529 } \
1530 } \
1531 PJSUA_LOCK();
1532
1533/* Unlock all buddies */
1534#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1535 for (i_=0; i_<cnt_; ++i_) { \
1536 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1537 }
1538
1539
1540
Benny Prijono834aee32006-02-19 01:38:06 +00001541/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001542static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001543{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001544 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001545
Benny Prijonof9c40c32007-06-28 07:20:26 +00001546 LOCK_BUDDIES;
1547
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001548 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001549
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001550 if (!pjsua_var.buddy[i].uri.slen)
1551 continue;
1552
1553 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001554 subscribe_buddy_presence(i);
1555
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001556 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001557 unsubscribe_buddy_presence(i);
1558
1559 }
1560 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001561
1562 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001563}
1564
Benny Prijono7a5f5102007-05-29 00:33:09 +00001565/* Timer callback to re-create client subscription */
1566static void pres_timer_cb(pj_timer_heap_t *th,
1567 pj_timer_entry *entry)
1568{
1569 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1570
Benny Prijono7a5f5102007-05-29 00:33:09 +00001571 entry->id = PJ_FALSE;
1572 refresh_client_subscriptions();
1573
1574 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1575 entry->id = PJ_TRUE;
1576
Benny Prijonof9c40c32007-06-28 07:20:26 +00001577 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001578}
1579
Benny Prijono834aee32006-02-19 01:38:06 +00001580
1581/*
1582 * Init presence
1583 */
1584pj_status_t pjsua_pres_init()
1585{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001587 pj_status_t status;
1588
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001589 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001590 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001591 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1592 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001593 }
1594
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1596 reset_buddy(i);
1597 }
1598
Benny Prijono834aee32006-02-19 01:38:06 +00001599 return status;
1600}
1601
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602
Benny Prijono834aee32006-02-19 01:38:06 +00001603/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001605 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001606pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001607{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001608 /* Start presence timer to re-subscribe to buddy's presence when
1609 * subscription has failed.
1610 */
1611 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1612 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1613
1614 pjsua_var.pres_timer.cb = &pres_timer_cb;
1615 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1616 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001617 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001618 }
1619
Benny Prijono9fc735d2006-05-28 14:58:12 +00001620 return PJ_SUCCESS;
1621}
1622
1623
1624/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001626 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001628{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001629 unsigned i;
1630
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001632
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001633 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1634 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001635 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001636 }
Benny Prijono834aee32006-02-19 01:38:06 +00001637}
1638
1639
1640/*
1641 * Shutdown presence.
1642 */
1643void pjsua_pres_shutdown(void)
1644{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001645 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001646
Benny Prijono7a5f5102007-05-29 00:33:09 +00001647 if (pjsua_var.pres_timer.id != 0) {
1648 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1649 pjsua_var.pres_timer.id = PJ_FALSE;
1650 }
1651
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001652 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1653 if (!pjsua_var.acc[i].valid)
1654 continue;
1655 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001656 }
1657
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001658 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1659 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001660 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001661
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001662 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001663}