blob: 88ea317fd76202b583da0e4f3bc1ce64c74d0385 [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono834aee32006-02-19 01:38:06 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
Benny Prijono834aee32006-02-19 01:38:06 +000022
Benny Prijono834aee32006-02-19 01:38:06 +000023
24#define THIS_FILE "pjsua_pres.c"
25
Benny Prijonoa17496a2007-10-31 10:20:31 +000026
Benny Prijono73bb7232009-10-20 13:56:26 +000027static void subscribe_buddy_presence(pjsua_buddy_id buddy_id);
28static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id);
Benny Prijonoa17496a2007-10-31 10:20:31 +000029
30
31/*
32 * Find buddy.
33 */
Benny Prijono73bb7232009-10-20 13:56:26 +000034static pjsua_buddy_id find_buddy(const pjsip_uri *uri)
Benny Prijonoa17496a2007-10-31 10:20:31 +000035{
36 const pjsip_sip_uri *sip_uri;
37 unsigned i;
38
Benny Prijonof0f8fd12007-11-10 12:05:59 +000039 uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +000040
41 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
42 return PJSUA_INVALID_ID;
43
44 sip_uri = (const pjsip_sip_uri*) uri;
45
46 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
47 const pjsua_buddy *b = &pjsua_var.buddy[i];
48
49 if (!pjsua_buddy_is_valid(i))
50 continue;
51
52 if (pj_stricmp(&sip_uri->user, &b->name)==0 &&
53 pj_stricmp(&sip_uri->host, &b->host)==0 &&
54 (sip_uri->port==(int)b->port || (sip_uri->port==0 && b->port==5060)))
55 {
56 /* Match */
57 return i;
58 }
59 }
60
61 return PJSUA_INVALID_ID;
62}
Benny Prijono7a5f5102007-05-29 00:33:09 +000063
Benny Prijono73bb7232009-10-20 13:56:26 +000064#define LOCK_DIALOG 1
65#define LOCK_PJSUA 2
66#define LOCK_ALL (LOCK_DIALOG | LOCK_PJSUA)
67
68/* Buddy lock object */
69struct buddy_lock
70{
71 pjsua_buddy *buddy;
72 pjsip_dialog *dlg;
73 pj_uint8_t flag;
74};
75
76/* Acquire lock to the specified buddy_id */
77pj_status_t lock_buddy(const char *title,
78 pjsua_buddy_id buddy_id,
79 struct buddy_lock *lck,
80 unsigned _unused_)
81{
82 enum { MAX_RETRY=50 };
83 pj_bool_t has_pjsua_lock = PJ_FALSE;
84 unsigned retry;
85
86 PJ_UNUSED_ARG(_unused_);
87
88 pj_bzero(lck, sizeof(*lck));
89
90 for (retry=0; retry<MAX_RETRY; ++retry) {
91
92 if (PJSUA_TRY_LOCK() != PJ_SUCCESS) {
93 pj_thread_sleep(retry/10);
94 continue;
95 }
96
97 has_pjsua_lock = PJ_TRUE;
98 lck->flag = LOCK_PJSUA;
99 lck->buddy = &pjsua_var.buddy[buddy_id];
100
101 if (lck->buddy->dlg == NULL)
102 return PJ_SUCCESS;
103
104 if (pjsip_dlg_try_inc_lock(lck->buddy->dlg) != PJ_SUCCESS) {
105 lck->flag = 0;
106 lck->buddy = NULL;
107 has_pjsua_lock = PJ_FALSE;
108 PJSUA_UNLOCK();
109 pj_thread_sleep(retry/10);
110 continue;
111 }
112
113 lck->dlg = lck->buddy->dlg;
114 lck->flag = LOCK_DIALOG;
115 PJSUA_UNLOCK();
116
117 break;
118 }
119
120 if (lck->flag == 0) {
121 if (has_pjsua_lock == PJ_FALSE)
122 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
123 "(possibly system has deadlocked) in %s",
124 title));
125 else
126 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
127 "(possibly system has deadlocked) in %s",
128 title));
129 return PJ_ETIMEDOUT;
130 }
131
132 return PJ_SUCCESS;
133}
134
135/* Release buddy lock */
136static void unlock_buddy(struct buddy_lock *lck)
137{
138 if (lck->flag & LOCK_DIALOG)
139 pjsip_dlg_dec_lock(lck->dlg);
140
141 if (lck->flag & LOCK_PJSUA)
142 PJSUA_UNLOCK();
143}
144
Benny Prijono834aee32006-02-19 01:38:06 +0000145
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146/*
147 * Get total number of buddies.
148 */
149PJ_DEF(unsigned) pjsua_get_buddy_count(void)
150{
151 return pjsua_var.buddy_cnt;
152}
Benny Prijono834aee32006-02-19 01:38:06 +0000153
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154
155/*
Benny Prijono705e7842008-07-21 18:12:51 +0000156 * Find buddy.
157 */
158PJ_DEF(pjsua_buddy_id) pjsua_buddy_find(const pj_str_t *uri_str)
159{
160 pj_str_t input;
161 pj_pool_t *pool;
162 pjsip_uri *uri;
163 pjsua_buddy_id buddy_id;
164
165 pool = pjsua_pool_create("buddyfind", 512, 512);
166 pj_strdup_with_null(pool, &input, uri_str);
167
168 uri = pjsip_parse_uri(pool, input.ptr, input.slen, 0);
169 if (!uri)
170 buddy_id = PJSUA_INVALID_ID;
Benny Prijono73bb7232009-10-20 13:56:26 +0000171 else {
172 PJSUA_LOCK();
173 buddy_id = find_buddy(uri);
174 PJSUA_UNLOCK();
175 }
Benny Prijono705e7842008-07-21 18:12:51 +0000176
177 pj_pool_release(pool);
178
179 return buddy_id;
180}
181
182
183/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184 * Check if buddy ID is valid.
185 */
186PJ_DEF(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id)
187{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000188 return buddy_id>=0 && buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy) &&
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 pjsua_var.buddy[buddy_id].uri.slen != 0;
190}
191
192
193/*
194 * Enum buddy IDs.
195 */
196PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[],
197 unsigned *count)
198{
199 unsigned i, c;
200
201 PJ_ASSERT_RETURN(ids && count, PJ_EINVAL);
202
203 PJSUA_LOCK();
204
205 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
206 if (!pjsua_var.buddy[i].uri.slen)
207 continue;
208 ids[c] = i;
209 ++c;
210 }
211
212 *count = c;
213
214 PJSUA_UNLOCK();
215
216 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217}
218
219
220/*
221 * Get detailed buddy info.
222 */
223PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
224 pjsua_buddy_info *info)
225{
Benny Prijono20da7992008-12-18 16:48:43 +0000226 unsigned total=0;
Benny Prijono73bb7232009-10-20 13:56:26 +0000227 struct buddy_lock lck;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 pjsua_buddy *buddy;
Benny Prijono73bb7232009-10-20 13:56:26 +0000229 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000230
Benny Prijono73bb7232009-10-20 13:56:26 +0000231 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000232
Benny Prijonoac623b32006-07-03 15:19:31 +0000233 pj_bzero(info, sizeof(pjsua_buddy_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000234
Benny Prijono73bb7232009-10-20 13:56:26 +0000235 status = lock_buddy("pjsua_buddy_get_info()", buddy_id, &lck, 0);
236 if (status != PJ_SUCCESS)
237 return status;
238
239 buddy = lck.buddy;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 info->id = buddy->index;
241 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijono73bb7232009-10-20 13:56:26 +0000242 unlock_buddy(&lck);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000243 return PJ_SUCCESS;
244 }
245
246 /* uri */
247 info->uri.ptr = info->buf_ + total;
248 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
249 total += info->uri.slen;
250
251 /* contact */
252 info->contact.ptr = info->buf_ + total;
253 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
254 total += info->contact.slen;
Benny Prijono97276602007-06-23 01:07:08 +0000255
Benny Prijono28add7e2009-06-15 16:03:40 +0000256 /* Presence status */
257 pj_memcpy(&info->pres_status, &buddy->status, sizeof(pjsip_pres_status));
258
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000259 /* status and status text */
260 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
261 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
262 info->status_text = pj_str("?");
263 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
264 info->status = PJSUA_BUDDY_STATUS_ONLINE;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000265
266 /* copy RPID information */
267 info->rpid = buddy->status.info[0].rpid;
268
269 if (info->rpid.note.slen)
270 info->status_text = info->rpid.note;
271 else
272 info->status_text = pj_str("Online");
273
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000274 } else {
275 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
Benny Prijono73bb7232009-10-20 13:56:26 +0000276 info->rpid = buddy->status.info[0].rpid;
277
278 if (info->rpid.note.slen)
279 info->status_text = info->rpid.note;
280 else
281 info->status_text = pj_str("Offline");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000282 }
283
284 /* monitor pres */
285 info->monitor_pres = buddy->monitor;
286
Benny Prijono63fba012008-07-17 14:19:10 +0000287 /* subscription state and termination reason */
Benny Prijono73bb7232009-10-20 13:56:26 +0000288 info->sub_term_code = buddy->term_code;
Benny Prijono63fba012008-07-17 14:19:10 +0000289 if (buddy->sub) {
290 info->sub_state = pjsip_evsub_get_state(buddy->sub);
Benny Prijonod06d8c52009-06-30 13:53:47 +0000291 info->sub_state_name = pjsip_evsub_get_state_name(buddy->sub);
Benny Prijono63fba012008-07-17 14:19:10 +0000292 if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
293 total < sizeof(info->buf_))
294 {
295 info->sub_term_reason.ptr = info->buf_ + total;
296 pj_strncpy(&info->sub_term_reason,
297 pjsip_evsub_get_termination_reason(buddy->sub),
298 sizeof(info->buf_) - total);
299 total += info->sub_term_reason.slen;
300 } else {
301 info->sub_term_reason = pj_str("");
302 }
303 } else if (total < sizeof(info->buf_)) {
Benny Prijonod06d8c52009-06-30 13:53:47 +0000304 info->sub_state_name = "NULL";
Benny Prijono63fba012008-07-17 14:19:10 +0000305 info->sub_term_reason.ptr = info->buf_ + total;
306 pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
307 sizeof(info->buf_) - total);
308 total += info->sub_term_reason.slen;
309 } else {
Benny Prijonod06d8c52009-06-30 13:53:47 +0000310 info->sub_state_name = "NULL";
Benny Prijono63fba012008-07-17 14:19:10 +0000311 info->sub_term_reason = pj_str("");
312 }
313
Benny Prijono73bb7232009-10-20 13:56:26 +0000314 unlock_buddy(&lck);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000315 return PJ_SUCCESS;
316}
317
Benny Prijono705e7842008-07-21 18:12:51 +0000318/*
319 * Set the user data associated with the buddy object.
320 */
321PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
322 void *user_data)
323{
Benny Prijono73bb7232009-10-20 13:56:26 +0000324 struct buddy_lock lck;
325 pj_status_t status;
Benny Prijono705e7842008-07-21 18:12:51 +0000326
Benny Prijono73bb7232009-10-20 13:56:26 +0000327 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
328
329 status = lock_buddy("pjsua_buddy_set_user_data()", buddy_id, &lck, 0);
330 if (status != PJ_SUCCESS)
331 return status;
Benny Prijono705e7842008-07-21 18:12:51 +0000332
333 pjsua_var.buddy[buddy_id].user_data = user_data;
334
Benny Prijono73bb7232009-10-20 13:56:26 +0000335 unlock_buddy(&lck);
Benny Prijono705e7842008-07-21 18:12:51 +0000336
337 return PJ_SUCCESS;
338}
339
340
341/*
342 * Get the user data associated with the budy object.
343 */
344PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
345{
Benny Prijono73bb7232009-10-20 13:56:26 +0000346 struct buddy_lock lck;
347 pj_status_t status;
Benny Prijono705e7842008-07-21 18:12:51 +0000348 void *user_data;
349
Benny Prijono73bb7232009-10-20 13:56:26 +0000350 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), NULL);
Benny Prijono705e7842008-07-21 18:12:51 +0000351
Benny Prijono73bb7232009-10-20 13:56:26 +0000352 status = lock_buddy("pjsua_buddy_get_user_data()", buddy_id, &lck, 0);
353 if (status != PJ_SUCCESS)
354 return NULL;
Benny Prijono705e7842008-07-21 18:12:51 +0000355
356 user_data = pjsua_var.buddy[buddy_id].user_data;
357
Benny Prijono73bb7232009-10-20 13:56:26 +0000358 unlock_buddy(&lck);
Benny Prijono705e7842008-07-21 18:12:51 +0000359
360 return user_data;
361}
362
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000363
364/*
365 * Reset buddy descriptor.
366 */
367static void reset_buddy(pjsua_buddy_id id)
368{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000369 pj_pool_t *pool = pjsua_var.buddy[id].pool;
Benny Prijonoac623b32006-07-03 15:19:31 +0000370 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000371 pjsua_var.buddy[id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000372 pjsua_var.buddy[id].index = id;
373}
374
375
376/*
377 * Add new buddy.
378 */
379PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
380 pjsua_buddy_id *p_buddy_id)
381{
382 pjsip_name_addr *url;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000383 pjsua_buddy *buddy;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 pjsip_sip_uri *sip_uri;
385 int index;
386 pj_str_t tmp;
387
388 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
389 PJ_ARRAY_SIZE(pjsua_var.buddy),
390 PJ_ETOOMANY);
391
Benny Prijonob90fd382011-09-18 14:59:56 +0000392 PJ_LOG(4,(THIS_FILE, "Adding buddy: %.*s",
393 (int)cfg->uri.slen, cfg->uri.ptr));
394 pj_log_push_indent();
395
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000396 PJSUA_LOCK();
397
398 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000399 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000400 if (pjsua_var.buddy[index].uri.slen == 0)
401 break;
402 }
403
404 /* Expect to find an empty slot */
405 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
406 PJSUA_UNLOCK();
407 /* This shouldn't happen */
408 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
Benny Prijonob90fd382011-09-18 14:59:56 +0000409 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000410 return PJ_ETOOMANY;
411 }
412
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000413 buddy = &pjsua_var.buddy[index];
414
415 /* Create pool for this buddy */
416 if (buddy->pool) {
417 pj_pool_reset(buddy->pool);
418 } else {
419 char name[PJ_MAX_OBJ_NAME];
420 pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
421 buddy->pool = pjsua_pool_create(name, 512, 256);
422 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423
Benny Prijono63fba012008-07-17 14:19:10 +0000424 /* Init buffers for presence subscription status */
425 buddy->term_reason.ptr = (char*)
426 pj_pool_alloc(buddy->pool,
427 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
428
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000429 /* Get name and display name for buddy */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000430 pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
431 url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000432 PJSIP_PARSE_URI_AS_NAMEADDR);
433
434 if (url == NULL) {
435 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000436 pj_pool_release(buddy->pool);
437 buddy->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000439 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 return PJSIP_EINVALIDURI;
441 }
442
Benny Prijonofc493592007-02-18 20:56:32 +0000443 /* Only support SIP schemes */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000444 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
445 pj_pool_release(buddy->pool);
446 buddy->pool = NULL;
447 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000448 pj_log_pop_indent();
Benny Prijonofc493592007-02-18 20:56:32 +0000449 return PJSIP_EINVALIDSCHEME;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000450 }
Benny Prijonofc493592007-02-18 20:56:32 +0000451
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000452 /* Reset buddy, to make sure everything is cleared with default
453 * values
454 */
455 reset_buddy(index);
456
457 /* Save URI */
458 pjsua_var.buddy[index].uri = tmp;
459
Benny Prijono9c1528f2007-02-10 19:22:25 +0000460 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000461 pjsua_var.buddy[index].name = sip_uri->user;
462 pjsua_var.buddy[index].display = url->display;
463 pjsua_var.buddy[index].host = sip_uri->host;
464 pjsua_var.buddy[index].port = sip_uri->port;
465 pjsua_var.buddy[index].monitor = cfg->subscribe;
466 if (pjsua_var.buddy[index].port == 0)
467 pjsua_var.buddy[index].port = 5060;
468
Benny Prijono705e7842008-07-21 18:12:51 +0000469 /* Save user data */
470 pjsua_var.buddy[index].user_data = (void*)cfg->user_data;
471
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000472 if (p_buddy_id)
473 *p_buddy_id = index;
474
475 pjsua_var.buddy_cnt++;
476
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000477 PJSUA_UNLOCK();
478
Benny Prijonob90fd382011-09-18 14:59:56 +0000479 PJ_LOG(4,(THIS_FILE, "Buddy %d added.", index));
480
Benny Prijonof9c40c32007-06-28 07:20:26 +0000481 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
482
Benny Prijonob90fd382011-09-18 14:59:56 +0000483 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000484 return PJ_SUCCESS;
485}
486
487
488/*
489 * Delete buddy.
490 */
491PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
492{
Benny Prijono73bb7232009-10-20 13:56:26 +0000493 struct buddy_lock lck;
494 pj_status_t status;
495
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 PJ_ASSERT_RETURN(buddy_id>=0 &&
497 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
498 PJ_EINVAL);
499
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000501 return PJ_SUCCESS;
502 }
503
Benny Prijono73bb7232009-10-20 13:56:26 +0000504 status = lock_buddy("pjsua_buddy_del()", buddy_id, &lck, 0);
505 if (status != PJ_SUCCESS)
506 return status;
507
Benny Prijonob90fd382011-09-18 14:59:56 +0000508 PJ_LOG(4,(THIS_FILE, "Buddy %d: deleting..", buddy_id));
509 pj_log_push_indent();
510
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000511 /* Unsubscribe presence */
512 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
513
Benny Prijonoa5776cb2009-04-14 15:11:23 +0000514 /* Not interested with further events for this buddy */
515 if (pjsua_var.buddy[buddy_id].sub) {
516 pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub,
517 pjsua_var.mod.id, NULL);
518 }
519
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000520 /* Remove buddy */
521 pjsua_var.buddy[buddy_id].uri.slen = 0;
522 pjsua_var.buddy_cnt--;
523
Benny Prijono011e3f22009-12-10 05:16:23 +0000524 /* Clear timer */
525 if (pjsua_var.buddy[buddy_id].timer.id) {
526 pjsua_cancel_timer(&pjsua_var.buddy[buddy_id].timer);
527 pjsua_var.buddy[buddy_id].timer.id = PJ_FALSE;
528 }
529
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000530 /* Reset buddy struct */
531 reset_buddy(buddy_id);
532
Benny Prijono73bb7232009-10-20 13:56:26 +0000533 unlock_buddy(&lck);
Benny Prijonob90fd382011-09-18 14:59:56 +0000534 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 return PJ_SUCCESS;
536}
537
538
539/*
540 * Enable/disable buddy's presence monitoring.
541 */
542PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
543 pj_bool_t subscribe)
544{
Benny Prijono73bb7232009-10-20 13:56:26 +0000545 struct buddy_lock lck;
546 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547
Benny Prijono73bb7232009-10-20 13:56:26 +0000548 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000549
Benny Prijono73bb7232009-10-20 13:56:26 +0000550 status = lock_buddy("pjsua_buddy_subscribe_pres()", buddy_id, &lck, 0);
551 if (status != PJ_SUCCESS)
552 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000553
Benny Prijonob90fd382011-09-18 14:59:56 +0000554 PJ_LOG(4,(THIS_FILE, "Buddy %d: unsubscribing presence..", buddy_id));
555 pj_log_push_indent();
556
Benny Prijono73bb7232009-10-20 13:56:26 +0000557 lck.buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558
Benny Prijono73bb7232009-10-20 13:56:26 +0000559 pjsua_buddy_update_pres(buddy_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560
Benny Prijono73bb7232009-10-20 13:56:26 +0000561 unlock_buddy(&lck);
Benny Prijonob90fd382011-09-18 14:59:56 +0000562 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000563 return PJ_SUCCESS;
564}
565
566
567/*
Benny Prijono10861432007-10-31 10:54:53 +0000568 * Update buddy's presence.
569 */
570PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
571{
Benny Prijono73bb7232009-10-20 13:56:26 +0000572 struct buddy_lock lck;
573 pj_status_t status;
Benny Prijono10861432007-10-31 10:54:53 +0000574
Benny Prijono73bb7232009-10-20 13:56:26 +0000575 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
Benny Prijono10861432007-10-31 10:54:53 +0000576
Benny Prijono73bb7232009-10-20 13:56:26 +0000577 status = lock_buddy("pjsua_buddy_update_pres()", buddy_id, &lck, 0);
578 if (status != PJ_SUCCESS)
579 return status;
Benny Prijono10861432007-10-31 10:54:53 +0000580
Benny Prijonob90fd382011-09-18 14:59:56 +0000581 PJ_LOG(4,(THIS_FILE, "Buddy %d: updating presence..", buddy_id));
582 pj_log_push_indent();
583
Benny Prijono73bb7232009-10-20 13:56:26 +0000584 /* Is this an unsubscribe request? */
585 if (!lck.buddy->monitor) {
586 unsubscribe_buddy_presence(buddy_id);
587 unlock_buddy(&lck);
Benny Prijonob90fd382011-09-18 14:59:56 +0000588 pj_log_pop_indent();
Benny Prijono73bb7232009-10-20 13:56:26 +0000589 return PJ_SUCCESS;
Benny Prijono10861432007-10-31 10:54:53 +0000590 }
591
592 /* Ignore if presence is already active for the buddy */
Benny Prijono73bb7232009-10-20 13:56:26 +0000593 if (lck.buddy->sub) {
594 unlock_buddy(&lck);
Benny Prijonob90fd382011-09-18 14:59:56 +0000595 pj_log_pop_indent();
Benny Prijono10861432007-10-31 10:54:53 +0000596 return PJ_SUCCESS;
597 }
598
599 /* Initiate presence subscription */
600 subscribe_buddy_presence(buddy_id);
601
Benny Prijono73bb7232009-10-20 13:56:26 +0000602 unlock_buddy(&lck);
Benny Prijonob90fd382011-09-18 14:59:56 +0000603 pj_log_pop_indent();
Benny Prijono10861432007-10-31 10:54:53 +0000604 return PJ_SUCCESS;
605}
606
607
608/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 * Dump presence subscriptions to log file.
610 */
611PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
612{
613 unsigned acc_id;
614 unsigned i;
615
616
617 PJSUA_LOCK();
618
619 /*
620 * When no detail is required, just dump number of server and client
621 * subscriptions.
622 */
623 if (verbose == PJ_FALSE) {
624
625 int count = 0;
626
627 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
628
629 if (!pjsua_var.acc[acc_id].valid)
630 continue;
631
632 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
633 struct pjsua_srv_pres *uapres;
634
635 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
636 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
637 ++count;
638 uapres = uapres->next;
639 }
640 }
641 }
642
643 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
644 count));
645
646 count = 0;
647
648 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
649 if (pjsua_var.buddy[i].uri.slen == 0)
650 continue;
651 if (pjsua_var.buddy[i].sub) {
652 ++count;
653 }
654 }
655
656 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
657 count));
658 PJSUA_UNLOCK();
659 return;
660 }
661
662
663 /*
664 * Dumping all server (UAS) subscriptions
665 */
666 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
667
668 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
669
670 if (!pjsua_var.acc[acc_id].valid)
671 continue;
672
673 PJ_LOG(3,(THIS_FILE, " %.*s",
674 (int)pjsua_var.acc[acc_id].cfg.id.slen,
675 pjsua_var.acc[acc_id].cfg.id.ptr));
676
677 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
678
679 PJ_LOG(3,(THIS_FILE, " - none - "));
680
681 } else {
682 struct pjsua_srv_pres *uapres;
683
684 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
685 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
686
687 PJ_LOG(3,(THIS_FILE, " %10s %s",
688 pjsip_evsub_get_state_name(uapres->sub),
689 uapres->remote));
690
691 uapres = uapres->next;
692 }
693 }
694 }
695
696 /*
697 * Dumping all client (UAC) subscriptions
698 */
699 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
700
701 if (pjsua_var.buddy_cnt == 0) {
702
703 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
704
705 } else {
706 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
707
708 if (pjsua_var.buddy[i].uri.slen == 0)
709 continue;
710
711 if (pjsua_var.buddy[i].sub) {
712 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
713 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
714 (int)pjsua_var.buddy[i].uri.slen,
715 pjsua_var.buddy[i].uri.ptr));
716 } else {
717 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
718 "(null)",
719 (int)pjsua_var.buddy[i].uri.slen,
720 pjsua_var.buddy[i].uri.ptr));
721 }
722 }
723 }
724
725 PJSUA_UNLOCK();
726}
727
728
729/***************************************************************************
730 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000731 */
732
733/* Proto */
734static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
735
736/* The module instance. */
737static pjsip_module mod_pjsua_pres =
738{
739 NULL, NULL, /* prev, next. */
740 { "mod-pjsua-pres", 14 }, /* Name. */
741 -1, /* Id */
742 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000743 NULL, /* load() */
744 NULL, /* start() */
745 NULL, /* stop() */
746 NULL, /* unload() */
747 &pres_on_rx_request, /* on_rx_request() */
748 NULL, /* on_rx_response() */
749 NULL, /* on_tx_request. */
750 NULL, /* on_tx_response() */
751 NULL, /* on_tsx_state() */
752
753};
754
755
756/* Callback called when *server* subscription state has changed. */
757static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
758{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000759 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000760
761 PJ_UNUSED_ARG(event);
762
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000763 PJSUA_LOCK();
764
Benny Prijonoa1e69682007-05-11 15:14:34 +0000765 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000766 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000767 pjsip_evsub_state state;
768
Benny Prijonoba736c42008-07-10 20:45:03 +0000769 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000770 uapres->remote, pjsip_evsub_get_state_name(sub)));
Benny Prijonob90fd382011-09-18 14:59:56 +0000771 pj_log_push_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000772
Benny Prijono63fba012008-07-17 14:19:10 +0000773 state = pjsip_evsub_get_state(sub);
774
775 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
776 pj_str_t from;
777
778 from = uapres->dlg->remote.info_str;
779 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
780 uapres, &from,
781 state, event);
782 }
783
784 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000785 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000786 pj_list_erase(uapres);
787 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000788 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000789 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000790
791 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000792}
793
794/* This is called when request is received.
795 * We need to check for incoming SUBSCRIBE request.
796 */
797static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
798{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000799 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000800 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000801 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000802 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
803 pjsua_srv_pres *uapres;
804 pjsip_evsub *sub;
805 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000806 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000807 pjsip_status_code st_code;
808 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000809 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000810 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000811 pj_status_t status;
812
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000813 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000814 return PJ_FALSE;
815
816 /* Incoming SUBSCRIBE: */
817
Benny Prijono384dab42009-10-14 01:58:04 +0000818 /* Don't want to accept the request if shutdown is in progress */
819 if (pjsua_var.thread_quit_flag) {
820 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
821 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
822 NULL, NULL);
823 return PJ_TRUE;
824 }
825
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000826 PJSUA_LOCK();
827
Benny Prijonoa91a0032006-02-26 21:23:45 +0000828 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000829 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000830 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000831
Benny Prijono6f979412006-06-15 12:25:46 +0000832 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
833 acc_id));
Benny Prijonob90fd382011-09-18 14:59:56 +0000834 pj_log_push_indent();
Benny Prijono6f979412006-06-15 12:25:46 +0000835
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000836 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000837 if (acc->contact.slen) {
838 contact = acc->contact;
839 } else {
840 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
841 acc_id, rdata);
842 if (status != PJ_SUCCESS) {
843 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
844 status);
845 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000846 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
847 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000848 pj_log_pop_indent();
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000849 return PJ_TRUE;
850 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000851 }
852
Benny Prijono834aee32006-02-19 01:38:06 +0000853 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000854 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000855 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000856 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000857 pjsua_perror(THIS_FILE,
858 "Unable to create UAS dialog for subscription",
859 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000860 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000861 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
862 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000863 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000864 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000865 }
866
Benny Prijono48ab2b72007-11-08 09:24:30 +0000867 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000868 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000869 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000870
Benny Prijono834aee32006-02-19 01:38:06 +0000871 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000872 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000873 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
874
875 /* Create server presence subscription: */
876 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
877 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000878 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
879 pjsip_tx_data *tdata;
880
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000881 pjsua_perror(THIS_FILE, "Unable to create server subscription",
882 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000883
884 if (code==599 || code > 699 || code < 300) {
885 code = 400;
886 }
887
888 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
889 if (status == PJ_SUCCESS) {
890 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
891 tdata);
892 }
893
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000895 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000896 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000897 }
898
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000899 /* If account is locked to specific transport, then lock dialog
900 * to this transport too.
901 */
902 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
903 pjsip_tpselector tp_sel;
904
905 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
906 pjsip_dlg_set_transport(dlg, &tp_sel);
907 }
908
Benny Prijono834aee32006-02-19 01:38:06 +0000909 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000910 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000911 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000912 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000913 uapres->acc_id = acc_id;
914 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000915 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
916 uapres->remote, PJSIP_MAX_URL_SIZE);
917 if (status < 1)
918 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
919 else
920 uapres->remote[status] = '\0';
921
Sauw Ming5c2f6da2011-02-11 07:39:14 +0000922 pjsip_evsub_add_header(sub, &acc->cfg.sub_hdr_list);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000923 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000924
925 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000926 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000927
928
Benny Prijono63fba012008-07-17 14:19:10 +0000929 /* Capture the value of Expires header. */
930 expires_hdr = (pjsip_expires_hdr*)
931 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
932 NULL);
933 if (expires_hdr)
934 uapres->expires = expires_hdr->ivalue;
935 else
936 uapres->expires = -1;
937
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000938 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000939 reason = pj_str("OK");
940 pjsua_msg_data_init(&msg_data);
941
942 /* Notify application callback, if any */
943 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
944 pjsua_buddy_id buddy_id;
945
Benny Prijono73bb7232009-10-20 13:56:26 +0000946 buddy_id = find_buddy(rdata->msg_info.from->uri);
Benny Prijono63fba012008-07-17 14:19:10 +0000947
948 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
949 &dlg->remote.info_str,
950 rdata, &st_code, &reason,
951 &msg_data);
952 }
953
954 /* Handle rejection case */
955 if (st_code >= 300) {
956 pjsip_tx_data *tdata;
957
958 /* Create response */
959 status = pjsip_dlg_create_response(dlg, rdata, st_code,
960 &reason, &tdata);
961 if (status != PJ_SUCCESS) {
962 pjsua_perror(THIS_FILE, "Error creating response", status);
963 pj_list_erase(uapres);
964 pjsip_pres_terminate(sub, PJ_FALSE);
965 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000966 pj_log_pop_indent();
Benny Prijono63fba012008-07-17 14:19:10 +0000967 return PJ_FALSE;
968 }
969
970 /* Add header list, if any */
971 pjsua_process_msg_data(tdata, &msg_data);
972
973 /* Send the response */
974 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
975 tdata);
976 if (status != PJ_SUCCESS) {
977 pjsua_perror(THIS_FILE, "Error sending response", status);
978 /* This is not fatal */
979 }
980
981 /* Terminate presence subscription */
982 pj_list_erase(uapres);
983 pjsip_pres_terminate(sub, PJ_FALSE);
984 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000985 pj_log_pop_indent();
Benny Prijono63fba012008-07-17 14:19:10 +0000986 return PJ_TRUE;
987 }
988
989 /* Create and send 2xx response to the SUBSCRIBE request: */
990 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000991 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000992 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
993 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000994 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000995 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000996 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000997 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000998 return PJ_FALSE;
999 }
1000
Benny Prijono63fba012008-07-17 14:19:10 +00001001 /* If code is 200, send NOTIFY now */
1002 if (st_code == 200) {
1003 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
1004 NULL, NULL, PJ_TRUE, &msg_data);
1005 }
1006
1007 /* Done: */
1008
1009 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001010 pj_log_pop_indent();
Benny Prijono63fba012008-07-17 14:19:10 +00001011 return PJ_TRUE;
1012}
1013
1014
1015/*
1016 * Send NOTIFY.
1017 */
1018PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
1019 pjsua_srv_pres *srv_pres,
1020 pjsip_evsub_state ev_state,
1021 const pj_str_t *state_str,
1022 const pj_str_t *reason,
1023 pj_bool_t with_body,
1024 const pjsua_msg_data *msg_data)
1025{
1026 pjsua_acc *acc;
1027 pjsip_pres_status pres_status;
1028 pjsua_buddy_id buddy_id;
1029 pjsip_tx_data *tdata;
1030 pj_status_t status;
1031
1032 /* Check parameters */
1033 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
1034
1035 /* Check that account ID is valid */
1036 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1037 PJ_EINVAL);
1038 /* Check that account is valid */
1039 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1040
Benny Prijonob90fd382011-09-18 14:59:56 +00001041 PJ_LOG(4,(THIS_FILE, "Acc %d: sending NOTIFY for srv_pres=0x%p..",
1042 acc_id, (int)(long)srv_pres));
1043 pj_log_push_indent();
1044
Benny Prijono63fba012008-07-17 14:19:10 +00001045 PJSUA_LOCK();
1046
1047 acc = &pjsua_var.acc[acc_id];
1048
1049 /* Check that the server presence subscription is still valid */
1050 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
1051 /* Subscription has been terminated */
1052 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001053 pj_log_pop_indent();
Benny Prijono63fba012008-07-17 14:19:10 +00001054 return PJ_EINVALIDOP;
1055 }
Benny Prijono834aee32006-02-19 01:38:06 +00001056
1057 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +00001058 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +00001059 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +00001060 pres_status.info[0].basic_open = acc->online_status;
1061 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001062 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +00001063 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001064 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +00001065 /* add RPID information */
1066 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1067 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +00001068
Benny Prijono63fba012008-07-17 14:19:10 +00001069 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +00001070
Benny Prijonoc61cc042007-06-27 13:01:59 +00001071 /* Check expires value. If it's zero, send our presense state but
1072 * set subscription state to TERMINATED.
1073 */
Benny Prijono63fba012008-07-17 14:19:10 +00001074 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +00001075 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +00001076
Benny Prijono63fba012008-07-17 14:19:10 +00001077 /* Create and send the NOTIFY to active subscription: */
1078 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
1079 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001080 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +00001081 /* Force removal of message body if msg_body==FALSE */
1082 if (!with_body) {
1083 tdata->msg->body = NULL;
1084 }
1085 pjsua_process_msg_data(tdata, msg_data);
1086 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001087 }
Benny Prijono834aee32006-02-19 01:38:06 +00001088
1089 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001090 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
1091 status);
Benny Prijono63fba012008-07-17 14:19:10 +00001092 pj_list_erase(srv_pres);
1093 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001094 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001095 pj_log_pop_indent();
Benny Prijono63fba012008-07-17 14:19:10 +00001096 return status;
Benny Prijono834aee32006-02-19 01:38:06 +00001097 }
1098
1099
Benny Prijonoa17496a2007-10-31 10:20:31 +00001100 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono73bb7232009-10-20 13:56:26 +00001101 buddy_id = find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +00001102 if (buddy_id != PJSUA_INVALID_ID) {
1103 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
1104 if (b->monitor && b->sub == NULL) {
1105 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
1106 "activating outgoing subscription", buddy_id));
1107 subscribe_buddy_presence(buddy_id);
1108 }
1109 }
1110
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001111 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00001112 pj_log_pop_indent();
Benny Prijono63fba012008-07-17 14:19:10 +00001113 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +00001114}
1115
1116
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001117/*
1118 * Client presence publication callback.
1119 */
1120static void publish_cb(struct pjsip_publishc_cbparam *param)
1121{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001122 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001123
1124 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
Benny Prijono53984d12009-04-28 22:19:49 +00001125
1126 pjsip_publishc_destroy(param->pubc);
1127 acc->publish_sess = NULL;
1128
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001129 if (param->status != PJ_SUCCESS) {
1130 char errmsg[PJ_ERR_MSG_SIZE];
1131
1132 pj_strerror(param->status, errmsg, sizeof(errmsg));
1133 PJ_LOG(1,(THIS_FILE,
1134 "Client publication (PUBLISH) failed, status=%d, msg=%s",
1135 param->status, errmsg));
Benny Prijono53984d12009-04-28 22:19:49 +00001136 } else if (param->code == 412) {
1137 /* 412 (Conditional Request Failed)
1138 * The PUBLISH refresh has failed, retry with new one.
1139 */
1140 pjsua_pres_init_publish_acc(acc->index);
1141
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001142 } else {
1143 PJ_LOG(1,(THIS_FILE,
1144 "Client publication (PUBLISH) failed (%d/%.*s)",
1145 param->code, (int)param->reason.slen,
1146 param->reason.ptr));
1147 }
1148
Benny Prijono53984d12009-04-28 22:19:49 +00001149 } else {
Benny Prijono534a9ba2009-10-13 14:01:59 +00001150 if (param->expiration < 1) {
Benny Prijono53984d12009-04-28 22:19:49 +00001151 /* Could happen if server "forgot" to include Expires header
1152 * in the response. We will not renew, so destroy the pubc.
1153 */
1154 pjsip_publishc_destroy(param->pubc);
1155 acc->publish_sess = NULL;
1156 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001157 }
1158}
1159
1160
1161/*
1162 * Send PUBLISH request.
1163 */
1164static pj_status_t send_publish(int acc_id, pj_bool_t active)
1165{
1166 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1167 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1168 pjsip_pres_status pres_status;
1169 pjsip_tx_data *tdata;
1170 pj_status_t status;
1171
Benny Prijonob90fd382011-09-18 14:59:56 +00001172 PJ_LOG(5,(THIS_FILE, "Acc %d: sending %sPUBLISH..",
1173 acc_id, (active ? "" : "un-")));
1174 pj_log_push_indent();
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001175
1176 /* Create PUBLISH request */
1177 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +00001178 char *bpos;
1179 pj_str_t entity;
1180
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001181 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1182 if (status != PJ_SUCCESS) {
1183 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1184 goto on_error;
1185 }
1186
1187 /* Set our online status: */
1188 pj_bzero(&pres_status, sizeof(pres_status));
1189 pres_status.info_cnt = 1;
1190 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001191 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001192 /* .. including RPID information */
1193 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1194 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001195
Benny Prijono8c6e8842007-02-24 15:33:54 +00001196 /* Be careful not to send PIDF with presence entity ID containing
1197 * "<" character.
1198 */
1199 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1200 char *epos = pj_strchr(&acc_cfg->id, '>');
1201 if (epos - bpos < 2) {
1202 pj_assert(!"Unexpected invalid URI");
1203 status = PJSIP_EINVALIDURI;
1204 goto on_error;
1205 }
1206 entity.ptr = bpos+1;
1207 entity.slen = epos - bpos - 1;
1208 } else {
1209 entity = acc_cfg->id;
1210 }
1211
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001212 /* Create and add PIDF message body */
1213 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001214 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001215 if (status != PJ_SUCCESS) {
1216 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1217 status);
1218 pjsip_tx_data_dec_ref(tdata);
1219 goto on_error;
1220 }
1221 } else {
1222 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1223 if (status != PJ_SUCCESS) {
1224 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1225 goto on_error;
1226 }
1227 }
1228
1229 /* Add headers etc */
1230 pjsua_process_msg_data(tdata, NULL);
1231
1232 /* Send the PUBLISH request */
1233 status = pjsip_publishc_send(acc->publish_sess, tdata);
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001234 if (status == PJ_EPENDING) {
1235 PJ_LOG(3,(THIS_FILE, "Previous request is in progress, "
1236 "PUBLISH request is queued"));
1237 } else if (status != PJ_SUCCESS) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001238 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1239 goto on_error;
1240 }
1241
1242 acc->publish_state = acc->online_status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001243 pj_log_pop_indent();
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001244 return PJ_SUCCESS;
1245
1246on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001247 if (acc->publish_sess) {
1248 pjsip_publishc_destroy(acc->publish_sess);
1249 acc->publish_sess = NULL;
1250 }
Benny Prijonob90fd382011-09-18 14:59:56 +00001251 pj_log_pop_indent();
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001252 return status;
1253}
1254
1255
1256/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001257pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001258{
1259 const pj_str_t STR_PRESENCE = { "presence", 8 };
1260 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1261 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1262 pj_status_t status;
1263
1264 /* Create and init client publication session */
1265 if (acc_cfg->publish_enabled) {
1266
1267 /* Create client publication */
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001268 status = pjsip_publishc_create(pjsua_var.endpt, &acc_cfg->publish_opt,
1269 acc, &publish_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001270 &acc->publish_sess);
1271 if (status != PJ_SUCCESS) {
1272 acc->publish_sess = NULL;
1273 return status;
1274 }
1275
1276 /* Initialize client publication */
1277 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1278 &acc_cfg->id, &acc_cfg->id,
1279 &acc_cfg->id,
Benny Prijono53984d12009-04-28 22:19:49 +00001280 PJSUA_PUBLISH_EXPIRATION);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001281 if (status != PJ_SUCCESS) {
1282 acc->publish_sess = NULL;
1283 return status;
1284 }
1285
Benny Prijono703b7d72007-03-20 09:13:24 +00001286 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001287 if (acc->cred_cnt) {
1288 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1289 acc->cred);
1290 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001291
1292 /* Set route-set */
1293 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1294
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001295 /* Send initial PUBLISH request */
1296 if (acc->online_status != 0) {
1297 status = send_publish(acc_id, PJ_TRUE);
1298 if (status != PJ_SUCCESS)
1299 return status;
1300 }
1301
1302 } else {
1303 acc->publish_sess = NULL;
1304 }
1305
1306 return PJ_SUCCESS;
1307}
1308
1309
1310/* Init presence for account */
1311pj_status_t pjsua_pres_init_acc(int acc_id)
1312{
1313 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1314
1315 /* Init presence subscription */
1316 pj_list_init(&acc->pres_srv_list);
1317
Benny Prijono8b6834f2007-02-24 13:29:22 +00001318 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001319}
1320
1321
Benny Prijono166d5022010-02-10 14:24:48 +00001322/* Unpublish presence publication */
1323void pjsua_pres_unpublish(pjsua_acc *acc)
1324{
1325 if (acc->publish_sess) {
1326 pjsua_acc_config *acc_cfg = &acc->cfg;
1327
1328 acc->online_status = PJ_FALSE;
1329 send_publish(acc->index, PJ_FALSE);
1330 /* By ticket #364, don't destroy the session yet (let the callback
1331 destroy it)
1332 if (acc->publish_sess) {
1333 pjsip_publishc_destroy(acc->publish_sess);
1334 acc->publish_sess = NULL;
1335 }
1336 */
1337 acc_cfg->publish_enabled = PJ_FALSE;
1338 }
1339}
1340
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341/* Terminate server subscription for the account */
1342void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001343{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001344 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijono834aee32006-02-19 01:38:06 +00001345 pjsua_srv_pres *uapres;
1346
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001347 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001348
Benny Prijono922933b2007-01-21 16:23:56 +00001349 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001350 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001351
1352 pjsip_pres_status pres_status;
1353 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001354 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355 pjsip_tx_data *tdata;
1356
Benny Prijono5516f912008-05-05 12:06:08 +00001357 next = uapres->next;
1358
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 pjsip_pres_get_status(uapres->sub, &pres_status);
1360
1361 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1362 pjsip_pres_set_status(uapres->sub, &pres_status);
1363
1364 if (pjsip_pres_notify(uapres->sub,
1365 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1366 &reason, &tdata)==PJ_SUCCESS)
1367 {
1368 pjsip_pres_send_request(uapres->sub, tdata);
1369 }
1370
Benny Prijono5516f912008-05-05 12:06:08 +00001371 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001372 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001373
Benny Prijono922933b2007-01-21 16:23:56 +00001374 /* Clear server presence subscription list because account might be reused
1375 * later. */
1376 pj_list_init(&acc->pres_srv_list);
1377
1378 /* Terminate presence publication, if any */
Benny Prijono166d5022010-02-10 14:24:48 +00001379 pjsua_pres_unpublish(acc);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001380}
1381
1382
Benny Prijono4461c7d2007-08-25 13:36:15 +00001383/* Update server subscription (e.g. when our online status has changed) */
1384void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001386 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1387 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001388 pjsua_srv_pres *uapres;
1389
1390 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1391
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001392 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001393
1394 pjsip_pres_status pres_status;
1395 pjsip_tx_data *tdata;
1396
1397 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001398
1399 /* Only send NOTIFY once subscription is active. Some subscriptions
1400 * may still be in NULL (when app is adding a new buddy while in the
1401 * on_incoming_subscribe() callback) or PENDING (when user approval is
1402 * being requested) state and we don't send NOTIFY to these subs until
1403 * the user accepted the request.
1404 */
1405 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1406 (force || pres_status.info[0].basic_open != acc->online_status))
1407 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001408
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001409 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001410 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1411 sizeof(pjrpid_element));
1412
Benny Prijono834aee32006-02-19 01:38:06 +00001413 pjsip_pres_set_status(uapres->sub, &pres_status);
1414
Benny Prijono21b9ad92006-08-15 13:11:22 +00001415 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1416 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001417 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001418 }
Benny Prijono834aee32006-02-19 01:38:06 +00001419 }
1420
1421 uapres = uapres->next;
1422 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001423
Benny Prijono8b6834f2007-02-24 13:29:22 +00001424 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1425 * session. If we don't have a PUBLISH session, then it could be
1426 * that we're waiting until registration has completed before we
1427 * send the first PUBLISH.
1428 */
1429 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001430 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001431 send_publish(acc_id, PJ_TRUE);
1432 }
1433 }
Benny Prijono834aee32006-02-19 01:38:06 +00001434}
1435
1436
1437
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438/***************************************************************************
1439 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001440 */
1441
Benny Prijono73bb7232009-10-20 13:56:26 +00001442static void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
1443{
1444 pjsua_buddy *buddy = (pjsua_buddy*)entry->user_data;
1445
1446 PJ_UNUSED_ARG(th);
1447
1448 entry->id = PJ_FALSE;
1449 pjsua_buddy_update_pres(buddy->index);
1450}
1451
1452/* Reschedule subscription refresh timer or terminate the subscription
1453 * refresh timer for the specified buddy.
1454 */
1455static void buddy_resubscribe(pjsua_buddy *buddy, pj_bool_t resched,
1456 unsigned msec_interval)
1457{
1458 if (buddy->timer.id) {
1459 pjsua_cancel_timer(&buddy->timer);
1460 buddy->timer.id = PJ_FALSE;
1461 }
1462
1463 if (resched) {
1464 pj_time_val delay;
1465
Benny Prijono12c01a92009-10-21 02:37:52 +00001466 PJ_LOG(4,(THIS_FILE,
1467 "Resubscribing buddy id %u in %u ms (reason: %.*s)",
1468 buddy->index, msec_interval,
1469 (int)buddy->term_reason.slen,
1470 buddy->term_reason.ptr));
1471
Benny Prijono73bb7232009-10-20 13:56:26 +00001472 pj_timer_entry_init(&buddy->timer, 0, buddy, &buddy_timer_cb);
1473 delay.sec = 0;
1474 delay.msec = msec_interval;
1475 pj_time_val_normalize(&delay);
1476
1477 if (pjsua_schedule_timer(&buddy->timer, &delay)==PJ_SUCCESS)
1478 buddy->timer.id = PJ_TRUE;
1479 }
1480}
1481
Benny Prijono834aee32006-02-19 01:38:06 +00001482/* Callback called when *client* subscription state has changed. */
1483static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1484{
1485 pjsua_buddy *buddy;
1486
1487 PJ_UNUSED_ARG(event);
1488
Benny Prijono73bb7232009-10-20 13:56:26 +00001489 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1490 * a dialog attached to it, lock_buddy() will use the dialog
1491 * lock, which we are currently holding!
1492 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001493 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001494 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001495 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001496 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001497 (int)pjsua_var.buddy[buddy->index].uri.slen,
1498 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001499 pjsip_evsub_get_state_name(sub)));
Benny Prijonob90fd382011-09-18 14:59:56 +00001500 pj_log_push_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001501
1502 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001503 int resub_delay = -1;
1504
Benny Prijono63fba012008-07-17 14:19:10 +00001505 if (buddy->term_reason.ptr == NULL) {
1506 buddy->term_reason.ptr = (char*)
1507 pj_pool_alloc(buddy->pool,
1508 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1509 }
1510 pj_strncpy(&buddy->term_reason,
1511 pjsip_evsub_get_termination_reason(sub),
1512 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
Benny Prijono73bb7232009-10-20 13:56:26 +00001513
1514 buddy->term_code = 200;
1515
1516 /* Determine whether to resubscribe automatically */
Benny Prijonod3d18c32009-11-10 10:54:11 +00001517 if (event && event->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001518 const pjsip_transaction *tsx = event->body.tsx_state.tsx;
1519 if (pjsip_method_cmp(&tsx->method,
1520 &pjsip_subscribe_method)==0)
1521 {
1522 buddy->term_code = tsx->status_code;
1523 switch (tsx->status_code) {
1524 case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST:
1525 /* 481: we refreshed too late? resubscribe
1526 * immediately.
1527 */
Benny Prijono6ab05322009-10-21 03:03:06 +00001528 /* But this must only happen when the 481 is received
1529 * on subscription refresh request. We MUST NOT try to
1530 * resubscribe automatically if the 481 is received
1531 * on the initial SUBSCRIBE (if server returns this
1532 * response for some reason).
1533 */
1534 if (buddy->dlg->remote.contact)
1535 resub_delay = 500;
Benny Prijono73bb7232009-10-20 13:56:26 +00001536 break;
1537 }
1538 } else if (pjsip_method_cmp(&tsx->method,
1539 &pjsip_notify_method)==0)
1540 {
1541 if (pj_stricmp2(&buddy->term_reason, "deactivated")==0 ||
1542 pj_stricmp2(&buddy->term_reason, "timeout")==0) {
1543 /* deactivated: The subscription has been terminated,
1544 * but the subscriber SHOULD retry immediately with
1545 * a new subscription.
1546 */
1547 /* timeout: The subscription has been terminated
1548 * because it was not refreshed before it expired.
1549 * Clients MAY re-subscribe immediately. The
1550 * "retry-after" parameter has no semantics for
1551 * "timeout".
1552 */
1553 resub_delay = 500;
1554 }
1555 else if (pj_stricmp2(&buddy->term_reason, "probation")==0||
1556 pj_stricmp2(&buddy->term_reason, "giveup")==0) {
1557 /* probation: The subscription has been terminated,
1558 * but the client SHOULD retry at some later time.
1559 * If a "retry-after" parameter is also present, the
1560 * client SHOULD wait at least the number of seconds
1561 * specified by that parameter before attempting to re-
1562 * subscribe.
1563 */
1564 /* giveup: The subscription has been terminated because
1565 * the notifier could not obtain authorization in a
1566 * timely fashion. If a "retry-after" parameter is
1567 * also present, the client SHOULD wait at least the
1568 * number of seconds specified by that parameter before
1569 * attempting to re-subscribe; otherwise, the client
1570 * MAY retry immediately, but will likely get put back
1571 * into pending state.
1572 */
1573 const pjsip_sub_state_hdr *sub_hdr;
1574 pj_str_t sub_state = { "Subscription-State", 18 };
1575 const pjsip_msg *msg;
1576
1577 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1578 sub_hdr = (const pjsip_sub_state_hdr*)
1579 pjsip_msg_find_hdr_by_name(msg, &sub_state,
1580 NULL);
1581 if (sub_hdr && sub_hdr->retry_after > 0)
1582 resub_delay = sub_hdr->retry_after * 1000;
1583 }
1584
1585 }
1586 }
1587
1588 /* For other cases of subscription termination, if resubscribe
1589 * timer is not set, schedule with default expiration (plus minus
1590 * some random value, to avoid sending SUBSCRIBEs all at once)
1591 */
1592 if (resub_delay == -1) {
1593 pj_assert(PJSUA_PRES_TIMER >= 3);
1594 resub_delay = PJSUA_PRES_TIMER*1000 - 2500 + (pj_rand()%5000);
1595 }
1596
1597 buddy_resubscribe(buddy, PJ_TRUE, resub_delay);
Benny Prijono6ab05322009-10-21 03:03:06 +00001598
Benny Prijono63fba012008-07-17 14:19:10 +00001599 } else {
Benny Prijono6ab05322009-10-21 03:03:06 +00001600 /* This will clear the last termination code/reason */
Benny Prijono73bb7232009-10-20 13:56:26 +00001601 buddy->term_code = 0;
Benny Prijono63fba012008-07-17 14:19:10 +00001602 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001603 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001604
Benny Prijono63499892010-10-12 12:45:15 +00001605 /* Call callbacks */
1606 if (pjsua_var.ua_cfg.cb.on_buddy_evsub_state)
1607 (*pjsua_var.ua_cfg.cb.on_buddy_evsub_state)(buddy->index, sub,
1608 event);
1609
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1611 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001612
1613 /* Clear subscription */
1614 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1615 buddy->sub = NULL;
1616 buddy->status.info_cnt = 0;
Benny Prijono73bb7232009-10-20 13:56:26 +00001617 buddy->dlg = NULL;
Benny Prijono63fba012008-07-17 14:19:10 +00001618 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1619 }
Benny Prijonob90fd382011-09-18 14:59:56 +00001620
1621 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001622 }
1623}
1624
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625
1626/* Callback when transaction state has changed. */
1627static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1628 pjsip_transaction *tsx,
1629 pjsip_event *event)
1630{
1631 pjsua_buddy *buddy;
1632 pjsip_contact_hdr *contact_hdr;
1633
Benny Prijono73bb7232009-10-20 13:56:26 +00001634 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1635 * a dialog attached to it, lock_buddy() will use the dialog
1636 * lock, which we are currently holding!
1637 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001638 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001639 if (!buddy) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640 return;
1641 }
1642
1643 /* We only use this to update buddy's Contact, when it's not
1644 * set.
1645 */
1646 if (buddy->contact.slen != 0) {
1647 /* Contact already set */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001648 return;
1649 }
1650
1651 /* Only care about 2xx response to outgoing SUBSCRIBE */
1652 if (tsx->status_code/100 != 2 ||
1653 tsx->role != PJSIP_UAC_ROLE ||
1654 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001655 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001657 return;
1658 }
1659
1660 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001661 contact_hdr = (pjsip_contact_hdr*)
1662 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 PJSIP_H_CONTACT, NULL);
Benny Prijono8b33bba2010-06-02 03:03:43 +00001664 if (!contact_hdr || !contact_hdr->uri) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001665 return;
1666 }
1667
Benny Prijonoa1e69682007-05-11 15:14:34 +00001668 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001669 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001670 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1671 contact_hdr->uri,
1672 buddy->contact.ptr,
1673 PJSIP_MAX_URL_SIZE);
1674 if (buddy->contact.slen < 0)
1675 buddy->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001676}
1677
1678
Benny Prijono834aee32006-02-19 01:38:06 +00001679/* Callback called when we receive NOTIFY */
1680static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1681 pjsip_rx_data *rdata,
1682 int *p_st_code,
1683 pj_str_t **p_st_text,
1684 pjsip_hdr *res_hdr,
1685 pjsip_msg_body **p_body)
1686{
1687 pjsua_buddy *buddy;
1688
Benny Prijono73bb7232009-10-20 13:56:26 +00001689 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1690 * a dialog attached to it, lock_buddy() will use the dialog
1691 * lock, which we are currently holding!
1692 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001693 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001694 if (buddy) {
1695 /* Update our info. */
1696 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001697 }
1698
1699 /* The default is to send 200 response to NOTIFY.
1700 * Just leave it there..
1701 */
1702 PJ_UNUSED_ARG(rdata);
1703 PJ_UNUSED_ARG(p_st_code);
1704 PJ_UNUSED_ARG(p_st_text);
1705 PJ_UNUSED_ARG(res_hdr);
1706 PJ_UNUSED_ARG(p_body);
1707}
1708
1709
Benny Prijono834aee32006-02-19 01:38:06 +00001710/* It does what it says.. */
Benny Prijono73bb7232009-10-20 13:56:26 +00001711static void subscribe_buddy_presence(pjsua_buddy_id buddy_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001712{
Benny Prijono63499892010-10-12 12:45:15 +00001713 pjsip_evsub_user pres_callback;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001714 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001715 pjsua_buddy *buddy;
1716 int acc_id;
1717 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001718 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001719 pjsip_tx_data *tdata;
1720 pj_status_t status;
1721
Benny Prijono63499892010-10-12 12:45:15 +00001722 /* Event subscription callback. */
1723 pj_bzero(&pres_callback, sizeof(pres_callback));
1724 pres_callback.on_evsub_state = &pjsua_evsub_on_state;
1725 pres_callback.on_tsx_state = &pjsua_evsub_on_tsx_state;
1726 pres_callback.on_rx_notify = &pjsua_evsub_on_rx_notify;
1727
Benny Prijono73bb7232009-10-20 13:56:26 +00001728 buddy = &pjsua_var.buddy[buddy_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001730
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001731 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001732
Benny Prijonob90fd382011-09-18 14:59:56 +00001733 PJ_LOG(4,(THIS_FILE, "Buddy %d: subscribing presence,using account %d..",
1734 buddy_id, acc_id));
1735 pj_log_push_indent();
Benny Prijonob4a17c92006-07-10 14:40:21 +00001736
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001737 /* Generate suitable Contact header unless one is already set in
1738 * the account
1739 */
1740 if (acc->contact.slen) {
1741 contact = acc->contact;
1742 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001743 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1744
1745 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001746 acc_id, &buddy->uri);
1747 if (status != PJ_SUCCESS) {
1748 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1749 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001750 pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00001751 pj_log_pop_indent();
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001752 return;
1753 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001754 }
1755
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001756 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001757 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001758 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001759 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001760 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001761 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001762 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001763 pjsua_perror(THIS_FILE, "Unable to create dialog",
1764 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001765 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00001766 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001767 return;
1768 }
1769
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001770 /* Increment the dialog's lock otherwise when presence session creation
1771 * fails the dialog will be destroyed prematurely.
1772 */
1773 pjsip_dlg_inc_lock(buddy->dlg);
1774
Benny Prijonof9c40c32007-06-28 07:20:26 +00001775 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001776 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1777 if (status != PJ_SUCCESS) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001778 buddy->sub = NULL;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001779 pjsua_perror(THIS_FILE, "Unable to create presence client",
1780 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001781 /* This should destroy the dialog since there's no session
1782 * referencing it
1783 */
Benny Prijono011e3f22009-12-10 05:16:23 +00001784 if (buddy->dlg) pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001785 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00001786 pj_log_pop_indent();
Benny Prijonodc752ca2006-09-22 16:55:42 +00001787 return;
1788 }
1789
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001790 /* If account is locked to specific transport, then lock dialog
1791 * to this transport too.
1792 */
1793 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1794 pjsip_tpselector tp_sel;
1795
1796 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001797 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001798 }
1799
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001800 /* Set route-set */
1801 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001802 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001803 }
1804
1805 /* Set credentials */
1806 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001807 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001808 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001809 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001810
Benny Prijono48ab2b72007-11-08 09:24:30 +00001811 /* Set authentication preference */
1812 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1813
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001814 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001815
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001816 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001817 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00001818 if (buddy->dlg) pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001819 if (buddy->sub) {
1820 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1821 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001822 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001823 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1824 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001825 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00001826 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001827 return;
1828 }
1829
Benny Prijono21b9ad92006-08-15 13:11:22 +00001830 pjsua_process_msg_data(tdata, NULL);
1831
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001832 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001833 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00001834 if (buddy->dlg) pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001835 if (buddy->sub) {
1836 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1837 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001838 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001839 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1840 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001841 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00001842 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001843 return;
1844 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001845
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001846 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001847 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00001848 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001849}
1850
1851
1852/* It does what it says... */
Benny Prijono73bb7232009-10-20 13:56:26 +00001853static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001854{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001855 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001856 pjsip_tx_data *tdata;
1857 pj_status_t status;
1858
Benny Prijono73bb7232009-10-20 13:56:26 +00001859 buddy = &pjsua_var.buddy[buddy_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860
1861 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001862 return;
1863
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001864 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001865 buddy->sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001866 return;
1867 }
1868
Benny Prijonob90fd382011-09-18 14:59:56 +00001869 PJ_LOG(5,(THIS_FILE, "Buddy %d: unsubscribing..", buddy_id));
1870 pj_log_push_indent();
1871
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001872 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001873 if (status == PJ_SUCCESS) {
1874 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001875 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001876 }
Benny Prijono834aee32006-02-19 01:38:06 +00001877
Benny Prijono48da92e2007-05-16 08:56:30 +00001878 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001879 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1880 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001881 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1882 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001883 }
Benny Prijonob90fd382011-09-18 14:59:56 +00001884
1885 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00001886}
1887
Benny Prijono834aee32006-02-19 01:38:06 +00001888/* It does what it says.. */
Benny Prijono73bb7232009-10-20 13:56:26 +00001889static pj_status_t refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001890{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001891 unsigned i;
Benny Prijono73bb7232009-10-20 13:56:26 +00001892 pj_status_t status;
Benny Prijonof9c40c32007-06-28 07:20:26 +00001893
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001894 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001895 struct buddy_lock lck;
Benny Prijono834aee32006-02-19 01:38:06 +00001896
Benny Prijono73bb7232009-10-20 13:56:26 +00001897 if (!pjsua_buddy_is_valid(i))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001898 continue;
1899
Benny Prijono73bb7232009-10-20 13:56:26 +00001900 status = lock_buddy("refresh_client_subscriptions()", i, &lck, 0);
1901 if (status != PJ_SUCCESS)
1902 return status;
1903
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001904 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001905 subscribe_buddy_presence(i);
1906
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001907 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001908 unsubscribe_buddy_presence(i);
1909
1910 }
Benny Prijono73bb7232009-10-20 13:56:26 +00001911
1912 unlock_buddy(&lck);
Benny Prijono834aee32006-02-19 01:38:06 +00001913 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001914
Benny Prijono73bb7232009-10-20 13:56:26 +00001915 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +00001916}
1917
Benny Prijono4dd961b2009-10-26 11:21:37 +00001918/***************************************************************************
1919 * MWI
1920 */
1921/* Callback called when *client* subscription state has changed. */
1922static void mwi_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1923{
1924 pjsua_acc *acc;
1925
1926 PJ_UNUSED_ARG(event);
1927
1928 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1929 * a dialog attached to it, lock_buddy() will use the dialog
1930 * lock, which we are currently holding!
1931 */
1932 acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
1933 if (!acc)
1934 return;
1935
1936 PJ_LOG(4,(THIS_FILE,
1937 "MWI subscription for %.*s is %s",
1938 (int)acc->cfg.id.slen, acc->cfg.id.ptr,
1939 pjsip_evsub_get_state_name(sub)));
1940
1941 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1942 /* Clear subscription */
1943 acc->mwi_dlg = NULL;
1944 acc->mwi_sub = NULL;
1945 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1946
1947 }
1948}
1949
1950/* Callback called when we receive NOTIFY */
1951static void mwi_evsub_on_rx_notify(pjsip_evsub *sub,
1952 pjsip_rx_data *rdata,
1953 int *p_st_code,
1954 pj_str_t **p_st_text,
1955 pjsip_hdr *res_hdr,
1956 pjsip_msg_body **p_body)
1957{
1958 pjsua_mwi_info mwi_info;
1959 pjsua_acc *acc;
1960
1961 PJ_UNUSED_ARG(p_st_code);
1962 PJ_UNUSED_ARG(p_st_text);
1963 PJ_UNUSED_ARG(res_hdr);
1964 PJ_UNUSED_ARG(p_body);
1965
1966 acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
1967 if (!acc)
1968 return;
1969
1970 /* Construct mwi_info */
1971 pj_bzero(&mwi_info, sizeof(mwi_info));
1972 mwi_info.evsub = sub;
1973 mwi_info.rdata = rdata;
1974
Benny Prijonob90fd382011-09-18 14:59:56 +00001975 PJ_LOG(4,(THIS_FILE, "MWI got NOTIFY.."));
1976 pj_log_push_indent();
1977
Benny Prijono4dd961b2009-10-26 11:21:37 +00001978 /* Call callback */
1979 if (pjsua_var.ua_cfg.cb.on_mwi_info) {
1980 (*pjsua_var.ua_cfg.cb.on_mwi_info)(acc->index, &mwi_info);
1981 }
Benny Prijonob90fd382011-09-18 14:59:56 +00001982
1983 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00001984}
1985
1986
1987/* Event subscription callback. */
1988static pjsip_evsub_user mwi_cb =
1989{
1990 &mwi_evsub_on_state,
1991 NULL, /* on_tsx_state: not interested */
1992 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1993 * we want to authenticate
1994 */
1995
1996 &mwi_evsub_on_rx_notify,
1997
1998 NULL, /* on_client_refresh: Use default behaviour, which is to
1999 * refresh client subscription. */
2000
2001 NULL, /* on_server_timeout: Use default behaviour, which is to send
2002 * NOTIFY to terminate.
2003 */
2004};
2005
2006void pjsua_start_mwi(pjsua_acc *acc)
2007{
2008 pj_pool_t *tmp_pool = NULL;
2009 pj_str_t contact;
2010 pjsip_tx_data *tdata;
2011 pj_status_t status;
2012
2013 if (!acc->cfg.mwi_enabled) {
2014 if (acc->mwi_sub) {
2015 /* Terminate MWI subscription */
2016 pjsip_tx_data *tdata;
2017 pjsip_evsub *sub = acc->mwi_sub;
2018
2019 /* Detach sub from this account */
2020 acc->mwi_sub = NULL;
2021 acc->mwi_dlg = NULL;
2022 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2023
2024 /* Unsubscribe */
2025 status = pjsip_mwi_initiate(acc->mwi_sub, 0, &tdata);
2026 if (status == PJ_SUCCESS) {
2027 status = pjsip_mwi_send_request(acc->mwi_sub, tdata);
2028 }
2029 }
2030 return;
2031 }
2032
2033 if (acc->mwi_sub) {
2034 /* Subscription is already active */
2035 return;
2036
2037 }
2038
Benny Prijonob90fd382011-09-18 14:59:56 +00002039 PJ_LOG(4,(THIS_FILE, "Starting MWI subscription.."));
2040 pj_log_push_indent();
2041
Benny Prijono4dd961b2009-10-26 11:21:37 +00002042 /* Generate suitable Contact header unless one is already set in
2043 * the account
2044 */
2045 if (acc->contact.slen) {
2046 contact = acc->contact;
2047 } else {
2048 tmp_pool = pjsua_pool_create("tmpmwi", 512, 256);
2049 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
2050 acc->index, &acc->cfg.id);
2051 if (status != PJ_SUCCESS) {
2052 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
2053 status);
2054 pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00002055 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00002056 return;
2057 }
2058 }
2059
2060 /* Create UAC dialog */
2061 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
2062 &acc->cfg.id,
2063 &contact,
2064 &acc->cfg.id,
2065 NULL, &acc->mwi_dlg);
2066 if (status != PJ_SUCCESS) {
2067 pjsua_perror(THIS_FILE, "Unable to create dialog", status);
2068 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00002069 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00002070 return;
2071 }
2072
2073 /* Increment the dialog's lock otherwise when presence session creation
2074 * fails the dialog will be destroyed prematurely.
2075 */
2076 pjsip_dlg_inc_lock(acc->mwi_dlg);
2077
2078 /* Create UAC subscription */
2079 status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb,
2080 PJSIP_EVSUB_NO_EVENT_ID, &acc->mwi_sub);
2081 if (status != PJ_SUCCESS) {
2082 pjsua_perror(THIS_FILE, "Error creating MWI subscription", status);
2083 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono011e3f22009-12-10 05:16:23 +00002084 if (acc->mwi_dlg) pjsip_dlg_dec_lock(acc->mwi_dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002085 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00002086 return;
2087 }
2088
2089 /* If account is locked to specific transport, then lock dialog
2090 * to this transport too.
2091 */
2092 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
2093 pjsip_tpselector tp_sel;
2094
2095 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
2096 pjsip_dlg_set_transport(acc->mwi_dlg, &tp_sel);
2097 }
2098
2099 /* Set route-set */
2100 if (!pj_list_empty(&acc->route_set)) {
2101 pjsip_dlg_set_route_set(acc->mwi_dlg, &acc->route_set);
2102 }
2103
2104 /* Set credentials */
2105 if (acc->cred_cnt) {
2106 pjsip_auth_clt_set_credentials( &acc->mwi_dlg->auth_sess,
2107 acc->cred_cnt, acc->cred);
2108 }
2109
2110 /* Set authentication preference */
2111 pjsip_auth_clt_set_prefs(&acc->mwi_dlg->auth_sess, &acc->cfg.auth_pref);
2112
2113 pjsip_evsub_set_mod_data(acc->mwi_sub, pjsua_var.mod.id, acc);
2114
2115 status = pjsip_mwi_initiate(acc->mwi_sub, -1, &tdata);
2116 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00002117 if (acc->mwi_dlg) pjsip_dlg_dec_lock(acc->mwi_dlg);
Benny Prijono4dd961b2009-10-26 11:21:37 +00002118 if (acc->mwi_sub) {
2119 pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
2120 }
2121 acc->mwi_sub = NULL;
2122 acc->mwi_dlg = NULL;
2123 pjsua_perror(THIS_FILE, "Unable to create initial MWI SUBSCRIBE",
2124 status);
2125 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00002126 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00002127 return;
2128 }
2129
2130 pjsua_process_msg_data(tdata, NULL);
2131
2132 status = pjsip_pres_send_request(acc->mwi_sub, tdata);
2133 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00002134 if (acc->mwi_dlg) pjsip_dlg_dec_lock(acc->mwi_dlg);
Benny Prijono4dd961b2009-10-26 11:21:37 +00002135 if (acc->mwi_sub) {
2136 pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
2137 }
2138 acc->mwi_sub = NULL;
2139 acc->mwi_dlg = NULL;
2140 pjsua_perror(THIS_FILE, "Unable to send initial MWI SUBSCRIBE",
2141 status);
2142 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonob90fd382011-09-18 14:59:56 +00002143 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00002144 return;
2145 }
2146
2147 pjsip_dlg_dec_lock(acc->mwi_dlg);
2148 if (tmp_pool) pj_pool_release(tmp_pool);
2149
Benny Prijonob90fd382011-09-18 14:59:56 +00002150 pj_log_pop_indent();
Benny Prijono4dd961b2009-10-26 11:21:37 +00002151}
2152
2153
Benny Prijonofe1bd342009-11-20 23:33:07 +00002154/***************************************************************************
2155 * Unsolicited MWI
2156 */
2157static pj_bool_t unsolicited_mwi_on_rx_request(pjsip_rx_data *rdata)
2158{
2159 pjsip_msg *msg = rdata->msg_info.msg;
2160 pj_str_t EVENT_HDR = { "Event", 5 };
2161 pj_str_t MWI = { "message-summary", 15 };
2162 pjsip_event_hdr *eh;
2163
2164 if (pjsip_method_cmp(&msg->line.req.method, &pjsip_notify_method)!=0) {
2165 /* Only interested with NOTIFY request */
2166 return PJ_FALSE;
2167 }
2168
2169 eh = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(msg, &EVENT_HDR, NULL);
2170 if (!eh) {
2171 /* Something wrong with the request, it has no Event hdr */
2172 return PJ_FALSE;
2173 }
2174
2175 if (pj_stricmp(&eh->event_type, &MWI) != 0) {
2176 /* Not MWI event */
2177 return PJ_FALSE;
2178 }
2179
Benny Prijonob90fd382011-09-18 14:59:56 +00002180 PJ_LOG(4,(THIS_FILE, "Got unsolicited NOTIFY from %s:%d..",
2181 rdata->pkt_info.src_name, rdata->pkt_info.src_port));
2182 pj_log_push_indent();
2183
Benny Prijonofe1bd342009-11-20 23:33:07 +00002184 /* Got unsolicited MWI request, respond with 200/OK first */
2185 pjsip_endpt_respond(pjsua_get_pjsip_endpt(), NULL, rdata, 200, NULL,
2186 NULL, NULL, NULL);
2187
2188
2189 /* Call callback */
2190 if (pjsua_var.ua_cfg.cb.on_mwi_info) {
2191 pjsua_acc_id acc_id;
2192 pjsua_mwi_info mwi_info;
2193
2194 acc_id = pjsua_acc_find_for_incoming(rdata);
2195
2196 pj_bzero(&mwi_info, sizeof(mwi_info));
2197 mwi_info.rdata = rdata;
2198
2199 (*pjsua_var.ua_cfg.cb.on_mwi_info)(acc_id, &mwi_info);
2200 }
2201
Benny Prijonob90fd382011-09-18 14:59:56 +00002202 pj_log_pop_indent();
Benny Prijonofe1bd342009-11-20 23:33:07 +00002203 return PJ_TRUE;
2204}
2205
2206/* The module instance. */
2207static pjsip_module pjsua_unsolicited_mwi_mod =
2208{
2209 NULL, NULL, /* prev, next. */
2210 { "mod-unsolicited-mwi", 19 }, /* Name. */
2211 -1, /* Id */
2212 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
2213 NULL, /* load() */
2214 NULL, /* start() */
2215 NULL, /* stop() */
2216 NULL, /* unload() */
2217 &unsolicited_mwi_on_rx_request, /* on_rx_request() */
2218 NULL, /* on_rx_response() */
2219 NULL, /* on_tx_request. */
2220 NULL, /* on_tx_response() */
2221 NULL, /* on_tsx_state() */
2222};
2223
2224static pj_status_t enable_unsolicited_mwi(void)
2225{
2226 pj_status_t status;
2227
2228 status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
2229 &pjsua_unsolicited_mwi_mod);
2230 if (status != PJ_SUCCESS)
2231 pjsua_perror(THIS_FILE, "Error registering unsolicited MWI module",
2232 status);
2233
2234 return status;
2235}
2236
2237
2238
Benny Prijono4dd961b2009-10-26 11:21:37 +00002239/***************************************************************************/
2240
Benny Prijono7a5f5102007-05-29 00:33:09 +00002241/* Timer callback to re-create client subscription */
2242static void pres_timer_cb(pj_timer_heap_t *th,
2243 pj_timer_entry *entry)
2244{
Benny Prijono53984d12009-04-28 22:19:49 +00002245 unsigned i;
Benny Prijono7a5f5102007-05-29 00:33:09 +00002246 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
2247
Benny Prijono4dd961b2009-10-26 11:21:37 +00002248 entry->id = PJ_FALSE;
2249
2250 /* Retry failed PUBLISH and MWI SUBSCRIBE requests */
Benny Prijono53984d12009-04-28 22:19:49 +00002251 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2252 pjsua_acc *acc = &pjsua_var.acc[i];
Benny Prijono4dd961b2009-10-26 11:21:37 +00002253
2254 /* Retry PUBLISH */
Benny Prijono53984d12009-04-28 22:19:49 +00002255 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
2256 pjsua_pres_init_publish_acc(acc->index);
Benny Prijono53984d12009-04-28 22:19:49 +00002257
Benny Prijono4dd961b2009-10-26 11:21:37 +00002258 /* Re-subscribe MWI subscription if it's terminated prematurely */
2259 if (acc->cfg.mwi_enabled && !acc->mwi_sub)
2260 pjsua_start_mwi(acc);
2261 }
Benny Prijono73bb7232009-10-20 13:56:26 +00002262
2263 /* #937: No need to do bulk client refresh, as buddies have their
2264 * own individual timer now.
2265 */
2266 //refresh_client_subscriptions();
Benny Prijono7a5f5102007-05-29 00:33:09 +00002267
2268 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
2269 entry->id = PJ_TRUE;
2270
Benny Prijonof9c40c32007-06-28 07:20:26 +00002271 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00002272}
2273
Benny Prijono834aee32006-02-19 01:38:06 +00002274
2275/*
2276 * Init presence
2277 */
2278pj_status_t pjsua_pres_init()
2279{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002280 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00002281 pj_status_t status;
2282
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002283 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00002284 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00002285 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
2286 status);
Benny Prijono834aee32006-02-19 01:38:06 +00002287 }
2288
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002289 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
2290 reset_buddy(i);
2291 }
2292
Benny Prijono834aee32006-02-19 01:38:06 +00002293 return status;
2294}
2295
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002296
Benny Prijono834aee32006-02-19 01:38:06 +00002297/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002298 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00002299 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002300pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00002301{
Benny Prijono7a5f5102007-05-29 00:33:09 +00002302 /* Start presence timer to re-subscribe to buddy's presence when
2303 * subscription has failed.
2304 */
2305 if (pjsua_var.pres_timer.id == PJ_FALSE) {
2306 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
2307
2308 pjsua_var.pres_timer.cb = &pres_timer_cb;
2309 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
2310 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00002311 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00002312 }
2313
Benny Prijonofe1bd342009-11-20 23:33:07 +00002314 if (pjsua_var.ua_cfg.enable_unsolicited_mwi) {
2315 pj_status_t status = enable_unsolicited_mwi();
2316 if (status != PJ_SUCCESS)
2317 return status;
2318 }
2319
Benny Prijono9fc735d2006-05-28 14:58:12 +00002320 return PJ_SUCCESS;
2321}
2322
2323
2324/*
Benny Prijono834aee32006-02-19 01:38:06 +00002325 * Shutdown presence.
2326 */
2327void pjsua_pres_shutdown(void)
2328{
Benny Prijono9fc735d2006-05-28 14:58:12 +00002329 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00002330
Benny Prijono384dab42009-10-14 01:58:04 +00002331 PJ_LOG(4,(THIS_FILE, "Shutting down presence.."));
Benny Prijonob90fd382011-09-18 14:59:56 +00002332 pj_log_push_indent();
Benny Prijono384dab42009-10-14 01:58:04 +00002333
Benny Prijono7a5f5102007-05-29 00:33:09 +00002334 if (pjsua_var.pres_timer.id != 0) {
2335 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
2336 pjsua_var.pres_timer.id = PJ_FALSE;
2337 }
2338
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002339 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2340 if (!pjsua_var.acc[i].valid)
2341 continue;
2342 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002343 }
2344
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002345 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
2346 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00002347 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00002348
Benny Prijono73bb7232009-10-20 13:56:26 +00002349 refresh_client_subscriptions();
2350
2351 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2352 if (pjsua_var.acc[i].valid)
2353 pjsua_pres_update_acc(i, PJ_FALSE);
2354 }
Benny Prijonob90fd382011-09-18 14:59:56 +00002355
2356 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +00002357}