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