blob: 174a106d13c430cb7625293f51922a4bc07ee26c [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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 Prijono834aee32006-02-19 01:38:06 +000020
21/*
22 * pjsua_pres.c
23 *
24 * Presence related stuffs.
25 */
26
27#define THIS_FILE "pjsua_pres.c"
28
29
30
31/* **************************************************************************
32 * THE FOLLOWING PART HANDLES SERVER SUBSCRIPTION
33 * **************************************************************************
34 */
35
36/* Proto */
37static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
38
39/* The module instance. */
40static pjsip_module mod_pjsua_pres =
41{
42 NULL, NULL, /* prev, next. */
43 { "mod-pjsua-pres", 14 }, /* Name. */
44 -1, /* Id */
45 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono834aee32006-02-19 01:38:06 +000046 NULL, /* load() */
47 NULL, /* start() */
48 NULL, /* stop() */
49 NULL, /* unload() */
50 &pres_on_rx_request, /* on_rx_request() */
51 NULL, /* on_rx_response() */
52 NULL, /* on_tx_request. */
53 NULL, /* on_tx_response() */
54 NULL, /* on_tsx_state() */
55
56};
57
58
59/* Callback called when *server* subscription state has changed. */
60static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
61{
62 pjsua_srv_pres *uapres = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
63
64 PJ_UNUSED_ARG(event);
65
66 if (uapres) {
67 PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s",
68 uapres->remote, pjsip_evsub_get_state_name(sub)));
69
70 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
71 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
72 pj_list_erase(uapres);
73 }
74 }
75}
76
77/* This is called when request is received.
78 * We need to check for incoming SUBSCRIBE request.
79 */
80static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
81{
Benny Prijonoa91a0032006-02-26 21:23:45 +000082 int acc_index;
Benny Prijono834aee32006-02-19 01:38:06 +000083 pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
84 pjsua_srv_pres *uapres;
85 pjsip_evsub *sub;
86 pjsip_evsub_user pres_cb;
87 pjsip_tx_data *tdata;
88 pjsip_pres_status pres_status;
89 pjsip_dialog *dlg;
90 pj_status_t status;
91
92 if (pjsip_method_cmp(req_method, &pjsip_subscribe_method) != 0)
93 return PJ_FALSE;
94
95 /* Incoming SUBSCRIBE: */
96
Benny Prijonoa91a0032006-02-26 21:23:45 +000097 /* Find which account for the incoming request. */
98 acc_index = pjsua_find_account_for_incoming(rdata);
99
Benny Prijono834aee32006-02-19 01:38:06 +0000100 /* Create UAS dialog: */
101 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000102 &pjsua.acc[acc_index].contact_uri,
103 &dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000104 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000105 pjsua_perror(THIS_FILE,
106 "Unable to create UAS dialog for subscription",
107 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000108 return PJ_FALSE;
109 }
110
111 /* Init callback: */
112 pj_memset(&pres_cb, 0, sizeof(pres_cb));
113 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
114
115 /* Create server presence subscription: */
116 status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
117 if (status != PJ_SUCCESS) {
118 PJ_TODO(DESTROY_DIALOG);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000119 pjsua_perror(THIS_FILE, "Unable to create server subscription",
120 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000121 return PJ_FALSE;
122 }
123
124 /* Attach our data to the subscription: */
125 uapres = pj_pool_alloc(dlg->pool, sizeof(pjsua_srv_pres));
126 uapres->sub = sub;
127 uapres->remote = pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
128 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
129 uapres->remote, PJSIP_MAX_URL_SIZE);
130 if (status < 1)
131 pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
132 else
133 uapres->remote[status] = '\0';
134
135 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, uapres);
136
137 /* Add server subscription to the list: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000138 pj_list_push_back(&pjsua.acc[acc_index].pres_srv_list, uapres);
Benny Prijono834aee32006-02-19 01:38:06 +0000139
140
141 /* Create and send 200 (OK) to the SUBSCRIBE request: */
142 status = pjsip_pres_accept(sub, rdata, 200, NULL);
143 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000144 pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
145 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000146 pj_list_erase(uapres);
147 return PJ_FALSE;
148 }
149
150
151 /* Set our online status: */
152 pj_memset(&pres_status, 0, sizeof(pres_status));
153 pres_status.info_cnt = 1;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000154 pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status;
Benny Prijono834aee32006-02-19 01:38:06 +0000155 //Both pjsua.local_uri and pjsua.contact_uri are enclosed in "<" and ">"
156 //causing XML parsing to fail.
157 //pres_status.info[0].contact = pjsua.local_uri;
158
159 pjsip_pres_set_status(sub, &pres_status);
160
161 /* Create and send the first NOTIFY to active subscription: */
162 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, NULL,
163 NULL, &tdata);
164 if (status == PJ_SUCCESS)
165 status = pjsip_pres_send_request( sub, tdata);
166
167 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000168 pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
169 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000170 pj_list_erase(uapres);
171 return PJ_FALSE;
172 }
173
174
175 /* Done: */
176
177 return PJ_TRUE;
178}
179
180
181/* Refresh subscription (e.g. when our online status has changed) */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000182static void refresh_server_subscription(int acc_index)
Benny Prijono834aee32006-02-19 01:38:06 +0000183{
184 pjsua_srv_pres *uapres;
185
Benny Prijonoa91a0032006-02-26 21:23:45 +0000186 uapres = pjsua.acc[acc_index].pres_srv_list.next;
Benny Prijono834aee32006-02-19 01:38:06 +0000187
Benny Prijonoa91a0032006-02-26 21:23:45 +0000188 while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
Benny Prijono834aee32006-02-19 01:38:06 +0000189
190 pjsip_pres_status pres_status;
191 pjsip_tx_data *tdata;
192
193 pjsip_pres_get_status(uapres->sub, &pres_status);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000194 if (pres_status.info[0].basic_open != pjsua.acc[acc_index].online_status) {
195 pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status;
Benny Prijono834aee32006-02-19 01:38:06 +0000196 pjsip_pres_set_status(uapres->sub, &pres_status);
197
198 if (pjsua.quit_flag) {
199 pj_str_t reason = { "noresource", 10 };
200 if (pjsip_pres_notify(uapres->sub,
201 PJSIP_EVSUB_STATE_TERMINATED, NULL,
202 &reason, &tdata)==PJ_SUCCESS)
203 {
204 pjsip_pres_send_request(uapres->sub, tdata);
205 }
206 } else {
207 if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS)
208 pjsip_pres_send_request(uapres->sub, tdata);
209 }
210 }
211
212 uapres = uapres->next;
213 }
214}
215
216
217
218/* **************************************************************************
219 * THE FOLLOWING PART HANDLES CLIENT SUBSCRIPTION
220 * **************************************************************************
221 */
222
223/* Callback called when *client* subscription state has changed. */
224static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
225{
226 pjsua_buddy *buddy;
227
228 PJ_UNUSED_ARG(event);
229
230 buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
231 if (buddy) {
232 PJ_LOG(3,(THIS_FILE,
233 "Presence subscription to %s is %s",
234 buddy->uri.ptr,
235 pjsip_evsub_get_state_name(sub)));
236
237 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
238 buddy->sub = NULL;
239 buddy->status.info_cnt = 0;
240 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
241 }
242 }
243}
244
245/* Callback called when we receive NOTIFY */
246static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
247 pjsip_rx_data *rdata,
248 int *p_st_code,
249 pj_str_t **p_st_text,
250 pjsip_hdr *res_hdr,
251 pjsip_msg_body **p_body)
252{
253 pjsua_buddy *buddy;
254
255 buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
256 if (buddy) {
257 /* Update our info. */
258 pjsip_pres_get_status(sub, &buddy->status);
259
260 if (buddy->status.info_cnt) {
261 PJ_LOG(3,(THIS_FILE, "%s is %s",
262 buddy->uri.ptr,
263 (buddy->status.info[0].basic_open?"online":"offline")));
264 } else {
265 PJ_LOG(3,(THIS_FILE, "No presence info for %s",
266 buddy->uri.ptr));
267 }
268 }
269
270 /* The default is to send 200 response to NOTIFY.
271 * Just leave it there..
272 */
273 PJ_UNUSED_ARG(rdata);
274 PJ_UNUSED_ARG(p_st_code);
275 PJ_UNUSED_ARG(p_st_text);
276 PJ_UNUSED_ARG(res_hdr);
277 PJ_UNUSED_ARG(p_body);
278}
279
280
281/* Event subscription callback. */
282static pjsip_evsub_user pres_callback =
283{
284 &pjsua_evsub_on_state,
285
286 NULL, /* on_tsx_state: don't care about transaction state. */
287
288 NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
289 * we want to authenticate
290 */
291
292 &pjsua_evsub_on_rx_notify,
293
294 NULL, /* on_client_refresh: Use default behaviour, which is to
295 * refresh client subscription. */
296
297 NULL, /* on_server_timeout: Use default behaviour, which is to send
298 * NOTIFY to terminate.
299 */
300};
301
302
303/* It does what it says.. */
304static void subscribe_buddy_presence(unsigned index)
305{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000306 int acc_index;
Benny Prijono834aee32006-02-19 01:38:06 +0000307 pjsip_dialog *dlg;
308 pjsip_tx_data *tdata;
309 pj_status_t status;
310
Benny Prijonoa91a0032006-02-26 21:23:45 +0000311 acc_index = pjsua.buddies[index].acc_index;
312
Benny Prijono834aee32006-02-19 01:38:06 +0000313 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoa91a0032006-02-26 21:23:45 +0000314 &pjsua.acc[acc_index].local_uri,
315 &pjsua.acc[acc_index].contact_uri,
Benny Prijono834aee32006-02-19 01:38:06 +0000316 &pjsua.buddies[index].uri,
317 NULL, &dlg);
318 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000319 pjsua_perror(THIS_FILE, "Unable to create dialog",
320 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000321 return;
322 }
323
324 status = pjsip_pres_create_uac( dlg, &pres_callback,
325 &pjsua.buddies[index].sub);
326 if (status != PJ_SUCCESS) {
327 pjsua.buddies[index].sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000328 pjsua_perror(THIS_FILE, "Unable to create presence client",
329 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000330 return;
331 }
332
333 pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id,
334 &pjsua.buddies[index]);
335
Benny Prijonoa91a0032006-02-26 21:23:45 +0000336 status = pjsip_pres_initiate(pjsua.buddies[index].sub, -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000337 if (status != PJ_SUCCESS) {
338 pjsua.buddies[index].sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000339 pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
340 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000341 return;
342 }
343
344 status = pjsip_pres_send_request(pjsua.buddies[index].sub, tdata);
345 if (status != PJ_SUCCESS) {
346 pjsua.buddies[index].sub = NULL;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000347 pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE",
348 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000349 return;
350 }
351
352 PJ_TODO(DESTROY_DIALOG_ON_ERROR);
353}
354
355
356/* It does what it says... */
357static void unsubscribe_buddy_presence(unsigned index)
358{
359 pjsip_tx_data *tdata;
360 pj_status_t status;
361
362 if (pjsua.buddies[index].sub == NULL)
363 return;
364
365 if (pjsip_evsub_get_state(pjsua.buddies[index].sub) ==
366 PJSIP_EVSUB_STATE_TERMINATED)
367 {
368 pjsua.buddies[index].sub = NULL;
369 return;
370 }
371
372 status = pjsip_pres_initiate( pjsua.buddies[index].sub, 0, &tdata);
373 if (status == PJ_SUCCESS)
374 status = pjsip_pres_send_request( pjsua.buddies[index].sub, tdata );
375
376 if (status == PJ_SUCCESS) {
377
378 //pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id,
379 // NULL);
380 //pjsua.buddies[index].sub = NULL;
381
382 } else {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000383 pjsua_perror(THIS_FILE, "Unable to unsubscribe presence",
384 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000385 }
386}
387
388
389/* It does what it says.. */
390static void refresh_client_subscription(void)
391{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000392 int i;
Benny Prijono834aee32006-02-19 01:38:06 +0000393
394 for (i=0; i<pjsua.buddy_cnt; ++i) {
395
396 if (pjsua.buddies[i].monitor && !pjsua.buddies[i].sub) {
397 subscribe_buddy_presence(i);
398
399 } else if (!pjsua.buddies[i].monitor && pjsua.buddies[i].sub) {
400 unsubscribe_buddy_presence(i);
401
402 }
403 }
404}
405
406
407/*
408 * Init presence
409 */
410pj_status_t pjsua_pres_init()
411{
412 pj_status_t status;
413
414 status = pjsip_endpt_register_module( pjsua.endpt, &mod_pjsua_pres);
415 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000416 pjsua_perror(THIS_FILE, "Unable to register pjsua presence module",
417 status);
Benny Prijono834aee32006-02-19 01:38:06 +0000418 }
419
420 return status;
421}
422
423/*
424 * Refresh presence
425 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000426void pjsua_pres_refresh(int acc_index)
Benny Prijono834aee32006-02-19 01:38:06 +0000427{
428 refresh_client_subscription();
Benny Prijonoa91a0032006-02-26 21:23:45 +0000429 refresh_server_subscription(acc_index);
Benny Prijono834aee32006-02-19 01:38:06 +0000430}
431
432
433/*
434 * Shutdown presence.
435 */
436void pjsua_pres_shutdown(void)
437{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000438 int acc_index;
439 int i;
Benny Prijono834aee32006-02-19 01:38:06 +0000440
Benny Prijonoa91a0032006-02-26 21:23:45 +0000441 for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
442 pjsua.acc[acc_index].online_status = 0;
443 }
444
Benny Prijono834aee32006-02-19 01:38:06 +0000445 for (i=0; i<pjsua.buddy_cnt; ++i) {
446 pjsua.buddies[i].monitor = 0;
447 }
Benny Prijonoa91a0032006-02-26 21:23:45 +0000448
449 for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
450 pjsua_pres_refresh(acc_index);
451 }
Benny Prijono834aee32006-02-19 01:38:06 +0000452}
453
454/*
455 * Dump presence status.
456 */
Benny Prijono1a174142006-03-01 20:46:13 +0000457void pjsua_pres_dump(pj_bool_t detail)
Benny Prijono834aee32006-02-19 01:38:06 +0000458{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000459 int acc_index;
460 int i;
Benny Prijono834aee32006-02-19 01:38:06 +0000461
Benny Prijono1a174142006-03-01 20:46:13 +0000462
463 /*
464 * When no detail is required, just dump number of server and client
465 * subscriptions.
466 */
467 if (detail == PJ_FALSE) {
468
469 int count = 0;
470
471 for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
472
473 if (!pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) {
474 struct pjsua_srv_pres *uapres;
475
476 uapres = pjsua.acc[acc_index].pres_srv_list.next;
477 while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
478 ++count;
479 uapres = uapres->next;
480 }
481 }
482 }
483
484 PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d",
485 count));
486
487 count = 0;
488
489 for (i=0; i<pjsua.buddy_cnt; ++i) {
490 if (pjsua.buddies[i].sub) {
491 ++count;
492 }
493 }
494
495 PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d",
496 count));
497 return;
498 }
499
500
501 /*
502 * Dumping all server (UAS) subscriptions
503 */
Benny Prijono834aee32006-02-19 01:38:06 +0000504 PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
Benny Prijono1a174142006-03-01 20:46:13 +0000505
Benny Prijonoa91a0032006-02-26 21:23:45 +0000506 for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
Benny Prijono834aee32006-02-19 01:38:06 +0000507
Benny Prijonoa91a0032006-02-26 21:23:45 +0000508 PJ_LOG(3,(THIS_FILE, " %.*s",
509 (int)pjsua.acc[acc_index].local_uri.slen,
510 pjsua.acc[acc_index].local_uri.ptr));
Benny Prijono834aee32006-02-19 01:38:06 +0000511
Benny Prijonoa91a0032006-02-26 21:23:45 +0000512 if (pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) {
Benny Prijono1a174142006-03-01 20:46:13 +0000513
Benny Prijonoa91a0032006-02-26 21:23:45 +0000514 PJ_LOG(3,(THIS_FILE, " - none - "));
Benny Prijono1a174142006-03-01 20:46:13 +0000515
Benny Prijonoa91a0032006-02-26 21:23:45 +0000516 } else {
517 struct pjsua_srv_pres *uapres;
518
519 uapres = pjsua.acc[acc_index].pres_srv_list.next;
520 while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
521
522 PJ_LOG(3,(THIS_FILE, " %10s %s",
523 pjsip_evsub_get_state_name(uapres->sub),
524 uapres->remote));
525
526 uapres = uapres->next;
527 }
Benny Prijono834aee32006-02-19 01:38:06 +0000528 }
529 }
530
Benny Prijono1a174142006-03-01 20:46:13 +0000531 /*
532 * Dumping all client (UAC) subscriptions
533 */
Benny Prijono834aee32006-02-19 01:38:06 +0000534 PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
Benny Prijono1a174142006-03-01 20:46:13 +0000535
Benny Prijono834aee32006-02-19 01:38:06 +0000536 if (pjsua.buddy_cnt == 0) {
Benny Prijono1a174142006-03-01 20:46:13 +0000537
Benny Prijono834aee32006-02-19 01:38:06 +0000538 PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
Benny Prijono1a174142006-03-01 20:46:13 +0000539
Benny Prijono834aee32006-02-19 01:38:06 +0000540 } else {
541 for (i=0; i<pjsua.buddy_cnt; ++i) {
542
543 if (pjsua.buddies[i].sub) {
544 PJ_LOG(3,(THIS_FILE, " %10s %s",
545 pjsip_evsub_get_state_name(pjsua.buddies[i].sub),
546 pjsua.buddies[i].uri.ptr));
547 } else {
548 PJ_LOG(3,(THIS_FILE, " %10s %s",
549 "(null)",
550 pjsua.buddies[i].uri.ptr));
551 }
552 }
553 }
554}
555