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