blob: 7a6d928cf6172c4af895cb23d13f9778311d4c78 [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono834aee32006-02-19 01:38:06 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijono834aee32006-02-19 01:38:06 +000021
Benny Prijono834aee32006-02-19 01:38:06 +000022
23#define THIS_FILE "pjsua_pres.c"
24
Benny Prijonoa17496a2007-10-31 10:20:31 +000025
26static void subscribe_buddy_presence(unsigned index);
27
28
29/*
30 * Find buddy.
31 */
32static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
33{
34 const pjsip_sip_uri *sip_uri;
35 unsigned i;
36
Benny Prijonof0f8fd12007-11-10 12:05:59 +000037 uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)uri);
Benny Prijonoa17496a2007-10-31 10:20:31 +000038
39 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
40 return PJSUA_INVALID_ID;
41
42 sip_uri = (const pjsip_sip_uri*) uri;
43
44 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
45 const pjsua_buddy *b = &pjsua_var.buddy[i];
46
47 if (!pjsua_buddy_is_valid(i))
48 continue;
49
50 if (pj_stricmp(&sip_uri->user, &b->name)==0 &&
51 pj_stricmp(&sip_uri->host, &b->host)==0 &&
52 (sip_uri->port==(int)b->port || (sip_uri->port==0 && b->port==5060)))
53 {
54 /* Match */
55 return i;
56 }
57 }
58
59 return PJSUA_INVALID_ID;
60}
Benny Prijono7a5f5102007-05-29 00:33:09 +000061
Benny Prijono834aee32006-02-19 01:38:06 +000062
Benny Prijonoeebe9af2006-06-13 22:57:13 +000063/*
64 * Get total number of buddies.
65 */
66PJ_DEF(unsigned) pjsua_get_buddy_count(void)
67{
68 return pjsua_var.buddy_cnt;
69}
Benny Prijono834aee32006-02-19 01:38:06 +000070
Benny Prijonoeebe9af2006-06-13 22:57:13 +000071
72/*
73 * Check if buddy ID is valid.
74 */
75PJ_DEF(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id)
76{
Benny Prijonoa1e69682007-05-11 15:14:34 +000077 return buddy_id>=0 && buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy) &&
Benny Prijonoeebe9af2006-06-13 22:57:13 +000078 pjsua_var.buddy[buddy_id].uri.slen != 0;
79}
80
81
82/*
83 * Enum buddy IDs.
84 */
85PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[],
86 unsigned *count)
87{
88 unsigned i, c;
89
90 PJ_ASSERT_RETURN(ids && count, PJ_EINVAL);
91
92 PJSUA_LOCK();
93
94 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
95 if (!pjsua_var.buddy[i].uri.slen)
96 continue;
97 ids[c] = i;
98 ++c;
99 }
100
101 *count = c;
102
103 PJSUA_UNLOCK();
104
105 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106}
107
108
109/*
110 * Get detailed buddy info.
111 */
112PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
113 pjsua_buddy_info *info)
114{
115 int total=0;
116 pjsua_buddy *buddy;
117
118 PJ_ASSERT_RETURN(buddy_id>=0 &&
119 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
120 PJ_EINVAL);
121
122 PJSUA_LOCK();
123
Benny Prijonoac623b32006-07-03 15:19:31 +0000124 pj_bzero(info, sizeof(pjsua_buddy_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000125
126 buddy = &pjsua_var.buddy[buddy_id];
127 info->id = buddy->index;
128 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
129 PJSUA_UNLOCK();
130 return PJ_SUCCESS;
131 }
132
133 /* uri */
134 info->uri.ptr = info->buf_ + total;
135 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
136 total += info->uri.slen;
137
138 /* contact */
139 info->contact.ptr = info->buf_ + total;
140 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
141 total += info->contact.slen;
Benny Prijono97276602007-06-23 01:07:08 +0000142
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143 /* status and status text */
144 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
145 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
146 info->status_text = pj_str("?");
147 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
148 info->status = PJSUA_BUDDY_STATUS_ONLINE;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000149
150 /* copy RPID information */
151 info->rpid = buddy->status.info[0].rpid;
152
153 if (info->rpid.note.slen)
154 info->status_text = info->rpid.note;
155 else
156 info->status_text = pj_str("Online");
157
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000158 } else {
159 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
160 info->status_text = pj_str("Offline");
161 }
162
163 /* monitor pres */
164 info->monitor_pres = buddy->monitor;
165
166 PJSUA_UNLOCK();
167 return PJ_SUCCESS;
168}
169
170
171/*
172 * Reset buddy descriptor.
173 */
174static void reset_buddy(pjsua_buddy_id id)
175{
Benny Prijonoac623b32006-07-03 15:19:31 +0000176 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000177 pjsua_var.buddy[id].index = id;
178}
179
180
181/*
182 * Add new buddy.
183 */
184PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
185 pjsua_buddy_id *p_buddy_id)
186{
187 pjsip_name_addr *url;
188 pjsip_sip_uri *sip_uri;
189 int index;
190 pj_str_t tmp;
191
192 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
193 PJ_ARRAY_SIZE(pjsua_var.buddy),
194 PJ_ETOOMANY);
195
196 PJSUA_LOCK();
197
198 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000199 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 if (pjsua_var.buddy[index].uri.slen == 0)
201 break;
202 }
203
204 /* Expect to find an empty slot */
205 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
206 PJSUA_UNLOCK();
207 /* This shouldn't happen */
208 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
209 return PJ_ETOOMANY;
210 }
211
212
213 /* Get name and display name for buddy */
214 pj_strdup_with_null(pjsua_var.pool, &tmp, &cfg->uri);
215 url = (pjsip_name_addr*)pjsip_parse_uri(pjsua_var.pool, tmp.ptr, tmp.slen,
216 PJSIP_PARSE_URI_AS_NAMEADDR);
217
218 if (url == NULL) {
219 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
220 PJSUA_UNLOCK();
221 return PJSIP_EINVALIDURI;
222 }
223
Benny Prijonofc493592007-02-18 20:56:32 +0000224 /* Only support SIP schemes */
225 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url))
226 return PJSIP_EINVALIDSCHEME;
227
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 /* Reset buddy, to make sure everything is cleared with default
229 * values
230 */
231 reset_buddy(index);
232
233 /* Save URI */
234 pjsua_var.buddy[index].uri = tmp;
235
Benny Prijono9c1528f2007-02-10 19:22:25 +0000236 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 pjsua_var.buddy[index].name = sip_uri->user;
238 pjsua_var.buddy[index].display = url->display;
239 pjsua_var.buddy[index].host = sip_uri->host;
240 pjsua_var.buddy[index].port = sip_uri->port;
241 pjsua_var.buddy[index].monitor = cfg->subscribe;
242 if (pjsua_var.buddy[index].port == 0)
243 pjsua_var.buddy[index].port = 5060;
244
245 if (p_buddy_id)
246 *p_buddy_id = index;
247
248 pjsua_var.buddy_cnt++;
249
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000250 PJSUA_UNLOCK();
251
Benny Prijonof9c40c32007-06-28 07:20:26 +0000252 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
253
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000254 return PJ_SUCCESS;
255}
256
257
258/*
259 * Delete buddy.
260 */
261PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
262{
263 PJ_ASSERT_RETURN(buddy_id>=0 &&
264 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
265 PJ_EINVAL);
266
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000267 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000268 return PJ_SUCCESS;
269 }
270
271 /* Unsubscribe presence */
272 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
273
Benny Prijonof9c40c32007-06-28 07:20:26 +0000274 PJSUA_LOCK();
275
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000276 /* Remove buddy */
277 pjsua_var.buddy[buddy_id].uri.slen = 0;
278 pjsua_var.buddy_cnt--;
279
280 /* Reset buddy struct */
281 reset_buddy(buddy_id);
282
283 PJSUA_UNLOCK();
284 return PJ_SUCCESS;
285}
286
287
288/*
289 * Enable/disable buddy's presence monitoring.
290 */
291PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
292 pj_bool_t subscribe)
293{
294 pjsua_buddy *buddy;
295
296 PJ_ASSERT_RETURN(buddy_id>=0 &&
297 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
298 PJ_EINVAL);
299
300 PJSUA_LOCK();
301
302 buddy = &pjsua_var.buddy[buddy_id];
303 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000304
305 PJSUA_UNLOCK();
306
Benny Prijonof9c40c32007-06-28 07:20:26 +0000307 pjsua_pres_refresh();
308
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000309 return PJ_SUCCESS;
310}
311
312
313/*
Benny Prijono10861432007-10-31 10:54:53 +0000314 * Update buddy's presence.
315 */
316PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
317{
318 pjsua_buddy *buddy;
319
320 PJ_ASSERT_RETURN(buddy_id>=0 &&
321 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
322 PJ_EINVAL);
323
324 PJSUA_LOCK();
325
326 buddy = &pjsua_var.buddy[buddy_id];
327
328 /* Return error if buddy's presence monitoring is not enabled */
329 if (!buddy->monitor) {
330 PJSUA_UNLOCK();
331 return PJ_EINVALIDOP;
332 }
333
334 /* Ignore if presence is already active for the buddy */
335 if (buddy->sub) {
336 PJSUA_UNLOCK();
337 return PJ_SUCCESS;
338 }
339
340 /* Initiate presence subscription */
341 subscribe_buddy_presence(buddy_id);
342
343 PJSUA_UNLOCK();
344
345 return PJ_SUCCESS;
346}
347
348
349/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000350 * Dump presence subscriptions to log file.
351 */
352PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
353{
354 unsigned acc_id;
355 unsigned i;
356
357
358 PJSUA_LOCK();
359
360 /*
361 * When no detail is required, just dump number of server and client
362 * subscriptions.
363 */
364 if (verbose == PJ_FALSE) {
365
366 int count = 0;
367
368 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
369
370 if (!pjsua_var.acc[acc_id].valid)
371 continue;
372
373 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
374 struct pjsua_srv_pres *uapres;
375
376 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
377 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
378 ++count;
379 uapres = uapres->next;
380 }
381 }
382 }
383
384 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
385 count));
386
387 count = 0;
388
389 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
390 if (pjsua_var.buddy[i].uri.slen == 0)
391 continue;
392 if (pjsua_var.buddy[i].sub) {
393 ++count;
394 }
395 }
396
397 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
398 count));
399 PJSUA_UNLOCK();
400 return;
401 }
402
403
404 /*
405 * Dumping all server (UAS) subscriptions
406 */
407 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
408
409 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
410
411 if (!pjsua_var.acc[acc_id].valid)
412 continue;
413
414 PJ_LOG(3,(THIS_FILE, " %.*s",
415 (int)pjsua_var.acc[acc_id].cfg.id.slen,
416 pjsua_var.acc[acc_id].cfg.id.ptr));
417
418 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
419
420 PJ_LOG(3,(THIS_FILE, " - none - "));
421
422 } else {
423 struct pjsua_srv_pres *uapres;
424
425 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
426 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
427
428 PJ_LOG(3,(THIS_FILE, " %10s %s",
429 pjsip_evsub_get_state_name(uapres->sub),
430 uapres->remote));
431
432 uapres = uapres->next;
433 }
434 }
435 }
436
437 /*
438 * Dumping all client (UAC) subscriptions
439 */
440 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
441
442 if (pjsua_var.buddy_cnt == 0) {
443
444 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
445
446 } else {
447 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
448
449 if (pjsua_var.buddy[i].uri.slen == 0)
450 continue;
451
452 if (pjsua_var.buddy[i].sub) {
453 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
454 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
455 (int)pjsua_var.buddy[i].uri.slen,
456 pjsua_var.buddy[i].uri.ptr));
457 } else {
458 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
459 "(null)",
460 (int)pjsua_var.buddy[i].uri.slen,
461 pjsua_var.buddy[i].uri.ptr));
462 }
463 }
464 }
465
466 PJSUA_UNLOCK();
467}
468
469
470/***************************************************************************
471 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000472 */
473
474/* Proto */
475static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
476
477/* The module instance. */
478static pjsip_module mod_pjsua_pres =
479{
480 NULL, NULL, /* prev, next. */
481 { "mod-pjsua-pres", 14 }, /* Name. */
482 -1, /* Id */
483 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000484 NULL, /* load() */
485 NULL, /* start() */
486 NULL, /* stop() */
487 NULL, /* unload() */
488 &pres_on_rx_request, /* on_rx_request() */
489 NULL, /* on_rx_response() */
490 NULL, /* on_tx_request. */
491 NULL, /* on_tx_response() */
492 NULL, /* on_tsx_state() */
493
494};
495
496
497/* Callback called when *server* subscription state has changed. */
498static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
499{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000501
502 PJ_UNUSED_ARG(event);
503
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 PJSUA_LOCK();
505
Benny Prijonoa1e69682007-05-11 15:14:34 +0000506 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000507 if (uapres) {
508 PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s",
509 uapres->remote, pjsip_evsub_get_state_name(sub)));
510
511 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000512 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000513 pj_list_erase(uapres);
514 }
515 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516
517 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000518}
519
520/* This is called when request is received.
521 * We need to check for incoming SUBSCRIBE request.
522 */
523static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
524{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000525 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000526 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000527 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000528 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
529 pjsua_srv_pres *uapres;
530 pjsip_evsub *sub;
531 pjsip_evsub_user pres_cb;
532 pjsip_tx_data *tdata;
533 pjsip_pres_status pres_status;
534 pjsip_dialog *dlg;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000535 pjsip_expires_hdr *expires_hdr;
536 pjsip_evsub_state ev_state;
Benny Prijonoa17496a2007-10-31 10:20:31 +0000537 pjsua_buddy_id buddy_id;
Benny Prijono834aee32006-02-19 01:38:06 +0000538 pj_status_t status;
539
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000540 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000541 return PJ_FALSE;
542
543 /* Incoming SUBSCRIBE: */
544
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 PJSUA_LOCK();
546
Benny Prijonoa91a0032006-02-26 21:23:45 +0000547 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000548 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000549 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000550
Benny Prijono6f979412006-06-15 12:25:46 +0000551 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
552 acc_id));
553
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000554 /* Create suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000555 if (acc->contact.slen) {
556 contact = acc->contact;
557 } else {
558 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
559 acc_id, rdata);
560 if (status != PJ_SUCCESS) {
561 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
562 status);
563 PJSUA_UNLOCK();
564 return PJ_TRUE;
565 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000566 }
567
Benny Prijono834aee32006-02-19 01:38:06 +0000568 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000569 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000570 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000571 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000572 pjsua_perror(THIS_FILE,
573 "Unable to create UAS dialog for subscription",
574 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000575 PJSUA_UNLOCK();
576 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000577 }
578
Benny Prijono48ab2b72007-11-08 09:24:30 +0000579 /* Set credentials and preference. */
Benny Prijono6f979412006-06-15 12:25:46 +0000580 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000581 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono6f979412006-06-15 12:25:46 +0000582
Benny Prijono834aee32006-02-19 01:38:06 +0000583 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000584 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000585 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
586
587 /* Create server presence subscription: */
588 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
589 if (status != PJ_SUCCESS) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000590 pjsip_dlg_terminate(dlg);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000591 pjsua_perror(THIS_FILE, "Unable to create server subscription",
592 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000593 PJSUA_UNLOCK();
594 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000595 }
596
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000597 /* If account is locked to specific transport, then lock dialog
598 * to this transport too.
599 */
600 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
601 pjsip_tpselector tp_sel;
602
603 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
604 pjsip_dlg_set_transport(dlg, &tp_sel);
605 }
606
Benny Prijono834aee32006-02-19 01:38:06 +0000607 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000608 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000609 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000610 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono834aee32006-02-19 01:38:06 +0000611 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
612 uapres->remote, PJSIP_MAX_URL_SIZE);
613 if (status < 1)
614 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
615 else
616 uapres->remote[status] = '\0';
617
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000618 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000619
620 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000621 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000622
623
624 /* Create and send 200 (OK) to the SUBSCRIBE request: */
625 status = pjsip_pres_accept(sub, rdata, 200, NULL);
626 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000627 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
628 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000629 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000630 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000632 return PJ_FALSE;
633 }
634
635
636 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000637 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000638 pres_status.info_cnt = 1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000639 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +0000640 pres_status.info[0].id = pjsua_var.acc[acc_id].cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000641 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000642 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000643 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono834aee32006-02-19 01:38:06 +0000644
645 pjsip_pres_set_status(sub, &pres_status);
646
Benny Prijonoc61cc042007-06-27 13:01:59 +0000647 /* Check expires value. If it's zero, send our presense state but
648 * set subscription state to TERMINATED.
649 */
650 expires_hdr=(pjsip_expires_hdr*)
651 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
652
653 if (expires_hdr && expires_hdr->ivalue == 0)
654 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
655 else
656 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
657
Benny Prijono834aee32006-02-19 01:38:06 +0000658 /* Create and send the first NOTIFY to active subscription: */
Benny Prijonoc61cc042007-06-27 13:01:59 +0000659 status = pjsip_pres_notify( sub, ev_state, NULL, NULL, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000660 if (status == PJ_SUCCESS) {
661 pjsua_process_msg_data(tdata, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000662 status = pjsip_pres_send_request( sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000663 }
Benny Prijono834aee32006-02-19 01:38:06 +0000664
665 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000666 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
667 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000668 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000669 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000670 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000671 return PJ_FALSE;
672 }
673
674
Benny Prijonoa17496a2007-10-31 10:20:31 +0000675 /* Subscribe to buddy's presence if we're not subscribed */
676 buddy_id = pjsua_find_buddy(dlg->remote.info->uri);
677 if (buddy_id != PJSUA_INVALID_ID) {
678 pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
679 if (b->monitor && b->sub == NULL) {
680 PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
681 "activating outgoing subscription", buddy_id));
682 subscribe_buddy_presence(buddy_id);
683 }
684 }
685
Benny Prijono834aee32006-02-19 01:38:06 +0000686 /* Done: */
687
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 PJSUA_UNLOCK();
689
Benny Prijono834aee32006-02-19 01:38:06 +0000690 return PJ_TRUE;
691}
692
693
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000694/*
695 * Client presence publication callback.
696 */
697static void publish_cb(struct pjsip_publishc_cbparam *param)
698{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000699 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000700
701 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
702 if (param->status != PJ_SUCCESS) {
703 char errmsg[PJ_ERR_MSG_SIZE];
704
705 pj_strerror(param->status, errmsg, sizeof(errmsg));
706 PJ_LOG(1,(THIS_FILE,
707 "Client publication (PUBLISH) failed, status=%d, msg=%s",
708 param->status, errmsg));
709 } else {
710 PJ_LOG(1,(THIS_FILE,
711 "Client publication (PUBLISH) failed (%d/%.*s)",
712 param->code, (int)param->reason.slen,
713 param->reason.ptr));
714 }
715
716 pjsip_publishc_destroy(param->pubc);
717 acc->publish_sess = NULL;
718 }
719}
720
721
722/*
723 * Send PUBLISH request.
724 */
725static pj_status_t send_publish(int acc_id, pj_bool_t active)
726{
727 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
728 pjsua_acc *acc = &pjsua_var.acc[acc_id];
729 pjsip_pres_status pres_status;
730 pjsip_tx_data *tdata;
731 pj_status_t status;
732
733
734 /* Create PUBLISH request */
735 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000736 char *bpos;
737 pj_str_t entity;
738
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000739 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
740 if (status != PJ_SUCCESS) {
741 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
742 goto on_error;
743 }
744
745 /* Set our online status: */
746 pj_bzero(&pres_status, sizeof(pres_status));
747 pres_status.info_cnt = 1;
748 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +0000749 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000750 /* .. including RPID information */
751 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
752 sizeof(pjrpid_element));
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000753
Benny Prijono8c6e8842007-02-24 15:33:54 +0000754 /* Be careful not to send PIDF with presence entity ID containing
755 * "<" character.
756 */
757 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
758 char *epos = pj_strchr(&acc_cfg->id, '>');
759 if (epos - bpos < 2) {
760 pj_assert(!"Unexpected invalid URI");
761 status = PJSIP_EINVALIDURI;
762 goto on_error;
763 }
764 entity.ptr = bpos+1;
765 entity.slen = epos - bpos - 1;
766 } else {
767 entity = acc_cfg->id;
768 }
769
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000770 /* Create and add PIDF message body */
771 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +0000772 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000773 if (status != PJ_SUCCESS) {
774 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
775 status);
776 pjsip_tx_data_dec_ref(tdata);
777 goto on_error;
778 }
779 } else {
780 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
781 if (status != PJ_SUCCESS) {
782 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
783 goto on_error;
784 }
785 }
786
787 /* Add headers etc */
788 pjsua_process_msg_data(tdata, NULL);
789
790 /* Send the PUBLISH request */
791 status = pjsip_publishc_send(acc->publish_sess, tdata);
792 if (status != PJ_SUCCESS) {
793 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
794 goto on_error;
795 }
796
797 acc->publish_state = acc->online_status;
798 return PJ_SUCCESS;
799
800on_error:
Benny Prijono29438152007-06-28 02:47:32 +0000801 if (acc->publish_sess) {
802 pjsip_publishc_destroy(acc->publish_sess);
803 acc->publish_sess = NULL;
804 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000805 return status;
806}
807
808
809/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +0000810pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000811{
812 const pj_str_t STR_PRESENCE = { "presence", 8 };
813 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
814 pjsua_acc *acc = &pjsua_var.acc[acc_id];
815 pj_status_t status;
816
817 /* Create and init client publication session */
818 if (acc_cfg->publish_enabled) {
819
820 /* Create client publication */
821 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
822 &acc->publish_sess);
823 if (status != PJ_SUCCESS) {
824 acc->publish_sess = NULL;
825 return status;
826 }
827
828 /* Initialize client publication */
829 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
830 &acc_cfg->id, &acc_cfg->id,
831 &acc_cfg->id,
Benny Prijono32767ec2007-11-07 03:45:03 +0000832 PJSUA_PRES_TIMER);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000833 if (status != PJ_SUCCESS) {
834 acc->publish_sess = NULL;
835 return status;
836 }
837
Benny Prijono703b7d72007-03-20 09:13:24 +0000838 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +0000839 if (acc->cred_cnt) {
840 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
841 acc->cred);
842 }
Benny Prijono703b7d72007-03-20 09:13:24 +0000843
844 /* Set route-set */
845 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
846
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000847 /* Send initial PUBLISH request */
848 if (acc->online_status != 0) {
849 status = send_publish(acc_id, PJ_TRUE);
850 if (status != PJ_SUCCESS)
851 return status;
852 }
853
854 } else {
855 acc->publish_sess = NULL;
856 }
857
858 return PJ_SUCCESS;
859}
860
861
862/* Init presence for account */
863pj_status_t pjsua_pres_init_acc(int acc_id)
864{
865 pjsua_acc *acc = &pjsua_var.acc[acc_id];
866
867 /* Init presence subscription */
868 pj_list_init(&acc->pres_srv_list);
869
Benny Prijono8b6834f2007-02-24 13:29:22 +0000870 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000871}
872
873
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000874/* Terminate server subscription for the account */
875void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +0000876{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000877 pjsua_acc *acc = &pjsua_var.acc[acc_id];
878 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +0000879 pjsua_srv_pres *uapres;
880
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000881 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +0000882
Benny Prijono922933b2007-01-21 16:23:56 +0000883 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000884 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000885
886 pjsip_pres_status pres_status;
887 pj_str_t reason = { "noresource", 10 };
888 pjsip_tx_data *tdata;
889
890 pjsip_pres_get_status(uapres->sub, &pres_status);
891
892 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
893 pjsip_pres_set_status(uapres->sub, &pres_status);
894
895 if (pjsip_pres_notify(uapres->sub,
896 PJSIP_EVSUB_STATE_TERMINATED, NULL,
897 &reason, &tdata)==PJ_SUCCESS)
898 {
899 pjsip_pres_send_request(uapres->sub, tdata);
900 }
901
902 uapres = uapres->next;
903 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000904
Benny Prijono922933b2007-01-21 16:23:56 +0000905 /* Clear server presence subscription list because account might be reused
906 * later. */
907 pj_list_init(&acc->pres_srv_list);
908
909 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000910 if (acc->publish_sess) {
911 acc->online_status = PJ_FALSE;
912 send_publish(acc_id, PJ_FALSE);
913 if (acc->publish_sess) {
914 pjsip_publishc_destroy(acc->publish_sess);
915 acc->publish_sess = NULL;
916 }
917 acc_cfg->publish_enabled = PJ_FALSE;
918 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000919}
920
921
Benny Prijono4461c7d2007-08-25 13:36:15 +0000922/* Update server subscription (e.g. when our online status has changed) */
923void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000924{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000925 pjsua_acc *acc = &pjsua_var.acc[acc_id];
926 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000927 pjsua_srv_pres *uapres;
928
929 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
930
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000931 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +0000932
933 pjsip_pres_status pres_status;
934 pjsip_tx_data *tdata;
935
936 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono4461c7d2007-08-25 13:36:15 +0000937 if (force || pres_status.info[0].basic_open != acc->online_status) {
938
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000939 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +0000940 pj_memcpy(&pres_status.info[0].rpid, &acc->rpid,
941 sizeof(pjrpid_element));
942
Benny Prijono834aee32006-02-19 01:38:06 +0000943 pjsip_pres_set_status(uapres->sub, &pres_status);
944
Benny Prijono21b9ad92006-08-15 13:11:22 +0000945 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
946 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000947 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000948 }
Benny Prijono834aee32006-02-19 01:38:06 +0000949 }
950
951 uapres = uapres->next;
952 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000953
Benny Prijono8b6834f2007-02-24 13:29:22 +0000954 /* Send PUBLISH if required. We only do this when we have a PUBLISH
955 * session. If we don't have a PUBLISH session, then it could be
956 * that we're waiting until registration has completed before we
957 * send the first PUBLISH.
958 */
959 if (acc_cfg->publish_enabled && acc->publish_sess) {
Benny Prijono4461c7d2007-08-25 13:36:15 +0000960 if (force || acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000961 send_publish(acc_id, PJ_TRUE);
962 }
963 }
Benny Prijono834aee32006-02-19 01:38:06 +0000964}
965
966
967
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000968/***************************************************************************
969 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000970 */
971
972/* Callback called when *client* subscription state has changed. */
973static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
974{
975 pjsua_buddy *buddy;
976
977 PJ_UNUSED_ARG(event);
978
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000979 PJSUA_LOCK();
980
Benny Prijonoa1e69682007-05-11 15:14:34 +0000981 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000982 if (buddy) {
983 PJ_LOG(3,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000984 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000985 (int)pjsua_var.buddy[buddy->index].uri.slen,
986 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +0000987 pjsip_evsub_get_state_name(sub)));
988
989 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
990 buddy->sub = NULL;
991 buddy->status.info_cnt = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000992 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000993 }
Benny Prijono9fc735d2006-05-28 14:58:12 +0000994
995 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000996 if (pjsua_var.ua_cfg.cb.on_buddy_state)
997 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono834aee32006-02-19 01:38:06 +0000998 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000999
1000 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001001}
1002
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001003
1004/* Callback when transaction state has changed. */
1005static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
1006 pjsip_transaction *tsx,
1007 pjsip_event *event)
1008{
1009 pjsua_buddy *buddy;
1010 pjsip_contact_hdr *contact_hdr;
1011
1012 PJSUA_LOCK();
1013
Benny Prijonoa1e69682007-05-11 15:14:34 +00001014 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001015 if (!buddy) {
1016 PJSUA_UNLOCK();
1017 return;
1018 }
1019
1020 /* We only use this to update buddy's Contact, when it's not
1021 * set.
1022 */
1023 if (buddy->contact.slen != 0) {
1024 /* Contact already set */
1025 PJSUA_UNLOCK();
1026 return;
1027 }
1028
1029 /* Only care about 2xx response to outgoing SUBSCRIBE */
1030 if (tsx->status_code/100 != 2 ||
1031 tsx->role != PJSIP_UAC_ROLE ||
1032 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001033 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001034 {
1035 PJSUA_UNLOCK();
1036 return;
1037 }
1038
1039 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001040 contact_hdr = (pjsip_contact_hdr*)
1041 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001042 PJSIP_H_CONTACT, NULL);
1043 if (!contact_hdr) {
1044 PJSUA_UNLOCK();
1045 return;
1046 }
1047
Benny Prijonoa1e69682007-05-11 15:14:34 +00001048 buddy->contact.ptr = (char*)
1049 pj_pool_alloc(pjsua_var.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001050 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
1051 contact_hdr->uri,
1052 buddy->contact.ptr,
1053 PJSIP_MAX_URL_SIZE);
1054 if (buddy->contact.slen < 0)
1055 buddy->contact.slen = 0;
1056
1057 PJSUA_UNLOCK();
1058}
1059
1060
Benny Prijono834aee32006-02-19 01:38:06 +00001061/* Callback called when we receive NOTIFY */
1062static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
1063 pjsip_rx_data *rdata,
1064 int *p_st_code,
1065 pj_str_t **p_st_text,
1066 pjsip_hdr *res_hdr,
1067 pjsip_msg_body **p_body)
1068{
1069 pjsua_buddy *buddy;
1070
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001071 PJSUA_LOCK();
1072
Benny Prijonoa1e69682007-05-11 15:14:34 +00001073 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +00001074 if (buddy) {
1075 /* Update our info. */
1076 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +00001077 }
1078
1079 /* The default is to send 200 response to NOTIFY.
1080 * Just leave it there..
1081 */
1082 PJ_UNUSED_ARG(rdata);
1083 PJ_UNUSED_ARG(p_st_code);
1084 PJ_UNUSED_ARG(p_st_text);
1085 PJ_UNUSED_ARG(res_hdr);
1086 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001087
1088 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +00001089}
1090
1091
1092/* Event subscription callback. */
1093static pjsip_evsub_user pres_callback =
1094{
1095 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001096 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +00001097
1098 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
1099 * we want to authenticate
1100 */
1101
1102 &pjsua_evsub_on_rx_notify,
1103
1104 NULL, /* on_client_refresh: Use default behaviour, which is to
1105 * refresh client subscription. */
1106
1107 NULL, /* on_server_timeout: Use default behaviour, which is to send
1108 * NOTIFY to terminate.
1109 */
1110};
1111
1112
1113/* It does what it says.. */
1114static void subscribe_buddy_presence(unsigned index)
1115{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001116 pjsua_buddy *buddy;
1117 int acc_id;
1118 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001119 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001120 pjsip_tx_data *tdata;
1121 pj_status_t status;
1122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001123 buddy = &pjsua_var.buddy[index];
1124 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001125
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001126 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001127
Benny Prijonob4a17c92006-07-10 14:40:21 +00001128 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1129 acc_id, index));
1130
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001131 /* Generate suitable Contact header unless one is already set in
1132 * the account
1133 */
1134 if (acc->contact.slen) {
1135 contact = acc->contact;
1136 } else {
1137 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
1138 acc_id, &buddy->uri);
1139 if (status != PJ_SUCCESS) {
1140 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1141 status);
1142 return;
1143 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001144 }
1145
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001146 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001147 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001148 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001149 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001150 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001151 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001152 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001153 pjsua_perror(THIS_FILE, "Unable to create dialog",
1154 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001155 return;
1156 }
1157
Benny Prijonof9c40c32007-06-28 07:20:26 +00001158 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001159 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1160 if (status != PJ_SUCCESS) {
1161 pjsua_var.buddy[index].sub = NULL;
1162 pjsua_perror(THIS_FILE, "Unable to create presence client",
1163 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001164 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001165 return;
1166 }
1167
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001168 /* If account is locked to specific transport, then lock dialog
1169 * to this transport too.
1170 */
1171 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1172 pjsip_tpselector tp_sel;
1173
1174 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001175 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001176 }
1177
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001178 /* Set route-set */
1179 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001180 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001181 }
1182
1183 /* Set credentials */
1184 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001185 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001186 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001187 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001188
Benny Prijono48ab2b72007-11-08 09:24:30 +00001189 /* Set authentication preference */
1190 pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
1191
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001192 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001193
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001194 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001195 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001196 if (buddy->sub) {
1197 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1198 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001199 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001200 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1201 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001202 return;
1203 }
1204
Benny Prijono21b9ad92006-08-15 13:11:22 +00001205 pjsua_process_msg_data(tdata, NULL);
1206
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001207 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001208 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001209 if (buddy->sub) {
1210 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1211 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001212 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001213 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1214 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001215 return;
1216 }
Benny Prijono834aee32006-02-19 01:38:06 +00001217}
1218
1219
1220/* It does what it says... */
1221static void unsubscribe_buddy_presence(unsigned index)
1222{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001223 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001224 pjsip_tx_data *tdata;
1225 pj_status_t status;
1226
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001227 buddy = &pjsua_var.buddy[index];
1228
1229 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001230 return;
1231
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001232 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1233 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001234 return;
1235 }
1236
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001238 if (status == PJ_SUCCESS) {
1239 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001240 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001241 }
Benny Prijono834aee32006-02-19 01:38:06 +00001242
Benny Prijono48da92e2007-05-16 08:56:30 +00001243 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1245 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001246 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1247 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001248 }
1249}
1250
1251
Benny Prijonof9c40c32007-06-28 07:20:26 +00001252/* Lock all buddies */
1253#define LOCK_BUDDIES unsigned cnt_ = 0; \
1254 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1255 unsigned i_; \
1256 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1257 if (pjsua_var.buddy[i_].sub) { \
1258 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1259 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1260 } \
1261 } \
1262 PJSUA_LOCK();
1263
1264/* Unlock all buddies */
1265#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1266 for (i_=0; i_<cnt_; ++i_) { \
1267 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1268 }
1269
1270
1271
Benny Prijono834aee32006-02-19 01:38:06 +00001272/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001273static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001274{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001275 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001276
Benny Prijonof9c40c32007-06-28 07:20:26 +00001277 LOCK_BUDDIES;
1278
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001279 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001280
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001281 if (!pjsua_var.buddy[i].uri.slen)
1282 continue;
1283
1284 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001285 subscribe_buddy_presence(i);
1286
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001287 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001288 unsubscribe_buddy_presence(i);
1289
1290 }
1291 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001292
1293 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001294}
1295
Benny Prijono7a5f5102007-05-29 00:33:09 +00001296/* Timer callback to re-create client subscription */
1297static void pres_timer_cb(pj_timer_heap_t *th,
1298 pj_timer_entry *entry)
1299{
1300 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1301
Benny Prijono7a5f5102007-05-29 00:33:09 +00001302 entry->id = PJ_FALSE;
1303 refresh_client_subscriptions();
1304
1305 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1306 entry->id = PJ_TRUE;
1307
Benny Prijonof9c40c32007-06-28 07:20:26 +00001308 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001309}
1310
Benny Prijono834aee32006-02-19 01:38:06 +00001311
1312/*
1313 * Init presence
1314 */
1315pj_status_t pjsua_pres_init()
1316{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001317 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001318 pj_status_t status;
1319
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001321 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001322 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1323 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001324 }
1325
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001326 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1327 reset_buddy(i);
1328 }
1329
Benny Prijono834aee32006-02-19 01:38:06 +00001330 return status;
1331}
1332
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001333
Benny Prijono834aee32006-02-19 01:38:06 +00001334/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001335 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001336 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001337pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001338{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001339 /* Start presence timer to re-subscribe to buddy's presence when
1340 * subscription has failed.
1341 */
1342 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1343 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1344
1345 pjsua_var.pres_timer.cb = &pres_timer_cb;
1346 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1347 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001348 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001349 }
1350
Benny Prijono9fc735d2006-05-28 14:58:12 +00001351 return PJ_SUCCESS;
1352}
1353
1354
1355/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001356 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001357 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001358void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001359{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001360 unsigned i;
1361
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001362 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001363
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001364 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1365 if (pjsua_var.acc[i].valid)
Benny Prijono4461c7d2007-08-25 13:36:15 +00001366 pjsua_pres_update_acc(i, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367 }
Benny Prijono834aee32006-02-19 01:38:06 +00001368}
1369
1370
1371/*
1372 * Shutdown presence.
1373 */
1374void pjsua_pres_shutdown(void)
1375{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001376 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001377
Benny Prijono7a5f5102007-05-29 00:33:09 +00001378 if (pjsua_var.pres_timer.id != 0) {
1379 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1380 pjsua_var.pres_timer.id = PJ_FALSE;
1381 }
1382
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1384 if (!pjsua_var.acc[i].valid)
1385 continue;
1386 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001387 }
1388
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1390 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001391 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001392
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001393 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001394}