blob: 2428a2f59314a3357456147b2ad2269554767d53 [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
392 PJSUA_LOCK();
393
394 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000395 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000396 if (pjsua_var.buddy[index].uri.slen == 0)
397 break;
398 }
399
400 /* Expect to find an empty slot */
401 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
402 PJSUA_UNLOCK();
403 /* This shouldn't happen */
404 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
405 return PJ_ETOOMANY;
406 }
407
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000408 buddy = &pjsua_var.buddy[index];
409
410 /* Create pool for this buddy */
411 if (buddy->pool) {
412 pj_pool_reset(buddy->pool);
413 } else {
414 char name[PJ_MAX_OBJ_NAME];
415 pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
416 buddy->pool = pjsua_pool_create(name, 512, 256);
417 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418
Benny Prijono63fba012008-07-17 14:19:10 +0000419 /* Init buffers for presence subscription status */
420 buddy->term_reason.ptr = (char*)
421 pj_pool_alloc(buddy->pool,
422 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
423
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 /* Get name and display name for buddy */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000425 pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
426 url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 PJSIP_PARSE_URI_AS_NAMEADDR);
428
429 if (url == NULL) {
430 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000431 pj_pool_release(buddy->pool);
432 buddy->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433 PJSUA_UNLOCK();
434 return PJSIP_EINVALIDURI;
435 }
436
Benny Prijonofc493592007-02-18 20:56:32 +0000437 /* Only support SIP schemes */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000438 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
439 pj_pool_release(buddy->pool);
440 buddy->pool = NULL;
441 PJSUA_UNLOCK();
Benny Prijonofc493592007-02-18 20:56:32 +0000442 return PJSIP_EINVALIDSCHEME;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000443 }
Benny Prijonofc493592007-02-18 20:56:32 +0000444
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 /* Reset buddy, to make sure everything is cleared with default
446 * values
447 */
448 reset_buddy(index);
449
450 /* Save URI */
451 pjsua_var.buddy[index].uri = tmp;
452
Benny Prijono9c1528f2007-02-10 19:22:25 +0000453 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000454 pjsua_var.buddy[index].name = sip_uri->user;
455 pjsua_var.buddy[index].display = url->display;
456 pjsua_var.buddy[index].host = sip_uri->host;
457 pjsua_var.buddy[index].port = sip_uri->port;
458 pjsua_var.buddy[index].monitor = cfg->subscribe;
459 if (pjsua_var.buddy[index].port == 0)
460 pjsua_var.buddy[index].port = 5060;
461
Benny Prijono705e7842008-07-21 18:12:51 +0000462 /* Save user data */
463 pjsua_var.buddy[index].user_data = (void*)cfg->user_data;
464
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 if (p_buddy_id)
466 *p_buddy_id = index;
467
468 pjsua_var.buddy_cnt++;
469
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000470 PJSUA_UNLOCK();
471
Benny Prijonof9c40c32007-06-28 07:20:26 +0000472 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
473
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000474 return PJ_SUCCESS;
475}
476
477
478/*
479 * Delete buddy.
480 */
481PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
482{
Benny Prijono73bb7232009-10-20 13:56:26 +0000483 struct buddy_lock lck;
484 pj_status_t status;
485
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000486 PJ_ASSERT_RETURN(buddy_id>=0 &&
487 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
488 PJ_EINVAL);
489
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000490 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000491 return PJ_SUCCESS;
492 }
493
Benny Prijono73bb7232009-10-20 13:56:26 +0000494 status = lock_buddy("pjsua_buddy_del()", buddy_id, &lck, 0);
495 if (status != PJ_SUCCESS)
496 return status;
497
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000498 /* Unsubscribe presence */
499 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
500
Benny Prijonoa5776cb2009-04-14 15:11:23 +0000501 /* Not interested with further events for this buddy */
502 if (pjsua_var.buddy[buddy_id].sub) {
503 pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub,
504 pjsua_var.mod.id, NULL);
505 }
506
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000507 /* Remove buddy */
508 pjsua_var.buddy[buddy_id].uri.slen = 0;
509 pjsua_var.buddy_cnt--;
510
Benny Prijono011e3f22009-12-10 05:16:23 +0000511 /* Clear timer */
512 if (pjsua_var.buddy[buddy_id].timer.id) {
513 pjsua_cancel_timer(&pjsua_var.buddy[buddy_id].timer);
514 pjsua_var.buddy[buddy_id].timer.id = PJ_FALSE;
515 }
516
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000517 /* Reset buddy struct */
518 reset_buddy(buddy_id);
519
Benny Prijono73bb7232009-10-20 13:56:26 +0000520 unlock_buddy(&lck);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 return PJ_SUCCESS;
522}
523
524
525/*
526 * Enable/disable buddy's presence monitoring.
527 */
528PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
529 pj_bool_t subscribe)
530{
Benny Prijono73bb7232009-10-20 13:56:26 +0000531 struct buddy_lock lck;
532 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000533
Benny Prijono73bb7232009-10-20 13:56:26 +0000534 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535
Benny Prijono73bb7232009-10-20 13:56:26 +0000536 status = lock_buddy("pjsua_buddy_subscribe_pres()", buddy_id, &lck, 0);
537 if (status != PJ_SUCCESS)
538 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000539
Benny Prijono73bb7232009-10-20 13:56:26 +0000540 lck.buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000541
Benny Prijono73bb7232009-10-20 13:56:26 +0000542 pjsua_buddy_update_pres(buddy_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000543
Benny Prijono73bb7232009-10-20 13:56:26 +0000544 unlock_buddy(&lck);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 return PJ_SUCCESS;
546}
547
548
549/*
Benny Prijono10861432007-10-31 10:54:53 +0000550 * Update buddy's presence.
551 */
552PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
553{
Benny Prijono73bb7232009-10-20 13:56:26 +0000554 struct buddy_lock lck;
555 pj_status_t status;
Benny Prijono10861432007-10-31 10:54:53 +0000556
Benny Prijono73bb7232009-10-20 13:56:26 +0000557 PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
Benny Prijono10861432007-10-31 10:54:53 +0000558
Benny Prijono73bb7232009-10-20 13:56:26 +0000559 status = lock_buddy("pjsua_buddy_update_pres()", buddy_id, &lck, 0);
560 if (status != PJ_SUCCESS)
561 return status;
Benny Prijono10861432007-10-31 10:54:53 +0000562
Benny Prijono73bb7232009-10-20 13:56:26 +0000563 /* Is this an unsubscribe request? */
564 if (!lck.buddy->monitor) {
565 unsubscribe_buddy_presence(buddy_id);
566 unlock_buddy(&lck);
567 return PJ_SUCCESS;
Benny Prijono10861432007-10-31 10:54:53 +0000568 }
569
570 /* Ignore if presence is already active for the buddy */
Benny Prijono73bb7232009-10-20 13:56:26 +0000571 if (lck.buddy->sub) {
572 unlock_buddy(&lck);
Benny Prijono10861432007-10-31 10:54:53 +0000573 return PJ_SUCCESS;
574 }
575
576 /* Initiate presence subscription */
577 subscribe_buddy_presence(buddy_id);
578
Benny Prijono73bb7232009-10-20 13:56:26 +0000579 unlock_buddy(&lck);
Benny Prijono10861432007-10-31 10:54:53 +0000580
581 return PJ_SUCCESS;
582}
583
584
585/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 * Dump presence subscriptions to log file.
587 */
588PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
589{
590 unsigned acc_id;
591 unsigned i;
592
593
594 PJSUA_LOCK();
595
596 /*
597 * When no detail is required, just dump number of server and client
598 * subscriptions.
599 */
600 if (verbose == PJ_FALSE) {
601
602 int count = 0;
603
604 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
605
606 if (!pjsua_var.acc[acc_id].valid)
607 continue;
608
609 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
610 struct pjsua_srv_pres *uapres;
611
612 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
613 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
614 ++count;
615 uapres = uapres->next;
616 }
617 }
618 }
619
620 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
621 count));
622
623 count = 0;
624
625 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
626 if (pjsua_var.buddy[i].uri.slen == 0)
627 continue;
628 if (pjsua_var.buddy[i].sub) {
629 ++count;
630 }
631 }
632
633 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
634 count));
635 PJSUA_UNLOCK();
636 return;
637 }
638
639
640 /*
641 * Dumping all server (UAS) subscriptions
642 */
643 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
644
645 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
646
647 if (!pjsua_var.acc[acc_id].valid)
648 continue;
649
650 PJ_LOG(3,(THIS_FILE, " %.*s",
651 (int)pjsua_var.acc[acc_id].cfg.id.slen,
652 pjsua_var.acc[acc_id].cfg.id.ptr));
653
654 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
655
656 PJ_LOG(3,(THIS_FILE, " - none - "));
657
658 } else {
659 struct pjsua_srv_pres *uapres;
660
661 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
662 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
663
664 PJ_LOG(3,(THIS_FILE, " %10s %s",
665 pjsip_evsub_get_state_name(uapres->sub),
666 uapres->remote));
667
668 uapres = uapres->next;
669 }
670 }
671 }
672
673 /*
674 * Dumping all client (UAC) subscriptions
675 */
676 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
677
678 if (pjsua_var.buddy_cnt == 0) {
679
680 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
681
682 } else {
683 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
684
685 if (pjsua_var.buddy[i].uri.slen == 0)
686 continue;
687
688 if (pjsua_var.buddy[i].sub) {
689 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
690 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
691 (int)pjsua_var.buddy[i].uri.slen,
692 pjsua_var.buddy[i].uri.ptr));
693 } else {
694 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
695 "(null)",
696 (int)pjsua_var.buddy[i].uri.slen,
697 pjsua_var.buddy[i].uri.ptr));
698 }
699 }
700 }
701
702 PJSUA_UNLOCK();
703}
704
705
706/***************************************************************************
707 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000708 */
709
710/* Proto */
711static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
712
713/* The module instance. */
714static pjsip_module mod_pjsua_pres =
715{
716 NULL, NULL, /* prev, next. */
717 { "mod-pjsua-pres", 14 }, /* Name. */
718 -1, /* Id */
719 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000720 NULL, /* load() */
721 NULL, /* start() */
722 NULL, /* stop() */
723 NULL, /* unload() */
724 &pres_on_rx_request, /* on_rx_request() */
725 NULL, /* on_rx_response() */
726 NULL, /* on_tx_request. */
727 NULL, /* on_tx_response() */
728 NULL, /* on_tsx_state() */
729
730};
731
732
733/* Callback called when *server* subscription state has changed. */
734static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
735{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000736 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000737
738 PJ_UNUSED_ARG(event);
739
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000740 PJSUA_LOCK();
741
Benny Prijonoa1e69682007-05-11 15:14:34 +0000742 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000743 if (uapres) {
Benny Prijono63fba012008-07-17 14:19:10 +0000744 pjsip_evsub_state state;
745
Benny Prijonoba736c42008-07-10 20:45:03 +0000746 PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
Benny Prijono834aee32006-02-19 01:38:06 +0000747 uapres->remote, pjsip_evsub_get_state_name(sub)));
748
Benny Prijono63fba012008-07-17 14:19:10 +0000749 state = pjsip_evsub_get_state(sub);
750
751 if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
752 pj_str_t from;
753
754 from = uapres->dlg->remote.info_str;
755 (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
756 uapres, &from,
757 state, event);
758 }
759
760 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000761 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000762 pj_list_erase(uapres);
763 }
764 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000765
766 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000767}
768
769/* This is called when request is received.
770 * We need to check for incoming SUBSCRIBE request.
771 */
772static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
773{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000774 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000775 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000776 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000777 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
778 pjsua_srv_pres *uapres;
779 pjsip_evsub *sub;
780 pjsip_evsub_user pres_cb;
Benny Prijono834aee32006-02-19 01:38:06 +0000781 pjsip_dialog *dlg;
Benny Prijono63fba012008-07-17 14:19:10 +0000782 pjsip_status_code st_code;
783 pj_str_t reason;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000784 pjsip_expires_hdr *expires_hdr;
Benny Prijono63fba012008-07-17 14:19:10 +0000785 pjsua_msg_data msg_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000786 pj_status_t status;
787
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000788 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000789 return PJ_FALSE;
790
791 /* Incoming SUBSCRIBE: */
792
Benny Prijono384dab42009-10-14 01:58:04 +0000793 /* Don't want to accept the request if shutdown is in progress */
794 if (pjsua_var.thread_quit_flag) {
795 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
796 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
797 NULL, NULL);
798 return PJ_TRUE;
799 }
800
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000801 PJSUA_LOCK();
802
Benny Prijonoa91a0032006-02-26 21:23:45 +0000803 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000804 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000805 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000806
Benny Prijono6f979412006-06-15 12:25:46 +0000807 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
808 acc_id));
809
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000810 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000811 if (acc->contact.slen) {
812 contact = acc->contact;
813 } else {
814 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
815 acc_id, rdata);
816 if (status != PJ_SUCCESS) {
817 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
818 status);
819 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000820 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
821 NULL, NULL);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000822 return PJ_TRUE;
823 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000824 }
825
Benny Prijono834aee32006-02-19 01:38:06 +0000826 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000827 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000828 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000829 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000830 pjsua_perror(THIS_FILE,
831 "Unable to create UAS dialog for subscription",
832 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000833 PJSUA_UNLOCK();
Benny Prijonoa330d452008-08-05 20:14:39 +0000834 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
835 NULL, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000836 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000837 }
838
Benny Prijono48ab2b72007-11-08 09:24:30 +0000839 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000840 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000841 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000842
Benny Prijono834aee32006-02-19 01:38:06 +0000843 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000844 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000845 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
846
847 /* Create server presence subscription: */
848 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
849 if (status != PJ_SUCCESS) {
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000850 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
851 pjsip_tx_data *tdata;
852
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000853 pjsua_perror(THIS_FILE, "Unable to create server subscription",
854 status);
Benny Prijonodbd9d4b2008-09-11 10:25:51 +0000855
856 if (code==599 || code > 699 || code < 300) {
857 code = 400;
858 }
859
860 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
861 if (status == PJ_SUCCESS) {
862 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
863 tdata);
864 }
865
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000866 PJSUA_UNLOCK();
867 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000868 }
869
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000870 /* If account is locked to specific transport, then lock dialog
871 * to this transport too.
872 */
873 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
874 pjsip_tpselector tp_sel;
875
876 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
877 pjsip_dlg_set_transport(dlg, &tp_sel);
878 }
879
Benny Prijono834aee32006-02-19 01:38:06 +0000880 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000881 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000882 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000883 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono63fba012008-07-17 14:19:10 +0000884 uapres->acc_id = acc_id;
885 uapres->dlg = dlg;
Benny Prijono834aee32006-02-19 01:38:06 +0000886 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
887 uapres->remote, PJSIP_MAX_URL_SIZE);
888 if (status < 1)
889 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
890 else
891 uapres->remote[status] = '\0';
892
Sauw Ming5c2f6da2011-02-11 07:39:14 +0000893 pjsip_evsub_add_header(sub, &acc->cfg.sub_hdr_list);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000895
896 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000897 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000898
899
Benny Prijono63fba012008-07-17 14:19:10 +0000900 /* Capture the value of Expires header. */
901 expires_hdr = (pjsip_expires_hdr*)
902 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
903 NULL);
904 if (expires_hdr)
905 uapres->expires = expires_hdr->ivalue;
906 else
907 uapres->expires = -1;
908
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000909 st_code = (pjsip_status_code)200;
Benny Prijono63fba012008-07-17 14:19:10 +0000910 reason = pj_str("OK");
911 pjsua_msg_data_init(&msg_data);
912
913 /* Notify application callback, if any */
914 if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
915 pjsua_buddy_id buddy_id;
916
Benny Prijono73bb7232009-10-20 13:56:26 +0000917 buddy_id = find_buddy(rdata->msg_info.from->uri);
Benny Prijono63fba012008-07-17 14:19:10 +0000918
919 (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
920 &dlg->remote.info_str,
921 rdata, &st_code, &reason,
922 &msg_data);
923 }
924
925 /* Handle rejection case */
926 if (st_code >= 300) {
927 pjsip_tx_data *tdata;
928
929 /* Create response */
930 status = pjsip_dlg_create_response(dlg, rdata, st_code,
931 &reason, &tdata);
932 if (status != PJ_SUCCESS) {
933 pjsua_perror(THIS_FILE, "Error creating response", status);
934 pj_list_erase(uapres);
935 pjsip_pres_terminate(sub, PJ_FALSE);
936 PJSUA_UNLOCK();
937 return PJ_FALSE;
938 }
939
940 /* Add header list, if any */
941 pjsua_process_msg_data(tdata, &msg_data);
942
943 /* Send the response */
944 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
945 tdata);
946 if (status != PJ_SUCCESS) {
947 pjsua_perror(THIS_FILE, "Error sending response", status);
948 /* This is not fatal */
949 }
950
951 /* Terminate presence subscription */
952 pj_list_erase(uapres);
953 pjsip_pres_terminate(sub, PJ_FALSE);
954 PJSUA_UNLOCK();
955 return PJ_TRUE;
956 }
957
958 /* Create and send 2xx response to the SUBSCRIBE request: */
959 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000960 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000961 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
962 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000963 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000964 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000965 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000966 return PJ_FALSE;
967 }
968
Benny Prijono63fba012008-07-17 14:19:10 +0000969 /* If code is 200, send NOTIFY now */
970 if (st_code == 200) {
971 pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
972 NULL, NULL, PJ_TRUE, &msg_data);
973 }
974
975 /* Done: */
976
977 PJSUA_UNLOCK();
978
979 return PJ_TRUE;
980}
981
982
983/*
984 * Send NOTIFY.
985 */
986PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
987 pjsua_srv_pres *srv_pres,
988 pjsip_evsub_state ev_state,
989 const pj_str_t *state_str,
990 const pj_str_t *reason,
991 pj_bool_t with_body,
992 const pjsua_msg_data *msg_data)
993{
994 pjsua_acc *acc;
995 pjsip_pres_status pres_status;
996 pjsua_buddy_id buddy_id;
997 pjsip_tx_data *tdata;
998 pj_status_t status;
999
1000 /* Check parameters */
1001 PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
1002
1003 /* Check that account ID is valid */
1004 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1005 PJ_EINVAL);
1006 /* Check that account is valid */
1007 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1008
1009 PJSUA_LOCK();
1010
1011 acc = &pjsua_var.acc[acc_id];
1012
1013 /* Check that the server presence subscription is still valid */
1014 if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
1015 /* Subscription has been terminated */
1016 PJSUA_UNLOCK();
1017 return PJ_EINVALIDOP;
1018 }
Benny Prijono834aee32006-02-19 01:38:06 +00001019
1020 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +00001021 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +00001022 pres_status.info_cnt = 1;
Benny Prijono63fba012008-07-17 14:19:10 +00001023 pres_status.info[0].basic_open = acc->online_status;
1024 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +00001026 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001027 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono7f6ee022008-07-31 08:32:46 +00001028 /* add RPID information */
1029 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1030 sizeof(pjrpid_element));
Benny Prijono834aee32006-02-19 01:38:06 +00001031
Benny Prijono63fba012008-07-17 14:19:10 +00001032 pjsip_pres_set_status(srv_pres->sub, &pres_status);
Benny Prijono834aee32006-02-19 01:38:06 +00001033
Benny Prijonoc61cc042007-06-27 13:01:59 +00001034 /* Check expires value. If it's zero, send our presense state but
1035 * set subscription state to TERMINATED.
1036 */
Benny Prijono63fba012008-07-17 14:19:10 +00001037 if (srv_pres->expires == 0)
Benny Prijonoc61cc042007-06-27 13:01:59 +00001038 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijonoc61cc042007-06-27 13:01:59 +00001039
Benny Prijono63fba012008-07-17 14:19:10 +00001040 /* Create and send the NOTIFY to active subscription: */
1041 status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
1042 reason, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001043 if (status == PJ_SUCCESS) {
Benny Prijono63fba012008-07-17 14:19:10 +00001044 /* Force removal of message body if msg_body==FALSE */
1045 if (!with_body) {
1046 tdata->msg->body = NULL;
1047 }
1048 pjsua_process_msg_data(tdata, msg_data);
1049 status = pjsip_pres_send_request( srv_pres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001050 }
Benny Prijono834aee32006-02-19 01:38:06 +00001051
1052 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001053 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
1054 status);
Benny Prijono63fba012008-07-17 14:19:10 +00001055 pj_list_erase(srv_pres);
1056 pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001057 PJSUA_UNLOCK();
Benny Prijono63fba012008-07-17 14:19:10 +00001058 return status;
Benny Prijono834aee32006-02-19 01:38:06 +00001059 }
1060
1061
Benny Prijonoa17496a2007-10-31 10:20:31 +00001062 /* Subscribe to buddy's presence if we're not subscribed */
Benny Prijono73bb7232009-10-20 13:56:26 +00001063 buddy_id = find_buddy(srv_pres->dlg->remote.info->uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +00001064 if (buddy_id != PJSUA_INVALID_ID) {
1065 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
1066 if (b->monitor && b->sub == NULL) {
1067 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
1068 "activating outgoing subscription", buddy_id));
1069 subscribe_buddy_presence(buddy_id);
1070 }
1071 }
1072
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001073 PJSUA_UNLOCK();
1074
Benny Prijono63fba012008-07-17 14:19:10 +00001075 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +00001076}
1077
1078
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001079/*
1080 * Client presence publication callback.
1081 */
1082static void publish_cb(struct pjsip_publishc_cbparam *param)
1083{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001084 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001085
1086 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
Benny Prijono53984d12009-04-28 22:19:49 +00001087
1088 pjsip_publishc_destroy(param->pubc);
1089 acc->publish_sess = NULL;
1090
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001091 if (param->status != PJ_SUCCESS) {
1092 char errmsg[PJ_ERR_MSG_SIZE];
1093
1094 pj_strerror(param->status, errmsg, sizeof(errmsg));
1095 PJ_LOG(1,(THIS_FILE,
1096 "Client publication (PUBLISH) failed, status=%d, msg=%s",
1097 param->status, errmsg));
Benny Prijono53984d12009-04-28 22:19:49 +00001098 } else if (param->code == 412) {
1099 /* 412 (Conditional Request Failed)
1100 * The PUBLISH refresh has failed, retry with new one.
1101 */
1102 pjsua_pres_init_publish_acc(acc->index);
1103
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001104 } else {
1105 PJ_LOG(1,(THIS_FILE,
1106 "Client publication (PUBLISH) failed (%d/%.*s)",
1107 param->code, (int)param->reason.slen,
1108 param->reason.ptr));
1109 }
1110
Benny Prijono53984d12009-04-28 22:19:49 +00001111 } else {
Benny Prijono534a9ba2009-10-13 14:01:59 +00001112 if (param->expiration < 1) {
Benny Prijono53984d12009-04-28 22:19:49 +00001113 /* Could happen if server "forgot" to include Expires header
1114 * in the response. We will not renew, so destroy the pubc.
1115 */
1116 pjsip_publishc_destroy(param->pubc);
1117 acc->publish_sess = NULL;
1118 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001119 }
1120}
1121
1122
1123/*
1124 * Send PUBLISH request.
1125 */
1126static pj_status_t send_publish(int acc_id, pj_bool_t active)
1127{
1128 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1129 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1130 pjsip_pres_status pres_status;
1131 pjsip_tx_data *tdata;
1132 pj_status_t status;
1133
1134
1135 /* Create PUBLISH request */
1136 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +00001137 char *bpos;
1138 pj_str_t entity;
1139
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001140 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
1141 if (status != PJ_SUCCESS) {
1142 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1143 goto on_error;
1144 }
1145
1146 /* Set our online status: */
1147 pj_bzero(&pres_status, sizeof(pres_status));
1148 pres_status.info_cnt = 1;
1149 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +00001150 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001151 /* .. including RPID information */
1152 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1153 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001154
Benny Prijono8c6e8842007-02-24 15:33:54 +00001155 /* Be careful not to send PIDF with presence entity ID containing
1156 * "<" character.
1157 */
1158 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
1159 char *epos = pj_strchr(&acc_cfg->id, '>');
1160 if (epos - bpos < 2) {
1161 pj_assert(!"Unexpected invalid URI");
1162 status = PJSIP_EINVALIDURI;
1163 goto on_error;
1164 }
1165 entity.ptr = bpos+1;
1166 entity.slen = epos - bpos - 1;
1167 } else {
1168 entity = acc_cfg->id;
1169 }
1170
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001171 /* Create and add PIDF message body */
1172 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +00001173 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001174 if (status != PJ_SUCCESS) {
1175 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
1176 status);
1177 pjsip_tx_data_dec_ref(tdata);
1178 goto on_error;
1179 }
1180 } else {
1181 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
1182 if (status != PJ_SUCCESS) {
1183 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
1184 goto on_error;
1185 }
1186 }
1187
1188 /* Add headers etc */
1189 pjsua_process_msg_data(tdata, NULL);
1190
1191 /* Send the PUBLISH request */
1192 status = pjsip_publishc_send(acc->publish_sess, tdata);
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001193 if (status == PJ_EPENDING) {
1194 PJ_LOG(3,(THIS_FILE, "Previous request is in progress, "
1195 "PUBLISH request is queued"));
1196 } else if (status != PJ_SUCCESS) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001197 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
1198 goto on_error;
1199 }
1200
1201 acc->publish_state = acc->online_status;
1202 return PJ_SUCCESS;
1203
1204on_error:
Benny Prijono29438152007-06-28 02:47:32 +00001205 if (acc->publish_sess) {
1206 pjsip_publishc_destroy(acc->publish_sess);
1207 acc->publish_sess = NULL;
1208 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001209 return status;
1210}
1211
1212
1213/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +00001214pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001215{
1216 const pj_str_t STR_PRESENCE = { "presence", 8 };
1217 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
1218 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1219 pj_status_t status;
1220
1221 /* Create and init client publication session */
1222 if (acc_cfg->publish_enabled) {
1223
1224 /* Create client publication */
Benny Prijonofe50c9e2009-10-12 07:44:14 +00001225 status = pjsip_publishc_create(pjsua_var.endpt, &acc_cfg->publish_opt,
1226 acc, &publish_cb,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001227 &acc->publish_sess);
1228 if (status != PJ_SUCCESS) {
1229 acc->publish_sess = NULL;
1230 return status;
1231 }
1232
1233 /* Initialize client publication */
1234 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
1235 &acc_cfg->id, &acc_cfg->id,
1236 &acc_cfg->id,
Benny Prijono53984d12009-04-28 22:19:49 +00001237 PJSUA_PUBLISH_EXPIRATION);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001238 if (status != PJ_SUCCESS) {
1239 acc->publish_sess = NULL;
1240 return status;
1241 }
1242
Benny Prijono703b7d72007-03-20 09:13:24 +00001243 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +00001244 if (acc->cred_cnt) {
1245 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
1246 acc->cred);
1247 }
Benny Prijono703b7d72007-03-20 09:13:24 +00001248
1249 /* Set route-set */
1250 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
1251
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001252 /* Send initial PUBLISH request */
1253 if (acc->online_status != 0) {
1254 status = send_publish(acc_id, PJ_TRUE);
1255 if (status != PJ_SUCCESS)
1256 return status;
1257 }
1258
1259 } else {
1260 acc->publish_sess = NULL;
1261 }
1262
1263 return PJ_SUCCESS;
1264}
1265
1266
1267/* Init presence for account */
1268pj_status_t pjsua_pres_init_acc(int acc_id)
1269{
1270 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1271
1272 /* Init presence subscription */
1273 pj_list_init(&acc->pres_srv_list);
1274
Benny Prijono8b6834f2007-02-24 13:29:22 +00001275 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001276}
1277
1278
Benny Prijono166d5022010-02-10 14:24:48 +00001279/* Unpublish presence publication */
1280void pjsua_pres_unpublish(pjsua_acc *acc)
1281{
1282 if (acc->publish_sess) {
1283 pjsua_acc_config *acc_cfg = &acc->cfg;
1284
1285 acc->online_status = PJ_FALSE;
1286 send_publish(acc->index, PJ_FALSE);
1287 /* By ticket #364, don't destroy the session yet (let the callback
1288 destroy it)
1289 if (acc->publish_sess) {
1290 pjsip_publishc_destroy(acc->publish_sess);
1291 acc->publish_sess = NULL;
1292 }
1293 */
1294 acc_cfg->publish_enabled = PJ_FALSE;
1295 }
1296}
1297
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298/* Terminate server subscription for the account */
1299void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001300{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001301 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijono834aee32006-02-19 01:38:06 +00001302 pjsua_srv_pres *uapres;
1303
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001304 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +00001305
Benny Prijono922933b2007-01-21 16:23:56 +00001306 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001307 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001308
1309 pjsip_pres_status pres_status;
1310 pj_str_t reason = { "noresource", 10 };
Benny Prijono5516f912008-05-05 12:06:08 +00001311 pjsua_srv_pres *next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001312 pjsip_tx_data *tdata;
1313
Benny Prijono5516f912008-05-05 12:06:08 +00001314 next = uapres->next;
1315
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316 pjsip_pres_get_status(uapres->sub, &pres_status);
1317
1318 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
1319 pjsip_pres_set_status(uapres->sub, &pres_status);
1320
1321 if (pjsip_pres_notify(uapres->sub,
1322 PJSIP_EVSUB_STATE_TERMINATED, NULL,
1323 &reason, &tdata)==PJ_SUCCESS)
1324 {
1325 pjsip_pres_send_request(uapres->sub, tdata);
1326 }
1327
Benny Prijono5516f912008-05-05 12:06:08 +00001328 uapres = next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001329 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001330
Benny Prijono922933b2007-01-21 16:23:56 +00001331 /* Clear server presence subscription list because account might be reused
1332 * later. */
1333 pj_list_init(&acc->pres_srv_list);
1334
1335 /* Terminate presence publication, if any */
Benny Prijono166d5022010-02-10 14:24:48 +00001336 pjsua_pres_unpublish(acc);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001337}
1338
1339
Benny Prijono4461c7d2007-08-25 13:36:15 +00001340/* Update server subscription (e.g. when our online status has changed) */
1341void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001342{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001343 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1344 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001345 pjsua_srv_pres *uapres;
1346
1347 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
1348
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001349 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +00001350
1351 pjsip_pres_status pres_status;
1352 pjsip_tx_data *tdata;
1353
1354 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono232759b2008-09-08 12:46:29 +00001355
1356 /* Only send NOTIFY once subscription is active. Some subscriptions
1357 * may still be in NULL (when app is adding a new buddy while in the
1358 * on_incoming_subscribe() callback) or PENDING (when user approval is
1359 * being requested) state and we don't send NOTIFY to these subs until
1360 * the user accepted the request.
1361 */
1362 if (pjsip_evsub_get_state(uapres->sub)==PJSIP_EVSUB_STATE_ACTIVE &&
1363 (force || pres_status.info[0].basic_open != acc->online_status))
1364 {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001365
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001366 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001367 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
1368 sizeof(pjrpid_element));
1369
Benny Prijono834aee32006-02-19 01:38:06 +00001370 pjsip_pres_set_status(uapres->sub, &pres_status);
1371
Benny Prijono21b9ad92006-08-15 13:11:22 +00001372 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
1373 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001374 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001375 }
Benny Prijono834aee32006-02-19 01:38:06 +00001376 }
1377
1378 uapres = uapres->next;
1379 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001380
Benny Prijono8b6834f2007-02-24 13:29:22 +00001381 /* Send PUBLISH if required. We only do this when we have a PUBLISH
1382 * session. If we don't have a PUBLISH session, then it could be
1383 * that we're waiting until registration has completed before we
1384 * send the first PUBLISH.
1385 */
1386 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +00001387 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +00001388 send_publish(acc_id, PJ_TRUE);
1389 }
1390 }
Benny Prijono834aee32006-02-19 01:38:06 +00001391}
1392
1393
1394
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395/***************************************************************************
1396 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +00001397 */
1398
Benny Prijono73bb7232009-10-20 13:56:26 +00001399static void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
1400{
1401 pjsua_buddy *buddy = (pjsua_buddy*)entry->user_data;
1402
1403 PJ_UNUSED_ARG(th);
1404
1405 entry->id = PJ_FALSE;
1406 pjsua_buddy_update_pres(buddy->index);
1407}
1408
1409/* Reschedule subscription refresh timer or terminate the subscription
1410 * refresh timer for the specified buddy.
1411 */
1412static void buddy_resubscribe(pjsua_buddy *buddy, pj_bool_t resched,
1413 unsigned msec_interval)
1414{
1415 if (buddy->timer.id) {
1416 pjsua_cancel_timer(&buddy->timer);
1417 buddy->timer.id = PJ_FALSE;
1418 }
1419
1420 if (resched) {
1421 pj_time_val delay;
1422
Benny Prijono12c01a92009-10-21 02:37:52 +00001423 PJ_LOG(4,(THIS_FILE,
1424 "Resubscribing buddy id %u in %u ms (reason: %.*s)",
1425 buddy->index, msec_interval,
1426 (int)buddy->term_reason.slen,
1427 buddy->term_reason.ptr));
1428
Benny Prijono73bb7232009-10-20 13:56:26 +00001429 pj_timer_entry_init(&buddy->timer, 0, buddy, &buddy_timer_cb);
1430 delay.sec = 0;
1431 delay.msec = msec_interval;
1432 pj_time_val_normalize(&delay);
1433
1434 if (pjsua_schedule_timer(&buddy->timer, &delay)==PJ_SUCCESS)
1435 buddy->timer.id = PJ_TRUE;
1436 }
1437}
1438
Benny Prijono834aee32006-02-19 01:38:06 +00001439/* Callback called when *client* subscription state has changed. */
1440static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1441{
1442 pjsua_buddy *buddy;
1443
1444 PJ_UNUSED_ARG(event);
1445
Benny Prijono73bb7232009-10-20 13:56:26 +00001446 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1447 * a dialog attached to it, lock_buddy() will use the dialog
1448 * lock, which we are currently holding!
1449 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001450 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001451 if (buddy) {
Benny Prijonoba736c42008-07-10 20:45:03 +00001452 PJ_LOG(4,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001453 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001454 (int)pjsua_var.buddy[buddy->index].uri.slen,
1455 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001456 pjsip_evsub_get_state_name(sub)));
1457
1458 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001459 int resub_delay = -1;
1460
Benny Prijono63fba012008-07-17 14:19:10 +00001461 if (buddy->term_reason.ptr == NULL) {
1462 buddy->term_reason.ptr = (char*)
1463 pj_pool_alloc(buddy->pool,
1464 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
1465 }
1466 pj_strncpy(&buddy->term_reason,
1467 pjsip_evsub_get_termination_reason(sub),
1468 PJSUA_BUDDY_SUB_TERM_REASON_LEN);
Benny Prijono73bb7232009-10-20 13:56:26 +00001469
1470 buddy->term_code = 200;
1471
1472 /* Determine whether to resubscribe automatically */
Benny Prijonod3d18c32009-11-10 10:54:11 +00001473 if (event && event->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001474 const pjsip_transaction *tsx = event->body.tsx_state.tsx;
1475 if (pjsip_method_cmp(&tsx->method,
1476 &pjsip_subscribe_method)==0)
1477 {
1478 buddy->term_code = tsx->status_code;
1479 switch (tsx->status_code) {
1480 case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST:
1481 /* 481: we refreshed too late? resubscribe
1482 * immediately.
1483 */
Benny Prijono6ab05322009-10-21 03:03:06 +00001484 /* But this must only happen when the 481 is received
1485 * on subscription refresh request. We MUST NOT try to
1486 * resubscribe automatically if the 481 is received
1487 * on the initial SUBSCRIBE (if server returns this
1488 * response for some reason).
1489 */
1490 if (buddy->dlg->remote.contact)
1491 resub_delay = 500;
Benny Prijono73bb7232009-10-20 13:56:26 +00001492 break;
1493 }
1494 } else if (pjsip_method_cmp(&tsx->method,
1495 &pjsip_notify_method)==0)
1496 {
1497 if (pj_stricmp2(&buddy->term_reason, "deactivated")==0 ||
1498 pj_stricmp2(&buddy->term_reason, "timeout")==0) {
1499 /* deactivated: The subscription has been terminated,
1500 * but the subscriber SHOULD retry immediately with
1501 * a new subscription.
1502 */
1503 /* timeout: The subscription has been terminated
1504 * because it was not refreshed before it expired.
1505 * Clients MAY re-subscribe immediately. The
1506 * "retry-after" parameter has no semantics for
1507 * "timeout".
1508 */
1509 resub_delay = 500;
1510 }
1511 else if (pj_stricmp2(&buddy->term_reason, "probation")==0||
1512 pj_stricmp2(&buddy->term_reason, "giveup")==0) {
1513 /* probation: The subscription has been terminated,
1514 * but the client SHOULD retry at some later time.
1515 * If a "retry-after" parameter is also present, the
1516 * client SHOULD wait at least the number of seconds
1517 * specified by that parameter before attempting to re-
1518 * subscribe.
1519 */
1520 /* giveup: The subscription has been terminated because
1521 * the notifier could not obtain authorization in a
1522 * timely fashion. If a "retry-after" parameter is
1523 * also present, the client SHOULD wait at least the
1524 * number of seconds specified by that parameter before
1525 * attempting to re-subscribe; otherwise, the client
1526 * MAY retry immediately, but will likely get put back
1527 * into pending state.
1528 */
1529 const pjsip_sub_state_hdr *sub_hdr;
1530 pj_str_t sub_state = { "Subscription-State", 18 };
1531 const pjsip_msg *msg;
1532
1533 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1534 sub_hdr = (const pjsip_sub_state_hdr*)
1535 pjsip_msg_find_hdr_by_name(msg, &sub_state,
1536 NULL);
1537 if (sub_hdr && sub_hdr->retry_after > 0)
1538 resub_delay = sub_hdr->retry_after * 1000;
1539 }
1540
1541 }
1542 }
1543
1544 /* For other cases of subscription termination, if resubscribe
1545 * timer is not set, schedule with default expiration (plus minus
1546 * some random value, to avoid sending SUBSCRIBEs all at once)
1547 */
1548 if (resub_delay == -1) {
1549 pj_assert(PJSUA_PRES_TIMER >= 3);
1550 resub_delay = PJSUA_PRES_TIMER*1000 - 2500 + (pj_rand()%5000);
1551 }
1552
1553 buddy_resubscribe(buddy, PJ_TRUE, resub_delay);
Benny Prijono6ab05322009-10-21 03:03:06 +00001554
Benny Prijono63fba012008-07-17 14:19:10 +00001555 } else {
Benny Prijono6ab05322009-10-21 03:03:06 +00001556 /* This will clear the last termination code/reason */
Benny Prijono73bb7232009-10-20 13:56:26 +00001557 buddy->term_code = 0;
Benny Prijono63fba012008-07-17 14:19:10 +00001558 buddy->term_reason.slen = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001559 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001560
Benny Prijono63499892010-10-12 12:45:15 +00001561 /* Call callbacks */
1562 if (pjsua_var.ua_cfg.cb.on_buddy_evsub_state)
1563 (*pjsua_var.ua_cfg.cb.on_buddy_evsub_state)(buddy->index, sub,
1564 event);
1565
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001566 if (pjsua_var.ua_cfg.cb.on_buddy_state)
1567 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono63fba012008-07-17 14:19:10 +00001568
1569 /* Clear subscription */
1570 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1571 buddy->sub = NULL;
1572 buddy->status.info_cnt = 0;
Benny Prijono73bb7232009-10-20 13:56:26 +00001573 buddy->dlg = NULL;
Benny Prijono63fba012008-07-17 14:19:10 +00001574 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1575 }
Benny Prijono834aee32006-02-19 01:38:06 +00001576 }
1577}
1578
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001579
1580/* Callback when transaction state has changed. */
1581static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1582 pjsip_transaction *tsx,
1583 pjsip_event *event)
1584{
1585 pjsua_buddy *buddy;
1586 pjsip_contact_hdr *contact_hdr;
1587
Benny Prijono73bb7232009-10-20 13:56:26 +00001588 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1589 * a dialog attached to it, lock_buddy() will use the dialog
1590 * lock, which we are currently holding!
1591 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001592 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593 if (!buddy) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001594 return;
1595 }
1596
1597 /* We only use this to update buddy's Contact, when it's not
1598 * set.
1599 */
1600 if (buddy->contact.slen != 0) {
1601 /* Contact already set */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602 return;
1603 }
1604
1605 /* Only care about 2xx response to outgoing SUBSCRIBE */
1606 if (tsx->status_code/100 != 2 ||
1607 tsx->role != PJSIP_UAC_ROLE ||
1608 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001609 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611 return;
1612 }
1613
1614 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001615 contact_hdr = (pjsip_contact_hdr*)
1616 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617 PJSIP_H_CONTACT, NULL);
Benny Prijono8b33bba2010-06-02 03:03:43 +00001618 if (!contact_hdr || !contact_hdr->uri) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619 return;
1620 }
1621
Benny Prijonoa1e69682007-05-11 15:14:34 +00001622 buddy->contact.ptr = (char*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001623 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1625 contact_hdr->uri,
1626 buddy->contact.ptr,
1627 PJSIP_MAX_URL_SIZE);
1628 if (buddy->contact.slen < 0)
1629 buddy->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001630}
1631
1632
Benny Prijono834aee32006-02-19 01:38:06 +00001633/* Callback called when we receive NOTIFY */
1634static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1635 pjsip_rx_data *rdata,
1636 int *p_st_code,
1637 pj_str_t **p_st_text,
1638 pjsip_hdr *res_hdr,
1639 pjsip_msg_body **p_body)
1640{
1641 pjsua_buddy *buddy;
1642
Benny Prijono73bb7232009-10-20 13:56:26 +00001643 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1644 * a dialog attached to it, lock_buddy() will use the dialog
1645 * lock, which we are currently holding!
1646 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001647 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001648 if (buddy) {
1649 /* Update our info. */
1650 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001651 }
1652
1653 /* The default is to send 200 response to NOTIFY.
1654 * Just leave it there..
1655 */
1656 PJ_UNUSED_ARG(rdata);
1657 PJ_UNUSED_ARG(p_st_code);
1658 PJ_UNUSED_ARG(p_st_text);
1659 PJ_UNUSED_ARG(res_hdr);
1660 PJ_UNUSED_ARG(p_body);
1661}
1662
1663
Benny Prijono834aee32006-02-19 01:38:06 +00001664/* It does what it says.. */
Benny Prijono73bb7232009-10-20 13:56:26 +00001665static void subscribe_buddy_presence(pjsua_buddy_id buddy_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001666{
Benny Prijono63499892010-10-12 12:45:15 +00001667 pjsip_evsub_user pres_callback;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001668 pj_pool_t *tmp_pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001669 pjsua_buddy *buddy;
1670 int acc_id;
1671 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001672 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001673 pjsip_tx_data *tdata;
1674 pj_status_t status;
1675
Benny Prijono63499892010-10-12 12:45:15 +00001676 /* Event subscription callback. */
1677 pj_bzero(&pres_callback, sizeof(pres_callback));
1678 pres_callback.on_evsub_state = &pjsua_evsub_on_state;
1679 pres_callback.on_tsx_state = &pjsua_evsub_on_tsx_state;
1680 pres_callback.on_rx_notify = &pjsua_evsub_on_rx_notify;
1681
Benny Prijono73bb7232009-10-20 13:56:26 +00001682 buddy = &pjsua_var.buddy[buddy_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001684
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001686
Benny Prijonob4a17c92006-07-10 14:40:21 +00001687 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
Benny Prijono73bb7232009-10-20 13:56:26 +00001688 acc_id, buddy_id));
Benny Prijonob4a17c92006-07-10 14:40:21 +00001689
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001690 /* Generate suitable Contact header unless one is already set in
1691 * the account
1692 */
1693 if (acc->contact.slen) {
1694 contact = acc->contact;
1695 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001696 tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
1697
1698 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001699 acc_id, &buddy->uri);
1700 if (status != PJ_SUCCESS) {
1701 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1702 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001703 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001704 return;
1705 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001706 }
1707
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001708 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001709 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001710 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001711 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001712 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001713 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001714 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001715 pjsua_perror(THIS_FILE, "Unable to create dialog",
1716 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001717 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001718 return;
1719 }
1720
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001721 /* Increment the dialog's lock otherwise when presence session creation
1722 * fails the dialog will be destroyed prematurely.
1723 */
1724 pjsip_dlg_inc_lock(buddy->dlg);
1725
Benny Prijonof9c40c32007-06-28 07:20:26 +00001726 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001727 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1728 if (status != PJ_SUCCESS) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001729 buddy->sub = NULL;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001730 pjsua_perror(THIS_FILE, "Unable to create presence client",
1731 status);
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001732 /* This should destroy the dialog since there's no session
1733 * referencing it
1734 */
Benny Prijono011e3f22009-12-10 05:16:23 +00001735 if (buddy->dlg) pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001736 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001737 return;
1738 }
1739
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001740 /* If account is locked to specific transport, then lock dialog
1741 * to this transport too.
1742 */
1743 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1744 pjsip_tpselector tp_sel;
1745
1746 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001747 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001748 }
1749
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001750 /* Set route-set */
1751 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001752 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753 }
1754
1755 /* Set credentials */
1756 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001757 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001758 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001759 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001760
Benny Prijono48ab2b72007-11-08 09:24:30 +00001761 /* Set authentication preference */
1762 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1763
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001764 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001765
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001766 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001767 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00001768 if (buddy->dlg) pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001769 if (buddy->sub) {
1770 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1771 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001772 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001773 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1774 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001775 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001776 return;
1777 }
1778
Benny Prijono21b9ad92006-08-15 13:11:22 +00001779 pjsua_process_msg_data(tdata, NULL);
1780
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001781 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001782 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00001783 if (buddy->dlg) pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoa6992c52007-06-05 22:58:32 +00001784 if (buddy->sub) {
1785 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1786 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001787 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001788 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1789 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001790 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001791 return;
1792 }
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001793
Benny Prijono0bd5eb92009-04-14 11:10:31 +00001794 pjsip_dlg_dec_lock(buddy->dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001795 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono834aee32006-02-19 01:38:06 +00001796}
1797
1798
1799/* It does what it says... */
Benny Prijono73bb7232009-10-20 13:56:26 +00001800static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id)
Benny Prijono834aee32006-02-19 01:38:06 +00001801{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001802 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001803 pjsip_tx_data *tdata;
1804 pj_status_t status;
1805
Benny Prijono73bb7232009-10-20 13:56:26 +00001806 buddy = &pjsua_var.buddy[buddy_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001807
1808 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001809 return;
1810
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001811 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001812 buddy->sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001813 return;
1814 }
1815
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001816 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001817 if (status == PJ_SUCCESS) {
1818 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001819 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001820 }
Benny Prijono834aee32006-02-19 01:38:06 +00001821
Benny Prijono48da92e2007-05-16 08:56:30 +00001822 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001823 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1824 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001825 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1826 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001827 }
1828}
1829
Benny Prijono834aee32006-02-19 01:38:06 +00001830/* It does what it says.. */
Benny Prijono73bb7232009-10-20 13:56:26 +00001831static pj_status_t refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001832{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001833 unsigned i;
Benny Prijono73bb7232009-10-20 13:56:26 +00001834 pj_status_t status;
Benny Prijonof9c40c32007-06-28 07:20:26 +00001835
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001836 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono73bb7232009-10-20 13:56:26 +00001837 struct buddy_lock lck;
Benny Prijono834aee32006-02-19 01:38:06 +00001838
Benny Prijono73bb7232009-10-20 13:56:26 +00001839 if (!pjsua_buddy_is_valid(i))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001840 continue;
1841
Benny Prijono73bb7232009-10-20 13:56:26 +00001842 status = lock_buddy("refresh_client_subscriptions()", i, &lck, 0);
1843 if (status != PJ_SUCCESS)
1844 return status;
1845
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001846 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001847 subscribe_buddy_presence(i);
1848
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001849 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001850 unsubscribe_buddy_presence(i);
1851
1852 }
Benny Prijono73bb7232009-10-20 13:56:26 +00001853
1854 unlock_buddy(&lck);
Benny Prijono834aee32006-02-19 01:38:06 +00001855 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001856
Benny Prijono73bb7232009-10-20 13:56:26 +00001857 return PJ_SUCCESS;
Benny Prijono834aee32006-02-19 01:38:06 +00001858}
1859
Benny Prijono4dd961b2009-10-26 11:21:37 +00001860/***************************************************************************
1861 * MWI
1862 */
1863/* Callback called when *client* subscription state has changed. */
1864static void mwi_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
1865{
1866 pjsua_acc *acc;
1867
1868 PJ_UNUSED_ARG(event);
1869
1870 /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
1871 * a dialog attached to it, lock_buddy() will use the dialog
1872 * lock, which we are currently holding!
1873 */
1874 acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
1875 if (!acc)
1876 return;
1877
1878 PJ_LOG(4,(THIS_FILE,
1879 "MWI subscription for %.*s is %s",
1880 (int)acc->cfg.id.slen, acc->cfg.id.ptr,
1881 pjsip_evsub_get_state_name(sub)));
1882
1883 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1884 /* Clear subscription */
1885 acc->mwi_dlg = NULL;
1886 acc->mwi_sub = NULL;
1887 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1888
1889 }
1890}
1891
1892/* Callback called when we receive NOTIFY */
1893static void mwi_evsub_on_rx_notify(pjsip_evsub *sub,
1894 pjsip_rx_data *rdata,
1895 int *p_st_code,
1896 pj_str_t **p_st_text,
1897 pjsip_hdr *res_hdr,
1898 pjsip_msg_body **p_body)
1899{
1900 pjsua_mwi_info mwi_info;
1901 pjsua_acc *acc;
1902
1903 PJ_UNUSED_ARG(p_st_code);
1904 PJ_UNUSED_ARG(p_st_text);
1905 PJ_UNUSED_ARG(res_hdr);
1906 PJ_UNUSED_ARG(p_body);
1907
1908 acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
1909 if (!acc)
1910 return;
1911
1912 /* Construct mwi_info */
1913 pj_bzero(&mwi_info, sizeof(mwi_info));
1914 mwi_info.evsub = sub;
1915 mwi_info.rdata = rdata;
1916
1917 /* Call callback */
1918 if (pjsua_var.ua_cfg.cb.on_mwi_info) {
1919 (*pjsua_var.ua_cfg.cb.on_mwi_info)(acc->index, &mwi_info);
1920 }
1921}
1922
1923
1924/* Event subscription callback. */
1925static pjsip_evsub_user mwi_cb =
1926{
1927 &mwi_evsub_on_state,
1928 NULL, /* on_tsx_state: not interested */
1929 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1930 * we want to authenticate
1931 */
1932
1933 &mwi_evsub_on_rx_notify,
1934
1935 NULL, /* on_client_refresh: Use default behaviour, which is to
1936 * refresh client subscription. */
1937
1938 NULL, /* on_server_timeout: Use default behaviour, which is to send
1939 * NOTIFY to terminate.
1940 */
1941};
1942
1943void pjsua_start_mwi(pjsua_acc *acc)
1944{
1945 pj_pool_t *tmp_pool = NULL;
1946 pj_str_t contact;
1947 pjsip_tx_data *tdata;
1948 pj_status_t status;
1949
1950 if (!acc->cfg.mwi_enabled) {
1951 if (acc->mwi_sub) {
1952 /* Terminate MWI subscription */
1953 pjsip_tx_data *tdata;
1954 pjsip_evsub *sub = acc->mwi_sub;
1955
1956 /* Detach sub from this account */
1957 acc->mwi_sub = NULL;
1958 acc->mwi_dlg = NULL;
1959 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
1960
1961 /* Unsubscribe */
1962 status = pjsip_mwi_initiate(acc->mwi_sub, 0, &tdata);
1963 if (status == PJ_SUCCESS) {
1964 status = pjsip_mwi_send_request(acc->mwi_sub, tdata);
1965 }
1966 }
1967 return;
1968 }
1969
1970 if (acc->mwi_sub) {
1971 /* Subscription is already active */
1972 return;
1973
1974 }
1975
1976 /* Generate suitable Contact header unless one is already set in
1977 * the account
1978 */
1979 if (acc->contact.slen) {
1980 contact = acc->contact;
1981 } else {
1982 tmp_pool = pjsua_pool_create("tmpmwi", 512, 256);
1983 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
1984 acc->index, &acc->cfg.id);
1985 if (status != PJ_SUCCESS) {
1986 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1987 status);
1988 pj_pool_release(tmp_pool);
1989 return;
1990 }
1991 }
1992
1993 /* Create UAC dialog */
1994 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
1995 &acc->cfg.id,
1996 &contact,
1997 &acc->cfg.id,
1998 NULL, &acc->mwi_dlg);
1999 if (status != PJ_SUCCESS) {
2000 pjsua_perror(THIS_FILE, "Unable to create dialog", status);
2001 if (tmp_pool) pj_pool_release(tmp_pool);
2002 return;
2003 }
2004
2005 /* Increment the dialog's lock otherwise when presence session creation
2006 * fails the dialog will be destroyed prematurely.
2007 */
2008 pjsip_dlg_inc_lock(acc->mwi_dlg);
2009
2010 /* Create UAC subscription */
2011 status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb,
2012 PJSIP_EVSUB_NO_EVENT_ID, &acc->mwi_sub);
2013 if (status != PJ_SUCCESS) {
2014 pjsua_perror(THIS_FILE, "Error creating MWI subscription", status);
2015 if (tmp_pool) pj_pool_release(tmp_pool);
Benny Prijono011e3f22009-12-10 05:16:23 +00002016 if (acc->mwi_dlg) pjsip_dlg_dec_lock(acc->mwi_dlg);
Benny Prijono4dd961b2009-10-26 11:21:37 +00002017 return;
2018 }
2019
2020 /* If account is locked to specific transport, then lock dialog
2021 * to this transport too.
2022 */
2023 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
2024 pjsip_tpselector tp_sel;
2025
2026 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
2027 pjsip_dlg_set_transport(acc->mwi_dlg, &tp_sel);
2028 }
2029
2030 /* Set route-set */
2031 if (!pj_list_empty(&acc->route_set)) {
2032 pjsip_dlg_set_route_set(acc->mwi_dlg, &acc->route_set);
2033 }
2034
2035 /* Set credentials */
2036 if (acc->cred_cnt) {
2037 pjsip_auth_clt_set_credentials( &acc->mwi_dlg->auth_sess,
2038 acc->cred_cnt, acc->cred);
2039 }
2040
2041 /* Set authentication preference */
2042 pjsip_auth_clt_set_prefs(&acc->mwi_dlg->auth_sess, &acc->cfg.auth_pref);
2043
2044 pjsip_evsub_set_mod_data(acc->mwi_sub, pjsua_var.mod.id, acc);
2045
2046 status = pjsip_mwi_initiate(acc->mwi_sub, -1, &tdata);
2047 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00002048 if (acc->mwi_dlg) pjsip_dlg_dec_lock(acc->mwi_dlg);
Benny Prijono4dd961b2009-10-26 11:21:37 +00002049 if (acc->mwi_sub) {
2050 pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
2051 }
2052 acc->mwi_sub = NULL;
2053 acc->mwi_dlg = NULL;
2054 pjsua_perror(THIS_FILE, "Unable to create initial MWI SUBSCRIBE",
2055 status);
2056 if (tmp_pool) pj_pool_release(tmp_pool);
2057 return;
2058 }
2059
2060 pjsua_process_msg_data(tdata, NULL);
2061
2062 status = pjsip_pres_send_request(acc->mwi_sub, tdata);
2063 if (status != PJ_SUCCESS) {
Benny Prijono011e3f22009-12-10 05:16:23 +00002064 if (acc->mwi_dlg) pjsip_dlg_dec_lock(acc->mwi_dlg);
Benny Prijono4dd961b2009-10-26 11:21:37 +00002065 if (acc->mwi_sub) {
2066 pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
2067 }
2068 acc->mwi_sub = NULL;
2069 acc->mwi_dlg = NULL;
2070 pjsua_perror(THIS_FILE, "Unable to send initial MWI SUBSCRIBE",
2071 status);
2072 if (tmp_pool) pj_pool_release(tmp_pool);
2073 return;
2074 }
2075
2076 pjsip_dlg_dec_lock(acc->mwi_dlg);
2077 if (tmp_pool) pj_pool_release(tmp_pool);
2078
2079}
2080
2081
Benny Prijonofe1bd342009-11-20 23:33:07 +00002082/***************************************************************************
2083 * Unsolicited MWI
2084 */
2085static pj_bool_t unsolicited_mwi_on_rx_request(pjsip_rx_data *rdata)
2086{
2087 pjsip_msg *msg = rdata->msg_info.msg;
2088 pj_str_t EVENT_HDR = { "Event", 5 };
2089 pj_str_t MWI = { "message-summary", 15 };
2090 pjsip_event_hdr *eh;
2091
2092 if (pjsip_method_cmp(&msg->line.req.method, &pjsip_notify_method)!=0) {
2093 /* Only interested with NOTIFY request */
2094 return PJ_FALSE;
2095 }
2096
2097 eh = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(msg, &EVENT_HDR, NULL);
2098 if (!eh) {
2099 /* Something wrong with the request, it has no Event hdr */
2100 return PJ_FALSE;
2101 }
2102
2103 if (pj_stricmp(&eh->event_type, &MWI) != 0) {
2104 /* Not MWI event */
2105 return PJ_FALSE;
2106 }
2107
2108 /* Got unsolicited MWI request, respond with 200/OK first */
2109 pjsip_endpt_respond(pjsua_get_pjsip_endpt(), NULL, rdata, 200, NULL,
2110 NULL, NULL, NULL);
2111
2112
2113 /* Call callback */
2114 if (pjsua_var.ua_cfg.cb.on_mwi_info) {
2115 pjsua_acc_id acc_id;
2116 pjsua_mwi_info mwi_info;
2117
2118 acc_id = pjsua_acc_find_for_incoming(rdata);
2119
2120 pj_bzero(&mwi_info, sizeof(mwi_info));
2121 mwi_info.rdata = rdata;
2122
2123 (*pjsua_var.ua_cfg.cb.on_mwi_info)(acc_id, &mwi_info);
2124 }
2125
2126
2127 return PJ_TRUE;
2128}
2129
2130/* The module instance. */
2131static pjsip_module pjsua_unsolicited_mwi_mod =
2132{
2133 NULL, NULL, /* prev, next. */
2134 { "mod-unsolicited-mwi", 19 }, /* Name. */
2135 -1, /* Id */
2136 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
2137 NULL, /* load() */
2138 NULL, /* start() */
2139 NULL, /* stop() */
2140 NULL, /* unload() */
2141 &unsolicited_mwi_on_rx_request, /* on_rx_request() */
2142 NULL, /* on_rx_response() */
2143 NULL, /* on_tx_request. */
2144 NULL, /* on_tx_response() */
2145 NULL, /* on_tsx_state() */
2146};
2147
2148static pj_status_t enable_unsolicited_mwi(void)
2149{
2150 pj_status_t status;
2151
2152 status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
2153 &pjsua_unsolicited_mwi_mod);
2154 if (status != PJ_SUCCESS)
2155 pjsua_perror(THIS_FILE, "Error registering unsolicited MWI module",
2156 status);
2157
2158 return status;
2159}
2160
2161
2162
Benny Prijono4dd961b2009-10-26 11:21:37 +00002163/***************************************************************************/
2164
Benny Prijono7a5f5102007-05-29 00:33:09 +00002165/* Timer callback to re-create client subscription */
2166static void pres_timer_cb(pj_timer_heap_t *th,
2167 pj_timer_entry *entry)
2168{
Benny Prijono53984d12009-04-28 22:19:49 +00002169 unsigned i;
Benny Prijono7a5f5102007-05-29 00:33:09 +00002170 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
2171
Benny Prijono4dd961b2009-10-26 11:21:37 +00002172 entry->id = PJ_FALSE;
2173
2174 /* Retry failed PUBLISH and MWI SUBSCRIBE requests */
Benny Prijono53984d12009-04-28 22:19:49 +00002175 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2176 pjsua_acc *acc = &pjsua_var.acc[i];
Benny Prijono4dd961b2009-10-26 11:21:37 +00002177
2178 /* Retry PUBLISH */
Benny Prijono53984d12009-04-28 22:19:49 +00002179 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
2180 pjsua_pres_init_publish_acc(acc->index);
Benny Prijono53984d12009-04-28 22:19:49 +00002181
Benny Prijono4dd961b2009-10-26 11:21:37 +00002182 /* Re-subscribe MWI subscription if it's terminated prematurely */
2183 if (acc->cfg.mwi_enabled && !acc->mwi_sub)
2184 pjsua_start_mwi(acc);
2185 }
Benny Prijono73bb7232009-10-20 13:56:26 +00002186
2187 /* #937: No need to do bulk client refresh, as buddies have their
2188 * own individual timer now.
2189 */
2190 //refresh_client_subscriptions();
Benny Prijono7a5f5102007-05-29 00:33:09 +00002191
2192 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
2193 entry->id = PJ_TRUE;
2194
Benny Prijonof9c40c32007-06-28 07:20:26 +00002195 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00002196}
2197
Benny Prijono834aee32006-02-19 01:38:06 +00002198
2199/*
2200 * Init presence
2201 */
2202pj_status_t pjsua_pres_init()
2203{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002204 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00002205 pj_status_t status;
2206
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002207 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00002208 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00002209 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
2210 status);
Benny Prijono834aee32006-02-19 01:38:06 +00002211 }
2212
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002213 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
2214 reset_buddy(i);
2215 }
2216
Benny Prijono834aee32006-02-19 01:38:06 +00002217 return status;
2218}
2219
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002220
Benny Prijono834aee32006-02-19 01:38:06 +00002221/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002222 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00002223 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002224pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00002225{
Benny Prijono7a5f5102007-05-29 00:33:09 +00002226 /* Start presence timer to re-subscribe to buddy's presence when
2227 * subscription has failed.
2228 */
2229 if (pjsua_var.pres_timer.id == PJ_FALSE) {
2230 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
2231
2232 pjsua_var.pres_timer.cb = &pres_timer_cb;
2233 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
2234 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00002235 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00002236 }
2237
Benny Prijonofe1bd342009-11-20 23:33:07 +00002238 if (pjsua_var.ua_cfg.enable_unsolicited_mwi) {
2239 pj_status_t status = enable_unsolicited_mwi();
2240 if (status != PJ_SUCCESS)
2241 return status;
2242 }
2243
Benny Prijono9fc735d2006-05-28 14:58:12 +00002244 return PJ_SUCCESS;
2245}
2246
2247
2248/*
Benny Prijono834aee32006-02-19 01:38:06 +00002249 * Shutdown presence.
2250 */
2251void pjsua_pres_shutdown(void)
2252{
Benny Prijono9fc735d2006-05-28 14:58:12 +00002253 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00002254
Benny Prijono384dab42009-10-14 01:58:04 +00002255 PJ_LOG(4,(THIS_FILE, "Shutting down presence.."));
2256
Benny Prijono7a5f5102007-05-29 00:33:09 +00002257 if (pjsua_var.pres_timer.id != 0) {
2258 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
2259 pjsua_var.pres_timer.id = PJ_FALSE;
2260 }
2261
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002262 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2263 if (!pjsua_var.acc[i].valid)
2264 continue;
2265 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002266 }
2267
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002268 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
2269 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00002270 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00002271
Benny Prijono73bb7232009-10-20 13:56:26 +00002272 refresh_client_subscriptions();
2273
2274 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2275 if (pjsua_var.acc[i].valid)
2276 pjsua_pres_update_acc(i, PJ_FALSE);
2277 }
Benny Prijono834aee32006-02-19 01:38:06 +00002278}