blob: 6f82256885a486292ad565c8b1a6842b344a7277 [file] [log] [blame]
Benny Prijonoe0312a72005-11-18 00:16:43 +00001/* $Id$ */
Benny Prijonoe7224612005-11-13 19:40:44 +00002/*
Benny Prijonoe0312a72005-11-18 00:16:43 +00003 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
Benny Prijonoe7224612005-11-13 19:40:44 +00004 *
Benny Prijonoe0312a72005-11-18 00:16:43 +00005 * 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.
Benny Prijonoe7224612005-11-13 19:40:44 +00009 *
Benny Prijonoe0312a72005-11-18 00:16:43 +000010 * This program is distributed in the hope that it will be useful,
Benny Prijonoe7224612005-11-13 19:40:44 +000011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Benny Prijonoe0312a72005-11-18 00:16:43 +000012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Benny Prijonoe7224612005-11-13 19:40:44 +000014 *
Benny Prijonoe0312a72005-11-18 00:16:43 +000015 * 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
Benny Prijonoe7224612005-11-13 19:40:44 +000018 */
19#include <pjsip_simple/presence.h>
20#include <pjsip/sip_transport.h>
21#include <pj/pool.h>
22#include <pj/string.h>
23#include <pj/guid.h>
24#include <pj/os.h>
25#include <stdio.h>
26
27/* Forward declarations. */
28static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
29static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
30 pjsip_event_sub_cb **cb, int *expires);
31static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
32static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
33static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
34
35/* Some string constants. */
36static pj_str_t PRESENCE_EVENT = { "presence", 8 };
37
38/* Accept types. */
39static pj_str_t accept_names[] = {
40 { "application/pidf+xml", 20 },
41 { "application/xpidf+xml", 21 }
42};
43static pjsip_media_type accept_types[] = {
44 {
45 { "application", 11 },
46 { "pidf+xml", 8 }
47 },
48 {
49 { "application", 11 },
50 { "xpidf+xml", 9 }
51 }
52};
53
54/* Callback that is registered by application. */
55static pjsip_presence_cb cb;
56
57/* Package callback to be register to event_notify */
58static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
59 &on_subscribe };
60
61/* Global/static callback to be registered to event_notify */
62static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
63 &on_sub_received_refresh,
64 NULL,
65 &on_received_notify,
66 NULL };
67
68/*
69 * Initialize presence module.
70 * This will register event package "presence" to event framework.
71 */
72PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
73{
74 pj_memcpy(&cb, pcb, sizeof(*pcb));
75 pjsip_event_sub_register_pkg( &PRESENCE_EVENT,
76 sizeof(accept_names)/sizeof(accept_names[0]),
77 accept_names,
78 &pkg_cb);
79}
80
81/*
82 * Create presence subscription.
83 */
84PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
85 const pj_str_t *local_url,
86 const pj_str_t *remote_url,
87 int expires,
88 void *user_data )
89{
90 pjsip_event_sub *sub;
91 pjsip_presentity *pres;
92
93 if (expires < 0)
94 expires = 300;
95
96 /* Create event subscription */
97 sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT,
98 expires,
99 sizeof(accept_names)/sizeof(accept_names[0]),
100 accept_names,
101 NULL, &sub_cb);
102 if (!sub)
103 return NULL;
104
105 /* Allocate presence descriptor. */
106 pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
107 pres->sub = sub;
108 pres->user_data = user_data;
109 sub->user_data = pres;
110
111 return pres;
112}
113
114/*
115 * Send SUBSCRIBE.
116 */
117PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
118{
119 return pjsip_event_sub_subscribe( pres->sub );
120}
121
122/*
123 * Set credentials to be used for outgoing requests.
124 */
125PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
126 int count,
127 const pjsip_cred_info cred[])
128{
129 return pjsip_event_sub_set_credentials(pres->sub, count, cred);
130}
131
132/*
133 * Set route-set.
134 */
135PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
136 const pjsip_route_hdr *hdr )
137{
138 return pjsip_event_sub_set_route_set( pres->sub, hdr );
139}
140
141/*
142 * Unsubscribe.
143 */
144PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
145{
146 return pjsip_event_sub_unsubscribe(pres->sub);
147}
148
149/*
150 * This is the pjsip_msg_body callback to print XML body.
151 */
152static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
153{
154 return pj_xml_print( body->data, buf, size, PJ_TRUE );
155}
156
157/*
158 * Create and initialize PIDF document and msg body (notifier only).
159 */
160static pj_status_t init_presence_info( pjsip_presentity *pres )
161{
162 pj_str_t uri;
163 pj_pool_t *pool = pres->sub->pool;
164 char tmp[PJSIP_MAX_URL_SIZE];
165 pjpidf_tuple *tuple;
166 const pjsip_media_type *content_type = NULL;
167
168 pj_assert(pres->uas_body == NULL);
169
170 /* Make entity_id */
171 uri.ptr = tmp;
172 uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri,
173 tmp, sizeof(tmp));
174 if (uri.slen < 0)
175 return -1;
176
177 if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
178 pj_str_t s;
179
180 /* Create <presence>. */
181 pres->uas_data.pidf = pjpidf_create(pool, &s);
182
183 /* Create <tuple> */
184 pj_create_unique_string(pool, &s);
185 tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
186
187 /* Set <contact> */
188 s.ptr = tmp;
189 s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
190 if (s.slen < 0)
191 return -1;
192 pjpidf_tuple_set_contact(pool, tuple, &s);
193
194 /* Content-Type */
195 content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
196
197 } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
198
199 /* Create XPIDF */
200 pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
201
202 /* Content-Type. */
203 content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
204 }
205
206 /* Create message body */
207 pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
208 pres->uas_body->content_type = *content_type;
209 pres->uas_body->data = pres->uas_data.pidf;
210 pres->uas_body->len = 0;
211 pres->uas_body->print_body = &print_xml;
212
213 return 0;
214}
215
216/*
217 * Send NOTIFY and set subscription state.
218 */
219PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
220 pjsip_event_sub_state state,
221 pj_bool_t is_online )
222{
223 pj_str_t reason = { "", 0 };
224
225 if (pres->uas_data.pidf == NULL) {
226 if (init_presence_info(pres) != 0)
227 return -1;
228 }
229
230 /* Update basic status in PIDF/XPIDF document. */
231 if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
232 pjpidf_tuple *first;
233 pjpidf_status *status;
234 pj_time_val now;
235 pj_parsed_time pnow;
236
237 first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
238 pj_assert(first);
239 status = pjpidf_op.tuple.get_status(first);
240 pj_assert(status);
241 pjpidf_op.status.set_basic_open(status, is_online);
242
243 /* Update timestamp. */
244 if (pres->timestamp.ptr == 0) {
245 pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
246 }
247 pj_gettimeofday(&now);
248 pj_time_decode(&now, &pnow);
249 pres->timestamp.slen = sprintf(pres->timestamp.ptr,
250 "%04d-%02d-%02dT%02d:%02d:%02dZ",
251 pnow.year, pnow.mon, pnow.day,
252 pnow.hour, pnow.min, pnow.sec);
253 pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
254
255 } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
256 pjxpidf_set_status( pres->uas_data.xpidf, is_online );
257
258 } else {
259 pj_assert(0);
260 }
261
262 /* Send notify. */
263 return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
264}
265
266/*
267 * Destroy subscription (can be called for both subscriber and notifier).
268 */
269PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
270{
271 return pjsip_event_sub_destroy(pres->sub);
272}
273
274/*
275 * This callback is called by event framework to query whether we want to
276 * accept an incoming subscription.
277 */
278static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
279{
280 if (cb.accept_presence) {
281 (*cb.accept_presence)(rdata, status);
282 }
283}
284
285/*
286 * This callback is called by event framework after we accept the incoming
287 * subscription, to notify about the new subscription instance.
288 */
289static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
290 pjsip_event_sub_cb **set_sub_cb, int *expires)
291{
292 pjsip_presentity *pres;
293 pjsip_accept_hdr *accept;
294
295 pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
296 pres->sub = sub;
297 pres->pres_type = PJSIP_PRES_TYPE_PIDF;
298 sub->user_data = pres;
299 *set_sub_cb = &sub_cb;
300
301 accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
302 if (accept) {
303 unsigned i;
304 int found = 0;
305 for (i=0; i<accept->count && !found; ++i) {
306 int j;
307 for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
308 if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
309 pres->pres_type = j;
310 found = 1;
311 break;
312 }
313 }
314 }
315 pj_assert(found );
316 }
317
318 (*cb.on_received_request)(pres, rdata, expires);
319}
320
321/*
322 * This callback is called by event framework when the subscription is
323 * terminated.
324 */
325static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
326{
327 pjsip_presentity *pres = sub->user_data;
328 if (cb.on_terminated)
329 (*cb.on_terminated)(pres, reason);
330}
331
332/*
333 * This callback is called by event framework when it receives incoming
334 * SUBSCRIBE request to refresh the subscription.
335 */
336static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
337{
338 pjsip_presentity *pres = sub->user_data;
339 if (cb.on_received_refresh)
340 (*cb.on_received_refresh)(pres, rdata);
341}
342
343/*
344 * This callback is called by event framework when it receives incoming
345 * NOTIFY request.
346 */
347static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
348{
349 pjsip_presentity *pres = sub->user_data;
350
351 if (cb.on_received_update) {
352 pj_status_t is_open;
353 pjsip_msg_body *body;
354 int i;
355
356 body = rdata->msg->body;
357 if (!body)
358 return;
359
360 for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
361 if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
362 !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
363 {
364 break;
365 }
366 }
367
368 if (i==PJSIP_PRES_TYPE_PIDF) {
369 pjpidf_pres *pres;
370 pjpidf_tuple *tuple;
371 pjpidf_status *status;
372
373 pres = pjpidf_parse(rdata->pool, body->data, body->len);
374 if (!pres)
375 return;
376 tuple = pjpidf_pres_get_first_tuple(pres);
377 if (!tuple)
378 return;
379 status = pjpidf_tuple_get_status(tuple);
380 if (!status)
381 return;
382 is_open = pjpidf_status_is_basic_open(status);
383
384 } else if (i==PJSIP_PRES_TYPE_XPIDF) {
385 pjxpidf_pres *pres;
386
387 pres = pjxpidf_parse(rdata->pool, body->data, body->len);
388 if (!pres)
389 return;
390 is_open = pjxpidf_get_status(pres);
391
392 } else {
393 return;
394 }
395
396 (*cb.on_received_update)(pres, is_open);
397 }
398}
399