blob: 75b2a78771f709196b99a6e5802ba9aa9584c177 [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
26# define PJSUA_PRES_TIMER 120
27#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;
73
74}
75
76
77/*
78 * Get detailed buddy info.
79 */
80PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
81 pjsua_buddy_info *info)
82{
83 int total=0;
84 pjsua_buddy *buddy;
85
86 PJ_ASSERT_RETURN(buddy_id>=0 &&
87 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
88 PJ_EINVAL);
89
90 PJSUA_LOCK();
91
Benny Prijonoac623b32006-07-03 15:19:31 +000092 pj_bzero(info, sizeof(pjsua_buddy_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093
94 buddy = &pjsua_var.buddy[buddy_id];
95 info->id = buddy->index;
96 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
97 PJSUA_UNLOCK();
98 return PJ_SUCCESS;
99 }
100
101 /* uri */
102 info->uri.ptr = info->buf_ + total;
103 pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
104 total += info->uri.slen;
105
106 /* contact */
107 info->contact.ptr = info->buf_ + total;
108 pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
109 total += info->contact.slen;
110
111 /* status and status text */
112 if (buddy->sub == NULL || buddy->status.info_cnt==0) {
113 info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
114 info->status_text = pj_str("?");
115 } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
116 info->status = PJSUA_BUDDY_STATUS_ONLINE;
117 info->status_text = pj_str("Online");
118 } else {
119 info->status = PJSUA_BUDDY_STATUS_OFFLINE;
120 info->status_text = pj_str("Offline");
121 }
122
123 /* monitor pres */
124 info->monitor_pres = buddy->monitor;
125
126 PJSUA_UNLOCK();
127 return PJ_SUCCESS;
128}
129
130
131/*
132 * Reset buddy descriptor.
133 */
134static void reset_buddy(pjsua_buddy_id id)
135{
Benny Prijonoac623b32006-07-03 15:19:31 +0000136 pj_bzero(&pjsua_var.buddy[id], sizeof(pjsua_var.buddy[id]));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000137 pjsua_var.buddy[id].index = id;
138}
139
140
141/*
142 * Add new buddy.
143 */
144PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
145 pjsua_buddy_id *p_buddy_id)
146{
147 pjsip_name_addr *url;
148 pjsip_sip_uri *sip_uri;
149 int index;
150 pj_str_t tmp;
151
152 PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <=
153 PJ_ARRAY_SIZE(pjsua_var.buddy),
154 PJ_ETOOMANY);
155
156 PJSUA_LOCK();
157
158 /* Find empty slot */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000159 for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 if (pjsua_var.buddy[index].uri.slen == 0)
161 break;
162 }
163
164 /* Expect to find an empty slot */
165 if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
166 PJSUA_UNLOCK();
167 /* This shouldn't happen */
168 pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
169 return PJ_ETOOMANY;
170 }
171
172
173 /* Get name and display name for buddy */
174 pj_strdup_with_null(pjsua_var.pool, &tmp, &cfg->uri);
175 url = (pjsip_name_addr*)pjsip_parse_uri(pjsua_var.pool, tmp.ptr, tmp.slen,
176 PJSIP_PARSE_URI_AS_NAMEADDR);
177
178 if (url == NULL) {
179 pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
180 PJSUA_UNLOCK();
181 return PJSIP_EINVALIDURI;
182 }
183
Benny Prijonofc493592007-02-18 20:56:32 +0000184 /* Only support SIP schemes */
185 if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url))
186 return PJSIP_EINVALIDSCHEME;
187
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188 /* Reset buddy, to make sure everything is cleared with default
189 * values
190 */
191 reset_buddy(index);
192
193 /* Save URI */
194 pjsua_var.buddy[index].uri = tmp;
195
Benny Prijono9c1528f2007-02-10 19:22:25 +0000196 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000197 pjsua_var.buddy[index].name = sip_uri->user;
198 pjsua_var.buddy[index].display = url->display;
199 pjsua_var.buddy[index].host = sip_uri->host;
200 pjsua_var.buddy[index].port = sip_uri->port;
201 pjsua_var.buddy[index].monitor = cfg->subscribe;
202 if (pjsua_var.buddy[index].port == 0)
203 pjsua_var.buddy[index].port = 5060;
204
205 if (p_buddy_id)
206 *p_buddy_id = index;
207
208 pjsua_var.buddy_cnt++;
209
210 pjsua_buddy_subscribe_pres(index, cfg->subscribe);
211
212 PJSUA_UNLOCK();
213
214 return PJ_SUCCESS;
215}
216
217
218/*
219 * Delete buddy.
220 */
221PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
222{
223 PJ_ASSERT_RETURN(buddy_id>=0 &&
224 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
225 PJ_EINVAL);
226
227 PJSUA_LOCK();
228
229 if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
230 PJSUA_UNLOCK();
231 return PJ_SUCCESS;
232 }
233
234 /* Unsubscribe presence */
235 pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
236
237 /* Remove buddy */
238 pjsua_var.buddy[buddy_id].uri.slen = 0;
239 pjsua_var.buddy_cnt--;
240
241 /* Reset buddy struct */
242 reset_buddy(buddy_id);
243
244 PJSUA_UNLOCK();
245 return PJ_SUCCESS;
246}
247
248
249/*
250 * Enable/disable buddy's presence monitoring.
251 */
252PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
253 pj_bool_t subscribe)
254{
255 pjsua_buddy *buddy;
256
257 PJ_ASSERT_RETURN(buddy_id>=0 &&
258 buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
259 PJ_EINVAL);
260
261 PJSUA_LOCK();
262
263 buddy = &pjsua_var.buddy[buddy_id];
264 buddy->monitor = subscribe;
265 pjsua_pres_refresh();
266
267 PJSUA_UNLOCK();
268
269 return PJ_SUCCESS;
270}
271
272
273/*
274 * Dump presence subscriptions to log file.
275 */
276PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
277{
278 unsigned acc_id;
279 unsigned i;
280
281
282 PJSUA_LOCK();
283
284 /*
285 * When no detail is required, just dump number of server and client
286 * subscriptions.
287 */
288 if (verbose == PJ_FALSE) {
289
290 int count = 0;
291
292 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
293
294 if (!pjsua_var.acc[acc_id].valid)
295 continue;
296
297 if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
298 struct pjsua_srv_pres *uapres;
299
300 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
301 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
302 ++count;
303 uapres = uapres->next;
304 }
305 }
306 }
307
308 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
309 count));
310
311 count = 0;
312
313 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
314 if (pjsua_var.buddy[i].uri.slen == 0)
315 continue;
316 if (pjsua_var.buddy[i].sub) {
317 ++count;
318 }
319 }
320
321 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
322 count));
323 PJSUA_UNLOCK();
324 return;
325 }
326
327
328 /*
329 * Dumping all server (UAS) subscriptions
330 */
331 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
332
333 for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
334
335 if (!pjsua_var.acc[acc_id].valid)
336 continue;
337
338 PJ_LOG(3,(THIS_FILE, " %.*s",
339 (int)pjsua_var.acc[acc_id].cfg.id.slen,
340 pjsua_var.acc[acc_id].cfg.id.ptr));
341
342 if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
343
344 PJ_LOG(3,(THIS_FILE, " - none - "));
345
346 } else {
347 struct pjsua_srv_pres *uapres;
348
349 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
350 while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
351
352 PJ_LOG(3,(THIS_FILE, " %10s %s",
353 pjsip_evsub_get_state_name(uapres->sub),
354 uapres->remote));
355
356 uapres = uapres->next;
357 }
358 }
359 }
360
361 /*
362 * Dumping all client (UAC) subscriptions
363 */
364 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
365
366 if (pjsua_var.buddy_cnt == 0) {
367
368 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
369
370 } else {
371 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
372
373 if (pjsua_var.buddy[i].uri.slen == 0)
374 continue;
375
376 if (pjsua_var.buddy[i].sub) {
377 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
378 pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
379 (int)pjsua_var.buddy[i].uri.slen,
380 pjsua_var.buddy[i].uri.ptr));
381 } else {
382 PJ_LOG(3,(THIS_FILE, " %10s %.*s",
383 "(null)",
384 (int)pjsua_var.buddy[i].uri.slen,
385 pjsua_var.buddy[i].uri.ptr));
386 }
387 }
388 }
389
390 PJSUA_UNLOCK();
391}
392
393
394/***************************************************************************
395 * Server subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000396 */
397
398/* Proto */
399static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
400
401/* The module instance. */
402static pjsip_module mod_pjsua_pres =
403{
404 NULL, NULL, /* prev, next. */
405 { "mod-pjsua-pres", 14 }, /* Name. */
406 -1, /* Id */
407 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +0000408 NULL, /* load() */
409 NULL, /* start() */
410 NULL, /* stop() */
411 NULL, /* unload() */
412 &pres_on_rx_request, /* on_rx_request() */
413 NULL, /* on_rx_response() */
414 NULL, /* on_tx_request. */
415 NULL, /* on_tx_response() */
416 NULL, /* on_tsx_state() */
417
418};
419
420
421/* Callback called when *server* subscription state has changed. */
422static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
423{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 pjsua_srv_pres *uapres;
Benny Prijono834aee32006-02-19 01:38:06 +0000425
426 PJ_UNUSED_ARG(event);
427
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428 PJSUA_LOCK();
429
Benny Prijonoa1e69682007-05-11 15:14:34 +0000430 uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000431 if (uapres) {
432 PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s",
433 uapres->remote, pjsip_evsub_get_state_name(sub)));
434
435 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000436 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000437 pj_list_erase(uapres);
438 }
439 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440
441 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000442}
443
444/* This is called when request is received.
445 * We need to check for incoming SUBSCRIBE request.
446 */
447static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
448{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000449 int acc_id;
Benny Prijono6f979412006-06-15 12:25:46 +0000450 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000451 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +0000452 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
453 pjsua_srv_pres *uapres;
454 pjsip_evsub *sub;
455 pjsip_evsub_user pres_cb;
456 pjsip_tx_data *tdata;
457 pjsip_pres_status pres_status;
458 pjsip_dialog *dlg;
459 pj_status_t status;
460
461 if (pjsip_method_cmp(req_method, &pjsip_subscribe_method) != 0)
462 return PJ_FALSE;
463
464 /* Incoming SUBSCRIBE: */
465
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 PJSUA_LOCK();
467
Benny Prijonoa91a0032006-02-26 21:23:45 +0000468 /* Find which account for the incoming request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000469 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijono6f979412006-06-15 12:25:46 +0000470 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000471
Benny Prijono6f979412006-06-15 12:25:46 +0000472 PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
473 acc_id));
474
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000475 /* Create suitable Contact header */
476 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
477 acc_id, rdata);
478 if (status != PJ_SUCCESS) {
479 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
480 PJSUA_UNLOCK();
481 return PJ_TRUE;
482 }
483
Benny Prijono834aee32006-02-19 01:38:06 +0000484 /* Create UAS dialog: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000485 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000486 &contact, &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000487 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000488 pjsua_perror(THIS_FILE,
489 "Unable to create UAS dialog for subscription",
490 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000491 PJSUA_UNLOCK();
492 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000493 }
494
Benny Prijono6f979412006-06-15 12:25:46 +0000495 /* Set credentials. */
496 pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
497
Benny Prijono834aee32006-02-19 01:38:06 +0000498 /* Init callback: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000499 pj_bzero(&pres_cb, sizeof(pres_cb));
Benny Prijono834aee32006-02-19 01:38:06 +0000500 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
501
502 /* Create server presence subscription: */
503 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
504 if (status != PJ_SUCCESS) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000505 pjsip_dlg_terminate(dlg);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000506 pjsua_perror(THIS_FILE, "Unable to create server subscription",
507 status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000508 PJSUA_UNLOCK();
509 return PJ_TRUE;
Benny Prijono834aee32006-02-19 01:38:06 +0000510 }
511
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000512 /* If account is locked to specific transport, then lock dialog
513 * to this transport too.
514 */
515 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
516 pjsip_tpselector tp_sel;
517
518 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
519 pjsip_dlg_set_transport(dlg, &tp_sel);
520 }
521
Benny Prijono834aee32006-02-19 01:38:06 +0000522 /* Attach our data to the subscription: */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000523 uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
Benny Prijono834aee32006-02-19 01:38:06 +0000524 uapres->sub = sub;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000525 uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
Benny Prijono834aee32006-02-19 01:38:06 +0000526 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
527 uapres->remote, PJSIP_MAX_URL_SIZE);
528 if (status < 1)
529 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
530 else
531 uapres->remote[status] = '\0';
532
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000533 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000534
535 /* Add server subscription to the list: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000537
538
539 /* Create and send 200 (OK) to the SUBSCRIBE request: */
540 status = pjsip_pres_accept(sub, rdata, 200, NULL);
541 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000542 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
543 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000544 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000545 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000546 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000547 return PJ_FALSE;
548 }
549
550
551 /* Set our online status: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000552 pj_bzero(&pres_status, sizeof(pres_status));
Benny Prijono834aee32006-02-19 01:38:06 +0000553 pres_status.info_cnt = 1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000554 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
555 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000556 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000557 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono834aee32006-02-19 01:38:06 +0000558
559 pjsip_pres_set_status(sub, &pres_status);
560
561 /* Create and send the first NOTIFY to active subscription: */
562 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, NULL,
563 NULL, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000564 if (status == PJ_SUCCESS) {
565 pjsua_process_msg_data(tdata, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000566 status = pjsip_pres_send_request( sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000567 }
Benny Prijono834aee32006-02-19 01:38:06 +0000568
569 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000570 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
571 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000572 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000573 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000575 return PJ_FALSE;
576 }
577
578
579 /* Done: */
580
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 PJSUA_UNLOCK();
582
Benny Prijono834aee32006-02-19 01:38:06 +0000583 return PJ_TRUE;
584}
585
586
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000587/*
588 * Client presence publication callback.
589 */
590static void publish_cb(struct pjsip_publishc_cbparam *param)
591{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000592 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000593
594 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
595 if (param->status != PJ_SUCCESS) {
596 char errmsg[PJ_ERR_MSG_SIZE];
597
598 pj_strerror(param->status, errmsg, sizeof(errmsg));
599 PJ_LOG(1,(THIS_FILE,
600 "Client publication (PUBLISH) failed, status=%d, msg=%s",
601 param->status, errmsg));
602 } else {
603 PJ_LOG(1,(THIS_FILE,
604 "Client publication (PUBLISH) failed (%d/%.*s)",
605 param->code, (int)param->reason.slen,
606 param->reason.ptr));
607 }
608
609 pjsip_publishc_destroy(param->pubc);
610 acc->publish_sess = NULL;
611 }
612}
613
614
615/*
616 * Send PUBLISH request.
617 */
618static pj_status_t send_publish(int acc_id, pj_bool_t active)
619{
620 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
621 pjsua_acc *acc = &pjsua_var.acc[acc_id];
622 pjsip_pres_status pres_status;
623 pjsip_tx_data *tdata;
624 pj_status_t status;
625
626
627 /* Create PUBLISH request */
628 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000629 char *bpos;
630 pj_str_t entity;
631
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000632 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
633 if (status != PJ_SUCCESS) {
634 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
635 goto on_error;
636 }
637
638 /* Set our online status: */
639 pj_bzero(&pres_status, sizeof(pres_status));
640 pres_status.info_cnt = 1;
641 pres_status.info[0].basic_open = acc->online_status;
642
Benny Prijono8c6e8842007-02-24 15:33:54 +0000643 /* Be careful not to send PIDF with presence entity ID containing
644 * "<" character.
645 */
646 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
647 char *epos = pj_strchr(&acc_cfg->id, '>');
648 if (epos - bpos < 2) {
649 pj_assert(!"Unexpected invalid URI");
650 status = PJSIP_EINVALIDURI;
651 goto on_error;
652 }
653 entity.ptr = bpos+1;
654 entity.slen = epos - bpos - 1;
655 } else {
656 entity = acc_cfg->id;
657 }
658
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000659 /* Create and add PIDF message body */
660 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +0000661 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000662 if (status != PJ_SUCCESS) {
663 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
664 status);
665 pjsip_tx_data_dec_ref(tdata);
666 goto on_error;
667 }
668 } else {
669 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
670 if (status != PJ_SUCCESS) {
671 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
672 goto on_error;
673 }
674 }
675
676 /* Add headers etc */
677 pjsua_process_msg_data(tdata, NULL);
678
679 /* Send the PUBLISH request */
680 status = pjsip_publishc_send(acc->publish_sess, tdata);
681 if (status != PJ_SUCCESS) {
682 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
683 goto on_error;
684 }
685
686 acc->publish_state = acc->online_status;
687 return PJ_SUCCESS;
688
689on_error:
690 pjsip_publishc_destroy(acc->publish_sess);
691 acc->publish_sess = NULL;
692 return status;
693}
694
695
696/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +0000697pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000698{
699 const pj_str_t STR_PRESENCE = { "presence", 8 };
700 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
701 pjsua_acc *acc = &pjsua_var.acc[acc_id];
702 pj_status_t status;
703
704 /* Create and init client publication session */
705 if (acc_cfg->publish_enabled) {
706
707 /* Create client publication */
708 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
709 &acc->publish_sess);
710 if (status != PJ_SUCCESS) {
711 acc->publish_sess = NULL;
712 return status;
713 }
714
715 /* Initialize client publication */
716 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
717 &acc_cfg->id, &acc_cfg->id,
718 &acc_cfg->id,
Benny Prijono703b7d72007-03-20 09:13:24 +0000719 60);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000720 if (status != PJ_SUCCESS) {
721 acc->publish_sess = NULL;
722 return status;
723 }
724
Benny Prijono703b7d72007-03-20 09:13:24 +0000725 /* Add credential for authentication */
726 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
727 acc->cred);
728
729 /* Set route-set */
730 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
731
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000732 /* Send initial PUBLISH request */
733 if (acc->online_status != 0) {
734 status = send_publish(acc_id, PJ_TRUE);
735 if (status != PJ_SUCCESS)
736 return status;
737 }
738
739 } else {
740 acc->publish_sess = NULL;
741 }
742
743 return PJ_SUCCESS;
744}
745
746
747/* Init presence for account */
748pj_status_t pjsua_pres_init_acc(int acc_id)
749{
750 pjsua_acc *acc = &pjsua_var.acc[acc_id];
751
752 /* Init presence subscription */
753 pj_list_init(&acc->pres_srv_list);
754
Benny Prijono8b6834f2007-02-24 13:29:22 +0000755 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000756}
757
758
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000759/* Terminate server subscription for the account */
760void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +0000761{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000762 pjsua_acc *acc = &pjsua_var.acc[acc_id];
763 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +0000764 pjsua_srv_pres *uapres;
765
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000766 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +0000767
Benny Prijono922933b2007-01-21 16:23:56 +0000768 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000769 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000770
771 pjsip_pres_status pres_status;
772 pj_str_t reason = { "noresource", 10 };
773 pjsip_tx_data *tdata;
774
775 pjsip_pres_get_status(uapres->sub, &pres_status);
776
777 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
778 pjsip_pres_set_status(uapres->sub, &pres_status);
779
780 if (pjsip_pres_notify(uapres->sub,
781 PJSIP_EVSUB_STATE_TERMINATED, NULL,
782 &reason, &tdata)==PJ_SUCCESS)
783 {
784 pjsip_pres_send_request(uapres->sub, tdata);
785 }
786
787 uapres = uapres->next;
788 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000789
Benny Prijono922933b2007-01-21 16:23:56 +0000790 /* Clear server presence subscription list because account might be reused
791 * later. */
792 pj_list_init(&acc->pres_srv_list);
793
794 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000795 if (acc->publish_sess) {
796 acc->online_status = PJ_FALSE;
797 send_publish(acc_id, PJ_FALSE);
798 if (acc->publish_sess) {
799 pjsip_publishc_destroy(acc->publish_sess);
800 acc->publish_sess = NULL;
801 }
802 acc_cfg->publish_enabled = PJ_FALSE;
803 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000804}
805
806
807/* Refresh subscription (e.g. when our online status has changed) */
808static void refresh_server_subscription(int acc_id)
809{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000810 pjsua_acc *acc = &pjsua_var.acc[acc_id];
811 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000812 pjsua_srv_pres *uapres;
813
814 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
815
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000816 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +0000817
818 pjsip_pres_status pres_status;
819 pjsip_tx_data *tdata;
820
821 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000822 if (pres_status.info[0].basic_open != acc->online_status) {
823 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono834aee32006-02-19 01:38:06 +0000824 pjsip_pres_set_status(uapres->sub, &pres_status);
825
Benny Prijono21b9ad92006-08-15 13:11:22 +0000826 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
827 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000828 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000829 }
Benny Prijono834aee32006-02-19 01:38:06 +0000830 }
831
832 uapres = uapres->next;
833 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000834
Benny Prijono8b6834f2007-02-24 13:29:22 +0000835 /* Send PUBLISH if required. We only do this when we have a PUBLISH
836 * session. If we don't have a PUBLISH session, then it could be
837 * that we're waiting until registration has completed before we
838 * send the first PUBLISH.
839 */
840 if (acc_cfg->publish_enabled && acc->publish_sess) {
841 if (acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000842 send_publish(acc_id, PJ_TRUE);
843 }
844 }
Benny Prijono834aee32006-02-19 01:38:06 +0000845}
846
847
848
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000849/***************************************************************************
850 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000851 */
852
853/* Callback called when *client* subscription state has changed. */
854static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
855{
856 pjsua_buddy *buddy;
857
858 PJ_UNUSED_ARG(event);
859
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000860 PJSUA_LOCK();
861
Benny Prijonoa1e69682007-05-11 15:14:34 +0000862 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000863 if (buddy) {
864 PJ_LOG(3,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000865 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000866 (int)pjsua_var.buddy[buddy->index].uri.slen,
867 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +0000868 pjsip_evsub_get_state_name(sub)));
869
870 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
871 buddy->sub = NULL;
872 buddy->status.info_cnt = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000873 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000874 }
Benny Prijono9fc735d2006-05-28 14:58:12 +0000875
876 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000877 if (pjsua_var.ua_cfg.cb.on_buddy_state)
878 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono834aee32006-02-19 01:38:06 +0000879 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000880
881 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000882}
883
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000884
885/* Callback when transaction state has changed. */
886static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
887 pjsip_transaction *tsx,
888 pjsip_event *event)
889{
890 pjsua_buddy *buddy;
891 pjsip_contact_hdr *contact_hdr;
892
893 PJSUA_LOCK();
894
Benny Prijonoa1e69682007-05-11 15:14:34 +0000895 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000896 if (!buddy) {
897 PJSUA_UNLOCK();
898 return;
899 }
900
901 /* We only use this to update buddy's Contact, when it's not
902 * set.
903 */
904 if (buddy->contact.slen != 0) {
905 /* Contact already set */
906 PJSUA_UNLOCK();
907 return;
908 }
909
910 /* Only care about 2xx response to outgoing SUBSCRIBE */
911 if (tsx->status_code/100 != 2 ||
912 tsx->role != PJSIP_UAC_ROLE ||
913 event->type != PJSIP_EVENT_RX_MSG ||
914 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)!=0)
915 {
916 PJSUA_UNLOCK();
917 return;
918 }
919
920 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000921 contact_hdr = (pjsip_contact_hdr*)
922 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000923 PJSIP_H_CONTACT, NULL);
924 if (!contact_hdr) {
925 PJSUA_UNLOCK();
926 return;
927 }
928
Benny Prijonoa1e69682007-05-11 15:14:34 +0000929 buddy->contact.ptr = (char*)
930 pj_pool_alloc(pjsua_var.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000931 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
932 contact_hdr->uri,
933 buddy->contact.ptr,
934 PJSIP_MAX_URL_SIZE);
935 if (buddy->contact.slen < 0)
936 buddy->contact.slen = 0;
937
938 PJSUA_UNLOCK();
939}
940
941
Benny Prijono834aee32006-02-19 01:38:06 +0000942/* Callback called when we receive NOTIFY */
943static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
944 pjsip_rx_data *rdata,
945 int *p_st_code,
946 pj_str_t **p_st_text,
947 pjsip_hdr *res_hdr,
948 pjsip_msg_body **p_body)
949{
950 pjsua_buddy *buddy;
951
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000952 PJSUA_LOCK();
953
Benny Prijonoa1e69682007-05-11 15:14:34 +0000954 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000955 if (buddy) {
956 /* Update our info. */
957 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +0000958 }
959
960 /* The default is to send 200 response to NOTIFY.
961 * Just leave it there..
962 */
963 PJ_UNUSED_ARG(rdata);
964 PJ_UNUSED_ARG(p_st_code);
965 PJ_UNUSED_ARG(p_st_text);
966 PJ_UNUSED_ARG(res_hdr);
967 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000968
969 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000970}
971
972
973/* Event subscription callback. */
974static pjsip_evsub_user pres_callback =
975{
976 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000977 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +0000978
979 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
980 * we want to authenticate
981 */
982
983 &pjsua_evsub_on_rx_notify,
984
985 NULL, /* on_client_refresh: Use default behaviour, which is to
986 * refresh client subscription. */
987
988 NULL, /* on_server_timeout: Use default behaviour, which is to send
989 * NOTIFY to terminate.
990 */
991};
992
993
994/* It does what it says.. */
995static void subscribe_buddy_presence(unsigned index)
996{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000997 pjsua_buddy *buddy;
998 int acc_id;
999 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001000 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001001 pjsip_dialog *dlg;
1002 pjsip_tx_data *tdata;
1003 pj_status_t status;
1004
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001005 buddy = &pjsua_var.buddy[index];
1006 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001007
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001008 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001009
Benny Prijonob4a17c92006-07-10 14:40:21 +00001010 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1011 acc_id, index));
1012
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001013 /* Generate suitable Contact header */
1014 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
1015 acc_id, &buddy->uri);
1016 if (status != PJ_SUCCESS) {
1017 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
1018 return;
1019 }
1020
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001021 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001022 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001023 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001024 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025 &buddy->uri,
Benny Prijono834aee32006-02-19 01:38:06 +00001026 NULL, &dlg);
1027 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001028 pjsua_perror(THIS_FILE, "Unable to create dialog",
1029 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001030 return;
1031 }
1032
Benny Prijonodc752ca2006-09-22 16:55:42 +00001033 status = pjsip_pres_create_uac( dlg, &pres_callback,
1034 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1035 if (status != PJ_SUCCESS) {
1036 pjsua_var.buddy[index].sub = NULL;
1037 pjsua_perror(THIS_FILE, "Unable to create presence client",
1038 status);
1039 pjsip_dlg_terminate(dlg);
1040 return;
1041 }
1042
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001043 /* If account is locked to specific transport, then lock dialog
1044 * to this transport too.
1045 */
1046 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1047 pjsip_tpselector tp_sel;
1048
1049 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
1050 pjsip_dlg_set_transport(dlg, &tp_sel);
1051 }
1052
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001053 /* Set route-set */
1054 if (!pj_list_empty(&acc->route_set)) {
1055 pjsip_dlg_set_route_set(dlg, &acc->route_set);
1056 }
1057
1058 /* Set credentials */
1059 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +00001060 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001061 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001062 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001063
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001064 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001065
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001066 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001067 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001068 if (buddy->sub) {
1069 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1070 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001071 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001072 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1073 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001074 return;
1075 }
1076
Benny Prijono21b9ad92006-08-15 13:11:22 +00001077 pjsua_process_msg_data(tdata, NULL);
1078
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001079 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001080 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001081 if (buddy->sub) {
1082 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1083 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001084 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001085 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1086 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001087 return;
1088 }
Benny Prijono834aee32006-02-19 01:38:06 +00001089}
1090
1091
1092/* It does what it says... */
1093static void unsubscribe_buddy_presence(unsigned index)
1094{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001096 pjsip_tx_data *tdata;
1097 pj_status_t status;
1098
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001099 buddy = &pjsua_var.buddy[index];
1100
1101 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001102 return;
1103
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001104 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1105 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001106 return;
1107 }
1108
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001109 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001110 if (status == PJ_SUCCESS) {
1111 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001112 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001113 }
Benny Prijono834aee32006-02-19 01:38:06 +00001114
Benny Prijono48da92e2007-05-16 08:56:30 +00001115 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001116 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1117 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001118 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1119 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001120 }
1121}
1122
1123
1124/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001125static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001126{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001127 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001128
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001129 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001130
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001131 if (!pjsua_var.buddy[i].uri.slen)
1132 continue;
1133
1134 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001135 subscribe_buddy_presence(i);
1136
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001137 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001138 unsubscribe_buddy_presence(i);
1139
1140 }
1141 }
1142}
1143
Benny Prijono7a5f5102007-05-29 00:33:09 +00001144/* Timer callback to re-create client subscription */
1145static void pres_timer_cb(pj_timer_heap_t *th,
1146 pj_timer_entry *entry)
1147{
1148 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1149
1150 PJ_UNUSED_ARG(th);
1151
1152 PJSUA_LOCK();
1153
1154 entry->id = PJ_FALSE;
1155 refresh_client_subscriptions();
1156
1157 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1158 entry->id = PJ_TRUE;
1159
1160 PJSUA_UNLOCK();
1161}
1162
Benny Prijono834aee32006-02-19 01:38:06 +00001163
1164/*
1165 * Init presence
1166 */
1167pj_status_t pjsua_pres_init()
1168{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001169 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001170 pj_status_t status;
1171
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001173 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001174 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1175 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001176 }
1177
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001178 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1179 reset_buddy(i);
1180 }
1181
Benny Prijono834aee32006-02-19 01:38:06 +00001182 return status;
1183}
1184
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185
Benny Prijono834aee32006-02-19 01:38:06 +00001186/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001187 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001188 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001189pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001190{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001191 /* Start presence timer to re-subscribe to buddy's presence when
1192 * subscription has failed.
1193 */
1194 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1195 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1196
1197 pjsua_var.pres_timer.cb = &pres_timer_cb;
1198 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1199 &pres_interval);
1200 }
1201
Benny Prijono9fc735d2006-05-28 14:58:12 +00001202 return PJ_SUCCESS;
1203}
1204
1205
1206/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001207 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001208 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001209void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001210{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001211 unsigned i;
1212
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001213 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001214
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1216 if (pjsua_var.acc[i].valid)
1217 refresh_server_subscription(i);
1218 }
Benny Prijono834aee32006-02-19 01:38:06 +00001219}
1220
1221
1222/*
1223 * Shutdown presence.
1224 */
1225void pjsua_pres_shutdown(void)
1226{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001227 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001228
Benny Prijono7a5f5102007-05-29 00:33:09 +00001229 if (pjsua_var.pres_timer.id != 0) {
1230 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1231 pjsua_var.pres_timer.id = PJ_FALSE;
1232 }
1233
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001234 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1235 if (!pjsua_var.acc[i].valid)
1236 continue;
1237 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001238 }
1239
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001240 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1241 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001242 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001243
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001244 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001245}