blob: d5316959e1df11db260eb96f4f90e3b7ef7a2440 [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;
Benny Prijonofe04fb52007-08-24 08:28:52 +0000556 pres_status.info[0].id = pjsua_var.acc[acc_id].cfg.pidf_tuple_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000557 //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
Benny Prijono834aee32006-02-19 01:38:06 +0000558 //causing XML parsing to fail.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000559 //pres_status.info[0].contact = pjsua_var.local_uri;
Benny Prijono834aee32006-02-19 01:38:06 +0000560
561 pjsip_pres_set_status(sub, &pres_status);
562
Benny Prijonoc61cc042007-06-27 13:01:59 +0000563 /* Check expires value. If it's zero, send our presense state but
564 * set subscription state to TERMINATED.
565 */
566 expires_hdr=(pjsip_expires_hdr*)
567 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
568
569 if (expires_hdr && expires_hdr->ivalue == 0)
570 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
571 else
572 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
573
Benny Prijono834aee32006-02-19 01:38:06 +0000574 /* Create and send the first NOTIFY to active subscription: */
Benny Prijonoc61cc042007-06-27 13:01:59 +0000575 status = pjsip_pres_notify( sub, ev_state, NULL, NULL, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000576 if (status == PJ_SUCCESS) {
577 pjsua_process_msg_data(tdata, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000578 status = pjsip_pres_send_request( sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000579 }
Benny Prijono834aee32006-02-19 01:38:06 +0000580
581 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000582 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
583 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000584 pj_list_erase(uapres);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000585 pjsip_pres_terminate(sub, PJ_FALSE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000587 return PJ_FALSE;
588 }
589
590
591 /* Done: */
592
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000593 PJSUA_UNLOCK();
594
Benny Prijono834aee32006-02-19 01:38:06 +0000595 return PJ_TRUE;
596}
597
598
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000599/*
600 * Client presence publication callback.
601 */
602static void publish_cb(struct pjsip_publishc_cbparam *param)
603{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000604 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000605
606 if (param->code/100 != 2 || param->status != PJ_SUCCESS) {
607 if (param->status != PJ_SUCCESS) {
608 char errmsg[PJ_ERR_MSG_SIZE];
609
610 pj_strerror(param->status, errmsg, sizeof(errmsg));
611 PJ_LOG(1,(THIS_FILE,
612 "Client publication (PUBLISH) failed, status=%d, msg=%s",
613 param->status, errmsg));
614 } else {
615 PJ_LOG(1,(THIS_FILE,
616 "Client publication (PUBLISH) failed (%d/%.*s)",
617 param->code, (int)param->reason.slen,
618 param->reason.ptr));
619 }
620
621 pjsip_publishc_destroy(param->pubc);
622 acc->publish_sess = NULL;
623 }
624}
625
626
627/*
628 * Send PUBLISH request.
629 */
630static pj_status_t send_publish(int acc_id, pj_bool_t active)
631{
632 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
633 pjsua_acc *acc = &pjsua_var.acc[acc_id];
634 pjsip_pres_status pres_status;
635 pjsip_tx_data *tdata;
636 pj_status_t status;
637
638
639 /* Create PUBLISH request */
640 if (active) {
Benny Prijono8c6e8842007-02-24 15:33:54 +0000641 char *bpos;
642 pj_str_t entity;
643
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000644 status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata);
645 if (status != PJ_SUCCESS) {
646 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
647 goto on_error;
648 }
649
650 /* Set our online status: */
651 pj_bzero(&pres_status, sizeof(pres_status));
652 pres_status.info_cnt = 1;
653 pres_status.info[0].basic_open = acc->online_status;
Benny Prijonofe04fb52007-08-24 08:28:52 +0000654 pres_status.info[0].id = acc->cfg.pidf_tuple_id;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000655
Benny Prijono8c6e8842007-02-24 15:33:54 +0000656 /* Be careful not to send PIDF with presence entity ID containing
657 * "<" character.
658 */
659 if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) {
660 char *epos = pj_strchr(&acc_cfg->id, '>');
661 if (epos - bpos < 2) {
662 pj_assert(!"Unexpected invalid URI");
663 status = PJSIP_EINVALIDURI;
664 goto on_error;
665 }
666 entity.ptr = bpos+1;
667 entity.slen = epos - bpos - 1;
668 } else {
669 entity = acc_cfg->id;
670 }
671
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000672 /* Create and add PIDF message body */
673 status = pjsip_pres_create_pidf(tdata->pool, &pres_status,
Benny Prijono8c6e8842007-02-24 15:33:54 +0000674 &entity, &tdata->msg->body);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000675 if (status != PJ_SUCCESS) {
676 pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request",
677 status);
678 pjsip_tx_data_dec_ref(tdata);
679 goto on_error;
680 }
681 } else {
682 status = pjsip_publishc_unpublish(acc->publish_sess, &tdata);
683 if (status != PJ_SUCCESS) {
684 pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status);
685 goto on_error;
686 }
687 }
688
689 /* Add headers etc */
690 pjsua_process_msg_data(tdata, NULL);
691
692 /* Send the PUBLISH request */
693 status = pjsip_publishc_send(acc->publish_sess, tdata);
694 if (status != PJ_SUCCESS) {
695 pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status);
696 goto on_error;
697 }
698
699 acc->publish_state = acc->online_status;
700 return PJ_SUCCESS;
701
702on_error:
Benny Prijono29438152007-06-28 02:47:32 +0000703 if (acc->publish_sess) {
704 pjsip_publishc_destroy(acc->publish_sess);
705 acc->publish_sess = NULL;
706 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000707 return status;
708}
709
710
711/* Create client publish session */
Benny Prijono8b6834f2007-02-24 13:29:22 +0000712pj_status_t pjsua_pres_init_publish_acc(int acc_id)
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000713{
714 const pj_str_t STR_PRESENCE = { "presence", 8 };
715 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
716 pjsua_acc *acc = &pjsua_var.acc[acc_id];
717 pj_status_t status;
718
719 /* Create and init client publication session */
720 if (acc_cfg->publish_enabled) {
721
722 /* Create client publication */
723 status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb,
724 &acc->publish_sess);
725 if (status != PJ_SUCCESS) {
726 acc->publish_sess = NULL;
727 return status;
728 }
729
730 /* Initialize client publication */
731 status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE,
732 &acc_cfg->id, &acc_cfg->id,
733 &acc_cfg->id,
Benny Prijono703b7d72007-03-20 09:13:24 +0000734 60);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000735 if (status != PJ_SUCCESS) {
736 acc->publish_sess = NULL;
737 return status;
738 }
739
Benny Prijono703b7d72007-03-20 09:13:24 +0000740 /* Add credential for authentication */
Benny Prijono29438152007-06-28 02:47:32 +0000741 if (acc->cred_cnt) {
742 pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt,
743 acc->cred);
744 }
Benny Prijono703b7d72007-03-20 09:13:24 +0000745
746 /* Set route-set */
747 pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set);
748
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000749 /* Send initial PUBLISH request */
750 if (acc->online_status != 0) {
751 status = send_publish(acc_id, PJ_TRUE);
752 if (status != PJ_SUCCESS)
753 return status;
754 }
755
756 } else {
757 acc->publish_sess = NULL;
758 }
759
760 return PJ_SUCCESS;
761}
762
763
764/* Init presence for account */
765pj_status_t pjsua_pres_init_acc(int acc_id)
766{
767 pjsua_acc *acc = &pjsua_var.acc[acc_id];
768
769 /* Init presence subscription */
770 pj_list_init(&acc->pres_srv_list);
771
Benny Prijono8b6834f2007-02-24 13:29:22 +0000772 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000773}
774
775
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000776/* Terminate server subscription for the account */
777void pjsua_pres_delete_acc(int acc_id)
Benny Prijono834aee32006-02-19 01:38:06 +0000778{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000779 pjsua_acc *acc = &pjsua_var.acc[acc_id];
780 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijono834aee32006-02-19 01:38:06 +0000781 pjsua_srv_pres *uapres;
782
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000783 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +0000784
Benny Prijono922933b2007-01-21 16:23:56 +0000785 /* Notify all subscribers that we're no longer available */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000786 while (uapres != &acc->pres_srv_list) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000787
788 pjsip_pres_status pres_status;
789 pj_str_t reason = { "noresource", 10 };
790 pjsip_tx_data *tdata;
791
792 pjsip_pres_get_status(uapres->sub, &pres_status);
793
794 pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
795 pjsip_pres_set_status(uapres->sub, &pres_status);
796
797 if (pjsip_pres_notify(uapres->sub,
798 PJSIP_EVSUB_STATE_TERMINATED, NULL,
799 &reason, &tdata)==PJ_SUCCESS)
800 {
801 pjsip_pres_send_request(uapres->sub, tdata);
802 }
803
804 uapres = uapres->next;
805 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000806
Benny Prijono922933b2007-01-21 16:23:56 +0000807 /* Clear server presence subscription list because account might be reused
808 * later. */
809 pj_list_init(&acc->pres_srv_list);
810
811 /* Terminate presence publication, if any */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000812 if (acc->publish_sess) {
813 acc->online_status = PJ_FALSE;
814 send_publish(acc_id, PJ_FALSE);
815 if (acc->publish_sess) {
816 pjsip_publishc_destroy(acc->publish_sess);
817 acc->publish_sess = NULL;
818 }
819 acc_cfg->publish_enabled = PJ_FALSE;
820 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000821}
822
823
824/* Refresh subscription (e.g. when our online status has changed) */
825static void refresh_server_subscription(int acc_id)
826{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000827 pjsua_acc *acc = &pjsua_var.acc[acc_id];
828 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000829 pjsua_srv_pres *uapres;
830
831 uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
832
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000833 while (uapres != &acc->pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +0000834
835 pjsip_pres_status pres_status;
836 pjsip_tx_data *tdata;
837
838 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000839 if (pres_status.info[0].basic_open != acc->online_status) {
840 pres_status.info[0].basic_open = acc->online_status;
Benny Prijono834aee32006-02-19 01:38:06 +0000841 pjsip_pres_set_status(uapres->sub, &pres_status);
842
Benny Prijono21b9ad92006-08-15 13:11:22 +0000843 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) {
844 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000845 pjsip_pres_send_request(uapres->sub, tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000846 }
Benny Prijono834aee32006-02-19 01:38:06 +0000847 }
848
849 uapres = uapres->next;
850 }
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000851
Benny Prijono8b6834f2007-02-24 13:29:22 +0000852 /* Send PUBLISH if required. We only do this when we have a PUBLISH
853 * session. If we don't have a PUBLISH session, then it could be
854 * that we're waiting until registration has completed before we
855 * send the first PUBLISH.
856 */
857 if (acc_cfg->publish_enabled && acc->publish_sess) {
858 if (acc->publish_state != acc->online_status) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000859 send_publish(acc_id, PJ_TRUE);
860 }
861 }
Benny Prijono834aee32006-02-19 01:38:06 +0000862}
863
864
865
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000866/***************************************************************************
867 * Client subscription.
Benny Prijono834aee32006-02-19 01:38:06 +0000868 */
869
870/* Callback called when *client* subscription state has changed. */
871static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
872{
873 pjsua_buddy *buddy;
874
875 PJ_UNUSED_ARG(event);
876
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000877 PJSUA_LOCK();
878
Benny Prijonoa1e69682007-05-11 15:14:34 +0000879 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000880 if (buddy) {
881 PJ_LOG(3,(THIS_FILE,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000882 "Presence subscription to %.*s is %s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000883 (int)pjsua_var.buddy[buddy->index].uri.slen,
884 pjsua_var.buddy[buddy->index].uri.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +0000885 pjsip_evsub_get_state_name(sub)));
886
887 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
888 buddy->sub = NULL;
889 buddy->status.info_cnt = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000890 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000891 }
Benny Prijono9fc735d2006-05-28 14:58:12 +0000892
893 /* Call callback */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 if (pjsua_var.ua_cfg.cb.on_buddy_state)
895 (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
Benny Prijono834aee32006-02-19 01:38:06 +0000896 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000897
898 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000899}
900
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000901
902/* Callback when transaction state has changed. */
903static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
904 pjsip_transaction *tsx,
905 pjsip_event *event)
906{
907 pjsua_buddy *buddy;
908 pjsip_contact_hdr *contact_hdr;
909
910 PJSUA_LOCK();
911
Benny Prijonoa1e69682007-05-11 15:14:34 +0000912 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000913 if (!buddy) {
914 PJSUA_UNLOCK();
915 return;
916 }
917
918 /* We only use this to update buddy's Contact, when it's not
919 * set.
920 */
921 if (buddy->contact.slen != 0) {
922 /* Contact already set */
923 PJSUA_UNLOCK();
924 return;
925 }
926
927 /* Only care about 2xx response to outgoing SUBSCRIBE */
928 if (tsx->status_code/100 != 2 ||
929 tsx->role != PJSIP_UAC_ROLE ||
930 event->type != PJSIP_EVENT_RX_MSG ||
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000931 pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000932 {
933 PJSUA_UNLOCK();
934 return;
935 }
936
937 /* Find contact header. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000938 contact_hdr = (pjsip_contact_hdr*)
939 pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000940 PJSIP_H_CONTACT, NULL);
941 if (!contact_hdr) {
942 PJSUA_UNLOCK();
943 return;
944 }
945
Benny Prijonoa1e69682007-05-11 15:14:34 +0000946 buddy->contact.ptr = (char*)
947 pj_pool_alloc(pjsua_var.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000948 buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
949 contact_hdr->uri,
950 buddy->contact.ptr,
951 PJSIP_MAX_URL_SIZE);
952 if (buddy->contact.slen < 0)
953 buddy->contact.slen = 0;
954
955 PJSUA_UNLOCK();
956}
957
958
Benny Prijono834aee32006-02-19 01:38:06 +0000959/* Callback called when we receive NOTIFY */
960static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
961 pjsip_rx_data *rdata,
962 int *p_st_code,
963 pj_str_t **p_st_text,
964 pjsip_hdr *res_hdr,
965 pjsip_msg_body **p_body)
966{
967 pjsua_buddy *buddy;
968
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000969 PJSUA_LOCK();
970
Benny Prijonoa1e69682007-05-11 15:14:34 +0000971 buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono834aee32006-02-19 01:38:06 +0000972 if (buddy) {
973 /* Update our info. */
974 pjsip_pres_get_status(sub, &buddy->status);
Benny Prijono834aee32006-02-19 01:38:06 +0000975 }
976
977 /* The default is to send 200 response to NOTIFY.
978 * Just leave it there..
979 */
980 PJ_UNUSED_ARG(rdata);
981 PJ_UNUSED_ARG(p_st_code);
982 PJ_UNUSED_ARG(p_st_text);
983 PJ_UNUSED_ARG(res_hdr);
984 PJ_UNUSED_ARG(p_body);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000985
986 PJSUA_UNLOCK();
Benny Prijono834aee32006-02-19 01:38:06 +0000987}
988
989
990/* Event subscription callback. */
991static pjsip_evsub_user pres_callback =
992{
993 &pjsua_evsub_on_state,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000994 &pjsua_evsub_on_tsx_state,
Benny Prijono834aee32006-02-19 01:38:06 +0000995
996 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
997 * we want to authenticate
998 */
999
1000 &pjsua_evsub_on_rx_notify,
1001
1002 NULL, /* on_client_refresh: Use default behaviour, which is to
1003 * refresh client subscription. */
1004
1005 NULL, /* on_server_timeout: Use default behaviour, which is to send
1006 * NOTIFY to terminate.
1007 */
1008};
1009
1010
1011/* It does what it says.. */
1012static void subscribe_buddy_presence(unsigned index)
1013{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001014 pjsua_buddy *buddy;
1015 int acc_id;
1016 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001017 pj_str_t contact;
Benny Prijono834aee32006-02-19 01:38:06 +00001018 pjsip_tx_data *tdata;
1019 pj_status_t status;
1020
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001021 buddy = &pjsua_var.buddy[index];
1022 acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001023
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001024 acc = &pjsua_var.acc[acc_id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00001025
Benny Prijonob4a17c92006-07-10 14:40:21 +00001026 PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
1027 acc_id, index));
1028
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001029 /* Generate suitable Contact header */
1030 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
1031 acc_id, &buddy->uri);
1032 if (status != PJ_SUCCESS) {
1033 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
1034 return;
1035 }
1036
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001037 /* Create UAC dialog */
Benny Prijono834aee32006-02-19 01:38:06 +00001038 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001039 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001040 &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001041 &buddy->uri,
Benny Prijonof9c40c32007-06-28 07:20:26 +00001042 NULL, &buddy->dlg);
Benny Prijono834aee32006-02-19 01:38:06 +00001043 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001044 pjsua_perror(THIS_FILE, "Unable to create dialog",
1045 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001046 return;
1047 }
1048
Benny Prijonof9c40c32007-06-28 07:20:26 +00001049 status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001050 PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
1051 if (status != PJ_SUCCESS) {
1052 pjsua_var.buddy[index].sub = NULL;
1053 pjsua_perror(THIS_FILE, "Unable to create presence client",
1054 status);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001055 pjsip_dlg_terminate(buddy->dlg);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001056 return;
1057 }
1058
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001059 /* If account is locked to specific transport, then lock dialog
1060 * to this transport too.
1061 */
1062 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
1063 pjsip_tpselector tp_sel;
1064
1065 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
Benny Prijonof9c40c32007-06-28 07:20:26 +00001066 pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001067 }
1068
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001069 /* Set route-set */
1070 if (!pj_list_empty(&acc->route_set)) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001071 pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001072 }
1073
1074 /* Set credentials */
1075 if (acc->cred_cnt) {
Benny Prijonof9c40c32007-06-28 07:20:26 +00001076 pjsip_auth_clt_set_credentials( &buddy->dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001077 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001078 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001079
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001080 pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
Benny Prijono834aee32006-02-19 01:38:06 +00001081
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001082 status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001083 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001084 if (buddy->sub) {
1085 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1086 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001087 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001088 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
1089 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001090 return;
1091 }
1092
Benny Prijono21b9ad92006-08-15 13:11:22 +00001093 pjsua_process_msg_data(tdata, NULL);
1094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095 status = pjsip_pres_send_request(buddy->sub, tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001096 if (status != PJ_SUCCESS) {
Benny Prijonoa6992c52007-06-05 22:58:32 +00001097 if (buddy->sub) {
1098 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1099 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001100 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001101 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
1102 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001103 return;
1104 }
Benny Prijono834aee32006-02-19 01:38:06 +00001105}
1106
1107
1108/* It does what it says... */
1109static void unsubscribe_buddy_presence(unsigned index)
1110{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001111 pjsua_buddy *buddy;
Benny Prijono834aee32006-02-19 01:38:06 +00001112 pjsip_tx_data *tdata;
1113 pj_status_t status;
1114
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001115 buddy = &pjsua_var.buddy[index];
1116
1117 if (buddy->sub == NULL)
Benny Prijono834aee32006-02-19 01:38:06 +00001118 return;
1119
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001120 if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1121 pjsua_var.buddy[index].sub = NULL;
Benny Prijono834aee32006-02-19 01:38:06 +00001122 return;
1123 }
1124
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001125 status = pjsip_pres_initiate( buddy->sub, 0, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +00001126 if (status == PJ_SUCCESS) {
1127 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001128 status = pjsip_pres_send_request( buddy->sub, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +00001129 }
Benny Prijono834aee32006-02-19 01:38:06 +00001130
Benny Prijono48da92e2007-05-16 08:56:30 +00001131 if (status != PJ_SUCCESS && buddy->sub) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001132 pjsip_pres_terminate(buddy->sub, PJ_FALSE);
1133 buddy->sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001134 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
1135 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001136 }
1137}
1138
1139
Benny Prijonof9c40c32007-06-28 07:20:26 +00001140/* Lock all buddies */
1141#define LOCK_BUDDIES unsigned cnt_ = 0; \
1142 pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
1143 unsigned i_; \
1144 for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
1145 if (pjsua_var.buddy[i_].sub) { \
1146 dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
1147 pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
1148 } \
1149 } \
1150 PJSUA_LOCK();
1151
1152/* Unlock all buddies */
1153#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
1154 for (i_=0; i_<cnt_; ++i_) { \
1155 pjsip_dlg_dec_lock(dlg_list_[i_]); \
1156 }
1157
1158
1159
Benny Prijono834aee32006-02-19 01:38:06 +00001160/* It does what it says.. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161static void refresh_client_subscriptions(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001162{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001163 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001164
Benny Prijonof9c40c32007-06-28 07:20:26 +00001165 LOCK_BUDDIES;
1166
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001167 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001168
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001169 if (!pjsua_var.buddy[i].uri.slen)
1170 continue;
1171
1172 if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001173 subscribe_buddy_presence(i);
1174
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001175 } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) {
Benny Prijono834aee32006-02-19 01:38:06 +00001176 unsubscribe_buddy_presence(i);
1177
1178 }
1179 }
Benny Prijonof9c40c32007-06-28 07:20:26 +00001180
1181 UNLOCK_BUDDIES;
Benny Prijono834aee32006-02-19 01:38:06 +00001182}
1183
Benny Prijono7a5f5102007-05-29 00:33:09 +00001184/* Timer callback to re-create client subscription */
1185static void pres_timer_cb(pj_timer_heap_t *th,
1186 pj_timer_entry *entry)
1187{
1188 pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
1189
Benny Prijono7a5f5102007-05-29 00:33:09 +00001190 entry->id = PJ_FALSE;
1191 refresh_client_subscriptions();
1192
1193 pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
1194 entry->id = PJ_TRUE;
1195
Benny Prijonof9c40c32007-06-28 07:20:26 +00001196 PJ_UNUSED_ARG(th);
Benny Prijono7a5f5102007-05-29 00:33:09 +00001197}
1198
Benny Prijono834aee32006-02-19 01:38:06 +00001199
1200/*
1201 * Init presence
1202 */
1203pj_status_t pjsua_pres_init()
1204{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001205 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001206 pj_status_t status;
1207
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208 status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres);
Benny Prijono834aee32006-02-19 01:38:06 +00001209 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001210 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
1211 status);
Benny Prijono834aee32006-02-19 01:38:06 +00001212 }
1213
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001214 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1215 reset_buddy(i);
1216 }
1217
Benny Prijono834aee32006-02-19 01:38:06 +00001218 return status;
1219}
1220
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221
Benny Prijono834aee32006-02-19 01:38:06 +00001222/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001223 * Start presence subsystem.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001224 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001225pj_status_t pjsua_pres_start(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001226{
Benny Prijono7a5f5102007-05-29 00:33:09 +00001227 /* Start presence timer to re-subscribe to buddy's presence when
1228 * subscription has failed.
1229 */
1230 if (pjsua_var.pres_timer.id == PJ_FALSE) {
1231 pj_time_val pres_interval = {PJSUA_PRES_TIMER, 0};
1232
1233 pjsua_var.pres_timer.cb = &pres_timer_cb;
1234 pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.pres_timer,
1235 &pres_interval);
Benny Prijono97276602007-06-23 01:07:08 +00001236 pjsua_var.pres_timer.id = PJ_TRUE;
Benny Prijono7a5f5102007-05-29 00:33:09 +00001237 }
1238
Benny Prijono9fc735d2006-05-28 14:58:12 +00001239 return PJ_SUCCESS;
1240}
1241
1242
1243/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244 * Refresh presence subscriptions
Benny Prijono834aee32006-02-19 01:38:06 +00001245 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246void pjsua_pres_refresh()
Benny Prijono834aee32006-02-19 01:38:06 +00001247{
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001248 unsigned i;
1249
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001250 refresh_client_subscriptions();
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001251
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1253 if (pjsua_var.acc[i].valid)
1254 refresh_server_subscription(i);
1255 }
Benny Prijono834aee32006-02-19 01:38:06 +00001256}
1257
1258
1259/*
1260 * Shutdown presence.
1261 */
1262void pjsua_pres_shutdown(void)
1263{
Benny Prijono9fc735d2006-05-28 14:58:12 +00001264 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001265
Benny Prijono7a5f5102007-05-29 00:33:09 +00001266 if (pjsua_var.pres_timer.id != 0) {
1267 pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
1268 pjsua_var.pres_timer.id = PJ_FALSE;
1269 }
1270
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1272 if (!pjsua_var.acc[i].valid)
1273 continue;
1274 pjsua_pres_delete_acc(i);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001275 }
1276
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001277 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1278 pjsua_var.buddy[i].monitor = 0;
Benny Prijono834aee32006-02-19 01:38:06 +00001279 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001280
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001281 pjsua_pres_refresh();
Benny Prijono834aee32006-02-19 01:38:06 +00001282}