/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.c 7 8/24/05 10:33a Bennylp $ */ #include #include #include #include #include #include #include /* Forward declarations. */ static void on_query_subscribe(pjsip_rx_data *rdata, int *status); static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, pjsip_event_sub_cb **cb, int *expires); static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); /* Some string constants. */ static pj_str_t PRESENCE_EVENT = { "presence", 8 }; /* Accept types. */ static pj_str_t accept_names[] = { { "application/pidf+xml", 20 }, { "application/xpidf+xml", 21 } }; static pjsip_media_type accept_types[] = { { { "application", 11 }, { "pidf+xml", 8 } }, { { "application", 11 }, { "xpidf+xml", 9 } } }; /* Callback that is registered by application. */ static pjsip_presence_cb cb; /* Package callback to be register to event_notify */ static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, &on_subscribe }; /* Global/static callback to be registered to event_notify */ static pjsip_event_sub_cb sub_cb = { &on_sub_terminated, &on_sub_received_refresh, NULL, &on_received_notify, NULL }; /* * Initialize presence module. * This will register event package "presence" to event framework. */ PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb) { pj_memcpy(&cb, pcb, sizeof(*pcb)); pjsip_event_sub_register_pkg( &PRESENCE_EVENT, sizeof(accept_names)/sizeof(accept_names[0]), accept_names, &pkg_cb); } /* * Create presence subscription. */ PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, const pj_str_t *local_url, const pj_str_t *remote_url, int expires, void *user_data ) { pjsip_event_sub *sub; pjsip_presentity *pres; if (expires < 0) expires = 300; /* Create event subscription */ sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, expires, sizeof(accept_names)/sizeof(accept_names[0]), accept_names, NULL, &sub_cb); if (!sub) return NULL; /* Allocate presence descriptor. */ pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); pres->sub = sub; pres->user_data = user_data; sub->user_data = pres; return pres; } /* * Send SUBSCRIBE. */ PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ) { return pjsip_event_sub_subscribe( pres->sub ); } /* * Set credentials to be used for outgoing requests. */ PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, int count, const pjsip_cred_info cred[]) { return pjsip_event_sub_set_credentials(pres->sub, count, cred); } /* * Set route-set. */ PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, const pjsip_route_hdr *hdr ) { return pjsip_event_sub_set_route_set( pres->sub, hdr ); } /* * Unsubscribe. */ PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ) { return pjsip_event_sub_unsubscribe(pres->sub); } /* * This is the pjsip_msg_body callback to print XML body. */ static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size) { return pj_xml_print( body->data, buf, size, PJ_TRUE ); } /* * Create and initialize PIDF document and msg body (notifier only). */ static pj_status_t init_presence_info( pjsip_presentity *pres ) { pj_str_t uri; pj_pool_t *pool = pres->sub->pool; char tmp[PJSIP_MAX_URL_SIZE]; pjpidf_tuple *tuple; const pjsip_media_type *content_type = NULL; pj_assert(pres->uas_body == NULL); /* Make entity_id */ uri.ptr = tmp; uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, tmp, sizeof(tmp)); if (uri.slen < 0) return -1; if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { pj_str_t s; /* Create . */ pres->uas_data.pidf = pjpidf_create(pool, &s); /* Create */ pj_create_unique_string(pool, &s); tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s); /* Set */ s.ptr = tmp; s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp)); if (s.slen < 0) return -1; pjpidf_tuple_set_contact(pool, tuple, &s); /* Content-Type */ content_type = &accept_types[PJSIP_PRES_TYPE_PIDF]; } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { /* Create XPIDF */ pres->uas_data.xpidf = pjxpidf_create(pool, &uri); /* Content-Type. */ content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF]; } /* Create message body */ pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); pres->uas_body->content_type = *content_type; pres->uas_body->data = pres->uas_data.pidf; pres->uas_body->len = 0; pres->uas_body->print_body = &print_xml; return 0; } /* * Send NOTIFY and set subscription state. */ PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, pjsip_event_sub_state state, pj_bool_t is_online ) { pj_str_t reason = { "", 0 }; if (pres->uas_data.pidf == NULL) { if (init_presence_info(pres) != 0) return -1; } /* Update basic status in PIDF/XPIDF document. */ if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { pjpidf_tuple *first; pjpidf_status *status; pj_time_val now; pj_parsed_time pnow; first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf); pj_assert(first); status = pjpidf_op.tuple.get_status(first); pj_assert(status); pjpidf_op.status.set_basic_open(status, is_online); /* Update timestamp. */ if (pres->timestamp.ptr == 0) { pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24); } pj_gettimeofday(&now); pj_time_decode(&now, &pnow); pres->timestamp.slen = sprintf(pres->timestamp.ptr, "%04d-%02d-%02dT%02d:%02d:%02dZ", pnow.year, pnow.mon, pnow.day, pnow.hour, pnow.min, pnow.sec); pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp); } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { pjxpidf_set_status( pres->uas_data.xpidf, is_online ); } else { pj_assert(0); } /* Send notify. */ return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body); } /* * Destroy subscription (can be called for both subscriber and notifier). */ PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ) { return pjsip_event_sub_destroy(pres->sub); } /* * This callback is called by event framework to query whether we want to * accept an incoming subscription. */ static void on_query_subscribe(pjsip_rx_data *rdata, int *status) { if (cb.accept_presence) { (*cb.accept_presence)(rdata, status); } } /* * This callback is called by event framework after we accept the incoming * subscription, to notify about the new subscription instance. */ static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, pjsip_event_sub_cb **set_sub_cb, int *expires) { pjsip_presentity *pres; pjsip_accept_hdr *accept; pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); pres->sub = sub; pres->pres_type = PJSIP_PRES_TYPE_PIDF; sub->user_data = pres; *set_sub_cb = &sub_cb; accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; int found = 0; for (i=0; icount && !found; ++i) { int j; for (j=0; jvalues[i], &accept_names[j])) { pres->pres_type = j; found = 1; break; } } } pj_assert(found ); } (*cb.on_received_request)(pres, rdata, expires); } /* * This callback is called by event framework when the subscription is * terminated. */ static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason) { pjsip_presentity *pres = sub->user_data; if (cb.on_terminated) (*cb.on_terminated)(pres, reason); } /* * This callback is called by event framework when it receives incoming * SUBSCRIBE request to refresh the subscription. */ static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata) { pjsip_presentity *pres = sub->user_data; if (cb.on_received_refresh) (*cb.on_received_refresh)(pres, rdata); } /* * This callback is called by event framework when it receives incoming * NOTIFY request. */ static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata) { pjsip_presentity *pres = sub->user_data; if (cb.on_received_update) { pj_status_t is_open; pjsip_msg_body *body; int i; body = rdata->msg->body; if (!body) return; for (i=0; icontent_type.type, &accept_types[i].type) && !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype)) { break; } } if (i==PJSIP_PRES_TYPE_PIDF) { pjpidf_pres *pres; pjpidf_tuple *tuple; pjpidf_status *status; pres = pjpidf_parse(rdata->pool, body->data, body->len); if (!pres) return; tuple = pjpidf_pres_get_first_tuple(pres); if (!tuple) return; status = pjpidf_tuple_get_status(tuple); if (!status) return; is_open = pjpidf_status_is_basic_open(status); } else if (i==PJSIP_PRES_TYPE_XPIDF) { pjxpidf_pres *pres; pres = pjxpidf_parse(rdata->pool, body->data, body->len); if (!pres) return; is_open = pjxpidf_get_status(pres); } else { return; } (*cb.on_received_update)(pres, is_open); } }