blob: 963db29ce31e6b021ff9f0dd234dbe7ce8ac5b98 [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 Prijono7a5f5102007-05-29 00:33:09 +000025#ifndef PJSUA_PRES_TIMER
Benny Prijono21576662007-07-18 02:04:56 +000026# define PJSUA_PRES_TIMER 120
Benny Prijono7a5f5102007-05-29 00:33:09 +000027#endif
28
Benny Prijono834aee32006-02-19 01:38:06 +000029
Benny Prijonoeebe9af2006-06-13 22:57:13 +000030/*
31 * Get total number of buddies.
32 */
33PJ_DEF(unsigned) pjsua_get_buddy_count(void)
34{
35 return pjsua_var.buddy_cnt;
36}
Benny Prijono834aee32006-02-19 01:38:06 +000037
Benny Prijonoeebe9af2006-06-13 22:57:13 +000038
39/*
40 * Check if buddy ID is valid.
41 */
42PJ_DEF(pj_bool_t) pjsua_buddy_is_valid(pjsua_buddy_id buddy_id)
43{
Benny Prijonoa1e69682007-05-11 15:14:34 +000044 return buddy_id>=0 && buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy) &&
Benny Prijonoeebe9af2006-06-13 22:57:13 +000045 pjsua_var.buddy[buddy_id].uri.slen != 0;
46}
47
48
49/*
50 * Enum buddy IDs.
51 */
52PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[],
53 unsigned *count)
54{
55 unsigned i, c;
56
57 PJ_ASSERT_RETURN(ids && count, PJ_EINVAL);
58
59 PJSUA_LOCK();
60
61 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
62 if (!pjsua_var.buddy[i].uri.slen)
63 continue;
64 ids[c] = i;
65 ++c;
66 }
67
68 *count = c;
69
70 PJSUA_UNLOCK();
71
72 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000073}
74
75
76/*
77 * Get detailed buddy info.
78 */
79PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
80 pjsua_buddy_info *info)
81{
82 int total=0;
83 pjsua_buddy *buddy;
84
85 PJ_ASSERT_RETURN(buddy_id>=0 &&
86 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
87 PJ_EINVAL);
88
89 PJSUA_LOCK();
90
Benny Prijonoac623b32006-07-03 15:19:31 +000091 pj_bzero(info, sizeof(pjsua_buddy_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +000092
93 buddy = &pjsua_var.buddy[buddy_id];
94 info->id = buddy->index;
95 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
96 PJSUA_UNLOCK();
97 return PJ_SUCCESS;
98 }
99
100 /* uri */
101 info->uri.ptr = info->buf_ + total;
102 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
103 total += info->uri.slen;
104
105 /* contact */
106 info->contact.ptr = info->buf_ + total;
107 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
108 total += info->contact.slen;
Benny Prijono97276602007-06-23 01:07:08 +0000109
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 /* status and status text */
111 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
112 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
113 info->status_text = pj_str("?");
114 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
115 info->status = PJSUA_BUDDY_STATUS_ONLINE;
116 info->status_text = pj_str("Online");
117 } else {
118 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
119 info->status_text = pj_str("Offline");
120 }
121
122 /* monitor pres */
123 info->monitor_pres = buddy->monitor;
124
125 PJSUA_UNLOCK();
126 return PJ_SUCCESS;
127}
128
129
130/*
131 * Reset buddy descriptor.
132 */
133static void reset_buddy(pjsua_buddy_id id)
134{
Benny Prijonoac623b32006-07-03 15:19:31 +0000135 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 pjsua_var.buddy[id].index = id;
137}
138
139
140/*
141 * Add new buddy.
142 */
143PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
144 pjsua_buddy_id *p_buddy_id)
145{
146 pjsip_name_addr *url;
147 pjsip_sip_uri *sip_uri;
148 int index;
149 pj_str_t tmp;
150
151 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
152 PJ_ARRAY_SIZE(pjsua_var.buddy),
153 PJ_ETOOMANY);
154
155 PJSUA_LOCK();
156
157 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000158 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 if (pjsua_var.buddy[index].uri.slen == 0)
160 break;
161 }
162
163 /* Expect to find an empty slot */
164 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
165 PJSUA_UNLOCK();
166 /* This shouldn't happen */
167 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
168 return PJ_ETOOMANY;
169 }
170
171
172 /* Get name and display name for buddy */
173 pj_strdup_with_null(pjsua_var.pool, &tmp, &cfg->uri);
174 url = (pjsip_name_addr*)pjsip_parse_uri(pjsua_var.pool, tmp.ptr, tmp.slen,
175 PJSIP_PARSE_URI_AS_NAMEADDR);
176
177 if (url == NULL) {
178 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
179 PJSUA_UNLOCK();
180 return PJSIP_EINVALIDURI;
181 }
182
Benny Prijonofc493592007-02-18 20:56:32 +0000183 /* Only support SIP schemes */
184 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url))
185 return PJSIP_EINVALIDSCHEME;
186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 /* Reset buddy, to make sure everything is cleared with default
188 * values
189 */
190 reset_buddy(index);
191
192 /* Save URI */
193 pjsua_var.buddy[index].uri = tmp;
194
Benny Prijono9c1528f2007-02-10 19:22:25 +0000195 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 pjsua_var.buddy[index].name = sip_uri->user;
197 pjsua_var.buddy[index].display = url->display;
198 pjsua_var.buddy[index].host = sip_uri->host;
199 pjsua_var.buddy[index].port = sip_uri->port;
200 pjsua_var.buddy[index].monitor = cfg->subscribe;
201 if (pjsua_var.buddy[index].port == 0)
202 pjsua_var.buddy[index].port = 5060;
203
204 if (p_buddy_id)
205 *p_buddy_id = index;
206
207 pjsua_var.buddy_cnt++;
208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 PJSUA_UNLOCK();
210
Benny Prijonof9c40c32007-06-28 07:20:26 +0000211 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 return PJ_SUCCESS;
214}
215
216
217/*
218 * Delete buddy.
219 */
220PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
221{
222 PJ_ASSERT_RETURN(buddy_id>=0 &&
223 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
224 PJ_EINVAL);
225
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000226 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 return PJ_SUCCESS;
228 }
229
230 /* Unsubscribe presence */
231 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
232
Benny Prijonof9c40c32007-06-28 07:20:26 +0000233 PJSUA_LOCK();
234
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235 /* Remove buddy */
236 pjsua_var.buddy[buddy_id].uri.slen = 0;
237 pjsua_var.buddy_cnt--;
238
239 /* Reset buddy struct */
240 reset_buddy(buddy_id);
241
242 PJSUA_UNLOCK();
243 return PJ_SUCCESS;
244}
245
246
247/*
248 * Enable/disable buddy's presence monitoring.
249 */
250PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
251 pj_bool_t subscribe)
252{
253 pjsua_buddy *buddy;
254
255 PJ_ASSERT_RETURN(buddy_id>=0 &&
256 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
257 PJ_EINVAL);
258
259 PJSUA_LOCK();
260
261 buddy = &pjsua_var.buddy[buddy_id];
262 buddy->monitor = subscribe;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000263
264 PJSUA_UNLOCK();
265
Benny Prijonof9c40c32007-06-28 07:20:26 +0000266 pjsua_pres_refresh();
267
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000268 return PJ_SUCCESS;
269}
270
271
272/*
273 * Dump presence subscriptions to log file.
274 */
275PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
276{
277 unsigned acc_id;
278 unsigned i;
279
280
281 PJSUA_LOCK();
282
283 /*
284 * When no detail is required, just dump number of server and client
285 * subscriptions.
286 */
287 if (verbose == PJ_FALSE) {
288
289 int count = 0;
290
291 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
292
293 if (!pjsua_var.acc[acc_id].valid)
294 continue;
295
296 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
297 struct pjsua_srv_pres *uapres;
298
299 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
300 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
301 ++count;
302 uapres = uapres->next;
303 }
304 }
305 }
306
307 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
308 count));
309
310 count = 0;
311
312 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
313 if (pjsua_var.buddy[i].uri.slen == 0)
314 continue;
315 if (pjsua_var.buddy[i].sub) {
316 ++count;
317 }
318 }
319
320 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
321 count));
322 PJSUA_UNLOCK();
323 return;
324 }
325
326
327 /*
328 * Dumping all server (UAS) subscriptions
329 */
330 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
331
332 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
333
334 if (!pjsua_var.acc[acc_id].valid)
335 continue;
336
337 PJ_LOG(3,(THIS_FILE, " %.*s",
338 (int)pjsua_var.acc[acc_id].cfg.id.slen,
339 pjsua_var.acc[acc_id].cfg.id.ptr));
340
341 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
342
343 PJ_LOG(3,(THIS_FILE, " - none - "));
344
345 } else {
346 struct pjsua_srv_pres *uapres;
347
348 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
349 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
350
351 PJ_LOG(3,(THIS_FILE, " %10s %s",
352 pjsip_evsub_get_state_name(uapres->sub),
353 uapres->remote));
354
355 uapres = uapres->next;
356 }
357 }
358 }
359
360 /*
361 * Dumping all client (UAC) subscriptions
362 */
363 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
364
365 if (pjsua_var.buddy_cnt == 0) {
366
367 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
368
369 } else {
370 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
371
372 if (pjsua_var.buddy[i].uri.slen == 0)
373 continue;
374
375 if (pjsua_var.buddy[i].sub) {
376 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
377 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
378 (int)pjsua_var.buddy[i].uri.slen,
379 pjsua_var.buddy[i].uri.ptr));
380 } else {
381 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
382 "(null)",
383 (int)pjsua_var.buddy[i].uri.slen,
384 pjsua_var.buddy[i].uri.ptr));
385 }
386 }
387 }
388
389 PJSUA_UNLOCK();
390}
391
392
393/***************************************************************************
394 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000395 */
396
397/* Proto */
398static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
399
400/* The module instance. */
401static pjsip_module mod_pjsua_pres =
402{
403 NULL, NULL, /* prev, next. */
404 { "mod-pjsua-pres", 14 }, /* Name. */
405 -1, /* Id */
406 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000407 NULL, /* load() */
408 NULL, /* start() */
409 NULL, /* stop() */
410 NULL, /* unload() */
411 &pres_on_rx_request, /* on_rx_request() */
412 NULL, /* on_rx_response() */
413 NULL, /* on_tx_request. */
414 NULL, /* on_tx_response() */
415 NULL, /* on_tsx_state() */
416
417};
418
419
420/* Callback called when *server* subscription state has changed. */
421static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
422{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000424
425 PJ_UNUSED_ARG(event);
426
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 PJSUA_LOCK();
428
Benny Prijonoa1e69682007-05-11 15:14:34 +0000429 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000430 if (uapres) {
431 PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s",
432 uapres->remote, pjsip_evsub_get_state_name(sub)));
433
434 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000436 pj_list_erase(uapres);
437 }
438 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000439
440 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000441}
442
443/* This is called when request is received.
444 * We need to check for incoming SUBSCRIBE request.
445 */
446static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
447{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000448 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000449 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000450 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000451 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
452 pjsua_srv_pres *uapres;
453 pjsip_evsub *sub;
454 pjsip_evsub_user pres_cb;
455 pjsip_tx_data *tdata;
456 pjsip_pres_status pres_status;
457 pjsip_dialog *dlg;
Benny Prijonoc61cc042007-06-27 13:01:59 +0000458 pjsip_expires_hdr *expires_hdr;
459 pjsip_evsub_state ev_state;
Benny Prijono834aee32006-02-19 01:38:06 +0000460 pj_status_t status;
461
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000462 if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
Benny Prijono834aee32006-02-19 01:38:06 +0000463 return PJ_FALSE;
464
465 /* Incoming SUBSCRIBE: */
466
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 PJSUA_LOCK();
468
Benny Prijonoa91a0032006-02-26 21:23:45 +0000469 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000470 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000471 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000472
Benny Prijono6f979412006-06-15 12:25:46 +0000473 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
474 acc_id));
475
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000476 /* Create suitable Contact header */
477 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
478 acc_id, rdata);
479 if (status != PJ_SUCCESS) {
480 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
481 PJSUA_UNLOCK();
482 return PJ_TRUE;
483 }
484
Benny Prijono834aee32006-02-19 01:38:06 +0000485 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000486 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000487 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000488 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000489 pjsua_perror(THIS_FILE,
490 "Unable to create UAS dialog for subscription",
491 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000492 PJSUA_UNLOCK();
493 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000494 }
495
Benny Prijono6f979412006-06-15 12:25:46 +0000496 /* Set credentials. */
497 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
498
Benny Prijono834aee32006-02-19 01:38:06 +0000499 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000500 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000501 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
502
503 /* Create server presence subscription: */
504 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
505 if (status != PJ_SUCCESS) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000506 pjsip_dlg_terminate(dlg);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000507 pjsua_perror(THIS_FILE, "Unable to create server subscription",
508 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000509 PJSUA_UNLOCK();
510 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000511 }
512
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000513 /* If account is locked to specific transport, then lock dialog
514 * to this transport too.
515 */
516 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
517 pjsip_tpselector tp_sel;
518
519 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
520 pjsip_dlg_set_transport(dlg, &tp_sel);
521 }
522
Benny Prijono834aee32006-02-19 01:38:06 +0000523 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000524 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000525 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000526 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono834aee32006-02-19 01:38:06 +0000527 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
528 uapres->remote, PJSIP_MAX_URL_SIZE);
529 if (status < 1)
530 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
531 else
532 uapres->remote[status] = '\0';
533
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000534 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000535
536 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000537 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000538
539
540 /* Create and send 200 (OK) to the SUBSCRIBE request: */
541 status = pjsip_pres_accept(sub, rdata, 200, NULL);
542 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000543 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
544 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000545 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000546 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000548 return PJ_FALSE;
549 }
550
551
552 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000553 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000554 pres_status.info_cnt = 1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000555 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
556 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000557 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono834aee32006-02-19 01:38:06 +0000559
560 pjsip_pres_set_status(sub, &pres_status);
561
Benny Prijonoc61cc042007-06-27 13:01:59 +0000562 /* Check expires value. If it's zero, send our presense state but
563 * set subscription state to TERMINATED.
564 */
565 expires_hdr=(pjsip_expires_hdr*)
566 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
567
568 if (expires_hdr && expires_hdr->ivalue == 0)
569 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
570 else
571 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
572
Benny Prijono834aee32006-02-19 01:38:06 +0000573 /* Create and send the first NOTIFY to active subscription: */
Benny Prijonoc61cc042007-06-27 13:01:59 +0000574 status = pjsip_pres_notify( sub, ev_state, NULL, NULL, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000575 if (status == PJ_SUCCESS) {
576 pjsua_process_msg_data(tdata, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000577 status = pjsip_pres_send_request( sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000578 }
Benny Prijono834aee32006-02-19 01:38:06 +0000579
580 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000581 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
582 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000583 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000584 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000585 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000586 return PJ_FALSE;
587 }
588
589
590 /* Done: */
591
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000592 PJSUA_UNLOCK();
593
Benny Prijono834aee32006-02-19 01:38:06 +0000594 return PJ_TRUE;
595}
596
597
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000598/*
599 * Client presence publication callback.
600 */
601static void publish_cb(struct pjsip_publishc_cbparam *param)
602{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000603 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000604
605 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
606 if (param->status != PJ_SUCCESS) {
607 char errmsg[PJ_ERR_MSG_SIZE];
608
609 pj_strerror(param->status, errmsg, sizeof(errmsg));
610 PJ_LOG(1,(THIS_FILE,
611 "Client publication (PUBLISH) failed, status=%d, msg=%s",
612 param->status, errmsg));
613 } else {
614 PJ_LOG(1,(THIS_FILE,
615 "Client publication (PUBLISH) failed (%d/%.*s)",
616 param->code, (int)param->reason.slen,
617 param->reason.ptr));
618 }
619
620 pjsip_publishc_destroy(param->pubc);
621 acc->publish_sess = NULL;
622 }
623}
624
625
626/*
627 * Send PUBLISH request.
628 */
629static pj_status_t send_publish(int acc_id, pj_bool_t active)
630{
631 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
632 pjsua_acc *acc = &pjsua_var.acc[acc_id];
633 pjsip_pres_status pres_status;
634 pjsip_tx_data *tdata;
635 pj_status_t status;
636
637
638 /* Create PUBLISH request */
639 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000640 char *bpos;
641 pj_str_t entity;
642
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000643 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
644 if (status != PJ_SUCCESS) {
645 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
646 goto on_error;
647 }
648
649 /* Set our online status: */
650 pj_bzero(&pres_status, sizeof(pres_status));
651 pres_status.info_cnt = 1;
652 pres_status.info[0].basic_open = acc->online_status;
653
Benny Prijono8c6e8842007-02-24 15:33:54 +0000654 /* Be careful not to send PIDF with presence entity ID containing
655 * "<" character.
656 */
657 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
658 char *epos = pj_strchr(&acc_cfg->id, '>');
659 if (epos - bpos < 2) {
660 pj_assert(!"Unexpected invalid URI");
661 status = PJSIP_EINVALIDURI;
662 goto on_error;
663 }
664 entity.ptr = bpos+1;
665 entity.slen = epos - bpos - 1;
666 } else {
667 entity = acc_cfg->id;
668 }
669
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000670 /* Create and add PIDF message body */
671 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +0000672 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000673 if (status != PJ_SUCCESS) {
674 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
675 status);
676 pjsip_tx_data_dec_ref(tdata);
677 goto on_error;
678 }
679 } else {
680 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
681 if (status != PJ_SUCCESS) {
682 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
683 goto on_error;
684 }
685 }
686
687 /* Add headers etc */
688 pjsua_process_msg_data(tdata, NULL);
689
690 /* Send the PUBLISH request */
691 status = pjsip_publishc_send(acc->publish_sess, tdata);
692 if (status != PJ_SUCCESS) {
693 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
694 goto on_error;
695 }
696
697 acc->publish_state = acc->online_status;
698 return PJ_SUCCESS;
699
700on_error:
Benny Prijono29438152007-06-28 02:47:32 +0000701 if (acc->publish_sess) {
702 pjsip_publishc_destroy(acc->publish_sess);
703 acc->publish_sess = NULL;
704 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000705 return status;
706}
707
708
709/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +0000710pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000711{
712 const pj_str_t STR_PRESENCE = { "presence", 8 };
713 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
714 pjsua_acc *acc = &pjsua_var.acc[acc_id];
715 pj_status_t status;
716
717 /* Create and init client publication session */
718 if (acc_cfg->publish_enabled) {
719
720 /* Create client publication */
721 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
722 &acc->publish_sess);
723 if (status != PJ_SUCCESS) {
724 acc->publish_sess = NULL;
725 return status;
726 }
727
728 /* Initialize client publication */
729 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
730 &acc_cfg->id, &acc_cfg->id,
731 &acc_cfg->id,
Benny Prijono703b7d72007-03-20 09:13:24 +0000732 60);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000733 if (status != PJ_SUCCESS) {
734 acc->publish_sess = NULL;
735 return status;
736 }
737
Benny Prijono703b7d72007-03-20 09:13:24 +0000738 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +0000739 if (acc->cred_cnt) {
740 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
741 acc->cred);
742 }
Benny Prijono703b7d72007-03-20 09:13:24 +0000743
744 /* Set route-set */
745 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
746
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000747 /* Send initial PUBLISH request */
748 if (acc->online_status != 0) {
749 status = send_publish(acc_id, PJ_TRUE);
750 if (status != PJ_SUCCESS)
751 return status;
752 }
753
754 } else {
755 acc->publish_sess = NULL;
756 }
757
758 return PJ_SUCCESS;
759}
760
761
762/* Init presence for account */
763pj_status_t pjsua_pres_init_acc(int acc_id)
764{
765 pjsua_acc *acc = &pjsua_var.acc[acc_id];
766
767 /* Init presence subscription */
768 pj_list_init(&acc->pres_srv_list);
769
Benny Prijono8b6834f2007-02-24 13:29:22 +0000770 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000771}
772
773
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000774/* Terminate server subscription for the account */
775void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +0000776{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000777 pjsua_acc *acc = &pjsua_var.acc[acc_id];
778 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +0000779 pjsua_srv_pres *uapres;
780
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000781 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +0000782
Benny Prijono922933b2007-01-21 16:23:56 +0000783 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000784 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000785
786 pjsip_pres_status pres_status;
787 pj_str_t reason = { "noresource", 10 };
788 pjsip_tx_data *tdata;
789
790 pjsip_pres_get_status(uapres->sub, &pres_status);
791
792 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
793 pjsip_pres_set_status(uapres->sub, &pres_status);
794
795 if (pjsip_pres_notify(uapres->sub,
796 PJSIP_EVSUB_STATE_TERMINATED, NULL,
797 &reason, &tdata)==PJ_SUCCESS)
798 {
799 pjsip_pres_send_request(uapres->sub, tdata);
800 }
801
802 uapres = uapres->next;
803 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000804
Benny Prijono922933b2007-01-21 16:23:56 +0000805 /* Clear server presence subscription list because account might be reused
806 * later. */
807 pj_list_init(&acc->pres_srv_list);
808
809 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000810 if (acc->publish_sess) {
811 acc->online_status = PJ_FALSE;
812 send_publish(acc_id, PJ_FALSE);
813 if (acc->publish_sess) {
814 pjsip_publishc_destroy(acc->publish_sess);
815 acc->publish_sess = NULL;
816 }
817 acc_cfg->publish_enabled = PJ_FALSE;
818 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000819}
820
821
822/* Refresh subscription (e.g. when our online status has changed) */
823static void refresh_server_subscription(int acc_id)
824{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000825 pjsua_acc *acc = &pjsua_var.acc[acc_id];
826 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000827 pjsua_srv_pres *uapres;
828
829 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
830
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000831 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +0000832
833 pjsip_pres_status pres_status;
834 pjsip_tx_data *tdata;
835
836 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000837 if (pres_status.info[0].basic_open != acc->online_status) {
838 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono834aee32006-02-19 01:38:06 +0000839 pjsip_pres_set_status(uapres->sub, &pres_status);
840
Benny Prijono21b9ad92006-08-15 13:11:22 +0000841 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
842 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000843 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000844 }
Benny Prijono834aee32006-02-19 01:38:06 +0000845 }
846
847 uapres = uapres->next;
848 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000849
Benny Prijono8b6834f2007-02-24 13:29:22 +0000850 /* Send PUBLISH if required. We only do this when we have a PUBLISH
851 * session. If we don't have a PUBLISH session, then it could be
852 * that we're waiting until registration has completed before we
853 * send the first PUBLISH.
854 */
855 if (acc_cfg->publish_enabled && acc->publish_sess) {
856 if (acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000857 send_publish(acc_id, PJ_TRUE);
858 }
859 }
Benny Prijono834aee32006-02-19 01:38:06 +0000860}
861
862
863
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000864/***************************************************************************
865 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000866 */
867
868/* Callback called when *client* subscription state has changed. */
869static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
870{
871 pjsua_buddy *buddy;
872
873 PJ_UNUSED_ARG(event);
874
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000875 PJSUA_LOCK();
876
Benny Prijonoa1e69682007-05-11 15:14:34 +0000877 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000878 if (buddy) {
879 PJ_LOG(3,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000880 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000881 (int)pjsua_var.buddy[buddy->index].uri.slen,
882 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +0000883 pjsip_evsub_get_state_name(sub)));
884
885 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
886 buddy->sub = NULL;
887 buddy->status.info_cnt = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000888 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000889 }
Benny Prijono9fc735d2006-05-28 14:58:12 +0000890
891 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000892 if (pjsua_var.ua_cfg.cb.on_buddy_state)
893 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono834aee32006-02-19 01:38:06 +0000894 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000895
896 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000897}
898
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000899
900/* Callback when transaction state has changed. */
901static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
902 pjsip_transaction *tsx,
903 pjsip_event *event)
904{
905 pjsua_buddy *buddy;
906 pjsip_contact_hdr *contact_hdr;
907
908 PJSUA_LOCK();
909
Benny Prijonoa1e69682007-05-11 15:14:34 +0000910 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000911 if (!buddy) {
912 PJSUA_UNLOCK();
913 return;
914 }
915
916 /* We only use this to update buddy's Contact, when it's not
917 * set.
918 */
919 if (buddy->contact.slen != 0) {
920 /* Contact already set */
921 PJSUA_UNLOCK();
922 return;
923 }
924
925 /* Only care about 2xx response to outgoing SUBSCRIBE */
926 if (tsx->status_code/100 != 2 ||
927 tsx->role != PJSIP_UAC_ROLE ||
928 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000929 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000930 {
931 PJSUA_UNLOCK();
932 return;
933 }
934
935 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000936 contact_hdr = (pjsip_contact_hdr*)
937 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000938 PJSIP_H_CONTACT, NULL);
939 if (!contact_hdr) {
940 PJSUA_UNLOCK();
941 return;
942 }
943
Benny Prijonoa1e69682007-05-11 15:14:34 +0000944 buddy->contact.ptr = (char*)
945 pj_pool_alloc(pjsua_var.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000946 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
947 contact_hdr->uri,
948 buddy->contact.ptr,
949 PJSIP_MAX_URL_SIZE);
950 if (buddy->contact.slen < 0)
951 buddy->contact.slen = 0;
952
953 PJSUA_UNLOCK();
954}
955
956
Benny Prijono834aee32006-02-19 01:38:06 +0000957/* Callback called when we receive NOTIFY */
958static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
959 pjsip_rx_data *rdata,
960 int *p_st_code,
961 pj_str_t **p_st_text,
962 pjsip_hdr *res_hdr,
963 pjsip_msg_body **p_body)
964{
965 pjsua_buddy *buddy;
966
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000967 PJSUA_LOCK();
968
Benny Prijonoa1e69682007-05-11 15:14:34 +0000969 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000970 if (buddy) {
971 /* Update our info. */
972 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +0000973 }
974
975 /* The default is to send 200 response to NOTIFY.
976 * Just leave it there..
977 */
978 PJ_UNUSED_ARG(rdata);
979 PJ_UNUSED_ARG(p_st_code);
980 PJ_UNUSED_ARG(p_st_text);
981 PJ_UNUSED_ARG(res_hdr);
982 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000983
984 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000985}
986
987
988/* Event subscription callback. */
989static pjsip_evsub_user pres_callback =
990{
991 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000992 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +0000993
994 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
995 * we want to authenticate
996 */
997
998 &pjsua_evsub_on_rx_notify,
999
1000 NULL, /* on_client_refresh: Use default behaviour, which is to
1001 * refresh client subscription. */
1002
1003 NULL, /* on_server_timeout: Use default behaviour, which is to send
1004 * NOTIFY to terminate.
1005 */
1006};
1007
1008
1009/* It does what it says.. */
1010static void subscribe_buddy_presence(unsigned index)
1011{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001012 pjsua_buddy *buddy;
1013 int acc_id;
1014 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001015 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001016 pjsip_tx_data *tdata;
1017 pj_status_t status;
1018
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001019 buddy = &pjsua_var.buddy[index];
1020 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001021
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001022 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001023
Benny Prijonob4a17c92006-07-10 14:40:21 +00001024 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1025 acc_id, index));
1026
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001027 /* Generate suitable Contact header */
1028 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
1029 acc_id, &buddy->uri);
1030 if (status != PJ_SUCCESS) {
1031 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
1032 return;
1033 }
1034
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001035 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001036 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001037 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001038 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001039 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001040 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001041 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001042 pjsua_perror(THIS_FILE, "Unable to create dialog",
1043 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001044 return;
1045 }
1046
Benny Prijonof9c40c32007-06-28 07:20:26 +00001047 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001048 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1049 if (status != PJ_SUCCESS) {
1050 pjsua_var.buddy[index].sub = NULL;
1051 pjsua_perror(THIS_FILE, "Unable to create presence client",
1052 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001053 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001054 return;
1055 }
1056
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001057 /* If account is locked to specific transport, then lock dialog
1058 * to this transport too.
1059 */
1060 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1061 pjsip_tpselector tp_sel;
1062
1063 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001064 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001065 }
1066
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001067 /* Set route-set */
1068 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001069 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001070 }
1071
1072 /* Set credentials */
1073 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001074 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001075 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001076 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001077
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001078 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001079
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001080 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001081 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001082 if (buddy->sub) {
1083 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1084 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001085 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001086 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1087 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001088 return;
1089 }
1090
Benny Prijono21b9ad92006-08-15 13:11:22 +00001091 pjsua_process_msg_data(tdata, NULL);
1092
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001093 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001094 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001095 if (buddy->sub) {
1096 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1097 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001098 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001099 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1100 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001101 return;
1102 }
Benny Prijono834aee32006-02-19 01:38:06 +00001103}
1104
1105
1106/* It does what it says... */
1107static void unsubscribe_buddy_presence(unsigned index)
1108{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001109 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001110 pjsip_tx_data *tdata;
1111 pj_status_t status;
1112
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 buddy = &pjsua_var.buddy[index];
1114
1115 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001116 return;
1117
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001118 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1119 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001120 return;
1121 }
1122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001123 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001124 if (status == PJ_SUCCESS) {
1125 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001126 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001127 }
Benny Prijono834aee32006-02-19 01:38:06 +00001128
Benny Prijono48da92e2007-05-16 08:56:30 +00001129 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1131 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001132 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1133 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001134 }
1135}
1136
1137
Benny Prijonof9c40c32007-06-28 07:20:26 +00001138/* Lock all buddies */
1139#define LOCK_BUDDIES unsigned cnt_ = 0; \
1140 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1141 unsigned i_; \
1142 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1143 if (pjsua_var.buddy[i_].sub) { \
1144 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1145 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1146 } \
1147 } \
1148 PJSUA_LOCK();
1149
1150/* Unlock all buddies */
1151#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1152 for (i_=0; i_<cnt_; ++i_) { \
1153 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1154 }
1155
1156
1157
Benny Prijono834aee32006-02-19 01:38:06 +00001158/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001160{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001161 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001162
Benny Prijonof9c40c32007-06-28 07:20:26 +00001163 LOCK_BUDDIES;
1164
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001165 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001166
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001167 if (!pjsua_var.buddy[i].uri.slen)
1168 continue;
1169
1170 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001171 subscribe_buddy_presence(i);
1172
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001173 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001174 unsubscribe_buddy_presence(i);
1175
1176 }
1177 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001178
1179 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001180}
1181
Benny Prijono7a5f5102007-05-29 00:33:09 +00001182/* Timer callback to re-create client subscription */
1183static void pres_timer_cb(pj_timer_heap_t *th,
1184 pj_timer_entry *entry)
1185{
1186 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1187
Benny Prijono7a5f5102007-05-29 00:33:09 +00001188 entry->id = PJ_FALSE;
1189 refresh_client_subscriptions();
1190
1191 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1192 entry->id = PJ_TRUE;
1193
Benny Prijonof9c40c32007-06-28 07:20:26 +00001194 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001195}
1196
Benny Prijono834aee32006-02-19 01:38:06 +00001197
1198/*
1199 * Init presence
1200 */
1201pj_status_t pjsua_pres_init()
1202{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001203 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001204 pj_status_t status;
1205
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001206 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001207 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001208 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1209 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001210 }
1211
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001212 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1213 reset_buddy(i);
1214 }
1215
Benny Prijono834aee32006-02-19 01:38:06 +00001216 return status;
1217}
1218
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001219
Benny Prijono834aee32006-02-19 01:38:06 +00001220/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001222 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001223pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001224{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001225 /* Start presence timer to re-subscribe to buddy's presence when
1226 * subscription has failed.
1227 */
1228 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1229 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1230
1231 pjsua_var.pres_timer.cb = &pres_timer_cb;
1232 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1233 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001234 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001235 }
1236
Benny Prijono9fc735d2006-05-28 14:58:12 +00001237 return PJ_SUCCESS;
1238}
1239
1240
1241/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001243 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001245{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001246 unsigned i;
1247
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001249
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001250 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1251 if (pjsua_var.acc[i].valid)
1252 refresh_server_subscription(i);
1253 }
Benny Prijono834aee32006-02-19 01:38:06 +00001254}
1255
1256
1257/*
1258 * Shutdown presence.
1259 */
1260void pjsua_pres_shutdown(void)
1261{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001262 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001263
Benny Prijono7a5f5102007-05-29 00:33:09 +00001264 if (pjsua_var.pres_timer.id != 0) {
1265 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1266 pjsua_var.pres_timer.id = PJ_FALSE;
1267 }
1268
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1270 if (!pjsua_var.acc[i].valid)
1271 continue;
1272 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001273 }
1274
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001275 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1276 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001277 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001278
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001279 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001280}