Changeset 197 for pjproject/trunk/pjsip/src/pjsip-simple/presence.c
- Timestamp:
- Feb 19, 2006 1:38:06 AM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip/src/pjsip-simple/presence.c
- Property svn:keywords set to id
r65 r197 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjsip_simple/presence.h> 20 #include <pjsip/sip_transport.h> 19 #include <pjsip-simple/presence.h> 20 #include <pjsip-simple/errno.h> 21 #include <pjsip-simple/evsub_msg.h> 22 #include <pjsip/sip_module.h> 23 #include <pjsip/sip_endpoint.h> 24 #include <pjsip/sip_dialog.h> 25 #include <pj/assert.h> 26 #include <pj/guid.h> 27 #include <pj/log.h> 28 #include <pj/os.h> 21 29 #include <pj/pool.h> 22 30 #include <pj/string.h> 23 #include <pj/guid.h> 24 #include <pj/os.h> 25 #include <stdio.h> 26 27 /* Forward declarations. */ 28 static void on_query_subscribe(pjsip_rx_data *rdata, int *status); 29 static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, 30 pjsip_event_sub_cb **cb, int *expires); 31 static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); 32 static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); 33 static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); 34 35 /* Some string constants. */ 36 static pj_str_t PRESENCE_EVENT = { "presence", 8 }; 37 38 /* Accept types. */ 39 static pj_str_t accept_names[] = { 40 { "application/pidf+xml", 20 }, 41 { "application/xpidf+xml", 21 } 31 32 33 #define THIS_FILE "presence.c" 34 #define PRES_DEFAULT_EXPIRES 600 35 36 /* 37 * Presence module (mod-presence) 38 */ 39 static struct pjsip_module mod_presence = 40 { 41 NULL, NULL, /* prev, next. */ 42 { "mod-presence", 12 }, /* Name. */ 43 -1, /* Id */ 44 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ 45 NULL, /* User data. */ 46 NULL, /* load() */ 47 NULL, /* start() */ 48 NULL, /* stop() */ 49 NULL, /* unload() */ 50 NULL, /* on_rx_request() */ 51 NULL, /* on_rx_response() */ 52 NULL, /* on_tx_request. */ 53 NULL, /* on_tx_response() */ 54 NULL, /* on_tsx_state() */ 42 55 }; 43 static pjsip_media_type accept_types[] = { 44 { 45 { "application", 11 }, 46 { "pidf+xml", 8 } 47 }, 48 { 49 { "application", 11 }, 50 { "xpidf+xml", 9 } 51 } 56 57 58 /* 59 * Presence message body type. 60 */ 61 typedef enum content_type 62 { 63 CONTENT_TYPE_NONE, 64 CONTENT_TYPE_PIDF, 65 CONTENT_TYPE_XPIDF, 66 } content_type; 67 68 /* 69 * This structure describe a presentity, for both subscriber and notifier. 70 */ 71 struct pjsip_pres 72 { 73 pjsip_evsub *sub; /**< Event subscribtion record. */ 74 pjsip_dialog *dlg; /**< The dialog. */ 75 content_type content_type; /**< Content-Type. */ 76 pjsip_pres_status status; /**< Presence status. */ 77 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/ 78 pjsip_evsub_user user_cb; /**< The user callback. */ 52 79 }; 53 80 54 /* Callback that is registered by application. */ 55 static pjsip_presence_cb cb; 56 57 /* Package callback to be register to event_notify */ 58 static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, 59 &on_subscribe }; 60 61 /* Global/static callback to be registered to event_notify */ 62 static 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 */ 72 PJ_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 */ 84 PJ_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; 81 82 typedef struct pjsip_pres pjsip_pres; 83 84 85 /* 86 * Forward decl for evsub callback. 87 */ 88 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); 89 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, 90 pjsip_event *event); 91 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, 92 pjsip_rx_data *rdata, 93 int *p_st_code, 94 pj_str_t **p_st_text, 95 pjsip_hdr *res_hdr, 96 pjsip_msg_body **p_body); 97 static void pres_on_evsub_rx_notify( pjsip_evsub *sub, 98 pjsip_rx_data *rdata, 99 int *p_st_code, 100 pj_str_t **p_st_text, 101 pjsip_hdr *res_hdr, 102 pjsip_msg_body **p_body); 103 static void pres_on_evsub_client_refresh(pjsip_evsub *sub); 104 static void pres_on_evsub_server_timeout(pjsip_evsub *sub); 105 106 107 /* 108 * Event subscription callback for presence. 109 */ 110 static pjsip_evsub_user pres_user = 111 { 112 &pres_on_evsub_state, 113 &pres_on_evsub_tsx_state, 114 &pres_on_evsub_rx_refresh, 115 &pres_on_evsub_rx_notify, 116 &pres_on_evsub_client_refresh, 117 &pres_on_evsub_server_timeout, 118 }; 119 120 121 /* 122 * Some static constants. 123 */ 124 const pj_str_t STR_EVENT = { "Event", 5 }; 125 const pj_str_t STR_PRESENCE = { "presence", 8 }; 126 const pj_str_t STR_APPLICATION = { "application", 11 }; 127 const pj_str_t STR_PIDF_XML = { "pidf+xml", 8}; 128 const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9}; 129 const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 }; 130 const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 }; 131 132 133 /* 134 * Init presence module. 135 */ 136 PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt, 137 pjsip_module *mod_evsub) 138 { 139 pj_status_t status; 140 pj_str_t accept[2]; 141 142 /* Check arguments. */ 143 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL); 144 145 /* Must have not been registered */ 146 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP); 147 148 /* Register to endpoint */ 149 status = pjsip_endpt_register_module(endpt, &mod_presence); 150 if (status != PJ_SUCCESS) 151 return status; 152 153 accept[0] = STR_APP_PIDF_XML; 154 accept[1] = STR_APP_XPIDF_XML; 155 156 /* Register event package to event module. */ 157 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE, 158 PRES_DEFAULT_EXPIRES, 159 PJ_ARRAY_SIZE(accept), accept); 160 if (status != PJ_SUCCESS) { 161 pjsip_endpt_unregister_module(endpt, &mod_presence); 162 return status; 163 } 164 165 return PJ_SUCCESS; 166 } 167 168 169 /* 170 * Get presence module instance. 171 */ 172 PJ_DEF(pjsip_module*) pjsip_pres_instance(void) 173 { 174 return &mod_presence; 175 } 176 177 178 /* 179 * Create client subscription. 180 */ 181 PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, 182 const pjsip_evsub_user *user_cb, 183 pjsip_evsub **p_evsub ) 184 { 185 pj_status_t status; 186 pjsip_pres *pres; 187 pjsip_evsub *sub; 188 189 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL); 190 191 pjsip_dlg_inc_lock(dlg); 95 192 96 193 /* 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 */ 117 PJ_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 */ 125 PJ_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 */ 135 PJ_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 */ 144 PJ_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 */ 152 static 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 */ 160 static 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 */ 219 PJ_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 */ 269 PJ_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 */ 278 static 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 */ 289 static 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; 194 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, &sub); 195 if (status != PJ_SUCCESS) 196 goto on_return; 197 198 /* Create presence */ 199 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres)); 200 pres->dlg = dlg; 201 if (user_cb) 202 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); 203 204 /* Attach to evsub */ 205 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); 206 207 *p_evsub = sub; 208 209 on_return: 210 pjsip_dlg_dec_lock(dlg); 211 return status; 212 } 213 214 215 /* 216 * Create server subscription. 217 */ 218 PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, 219 const pjsip_evsub_user *user_cb, 220 pjsip_rx_data *rdata, 221 pjsip_evsub **p_evsub ) 222 { 293 223 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); 224 pjsip_event_hdr *event; 225 pjsip_expires_hdr *expires_hdr; 226 unsigned expires; 227 content_type content_type = CONTENT_TYPE_NONE; 228 pjsip_evsub *sub; 229 pjsip_pres *pres; 230 pj_status_t status; 231 232 /* Check arguments */ 233 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); 234 235 /* Must be request message */ 236 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, 237 PJSIP_ENOTREQUESTMSG); 238 239 /* Check that request is SUBSCRIBE */ 240 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, 241 &pjsip_subscribe_method)==0, 242 PJSIP_SIMPLE_ENOTSUBSCRIBE); 243 244 /* Check that Event header contains "presence" */ 245 event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); 246 if (!event) { 247 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); 248 } 249 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { 250 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); 251 } 252 253 /* Check that request contains compatible Accept header. */ 254 accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); 302 255 if (accept) { 303 256 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 */ 325 static 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 */ 336 static 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 */ 347 static 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 { 257 for (i=0; i<accept->count; ++i) { 258 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { 259 content_type = CONTENT_TYPE_PIDF; 260 break; 261 } else 262 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { 263 content_type = CONTENT_TYPE_XPIDF; 364 264 break; 365 265 } 366 266 } 367 267 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 268 if (i==accept->count) { 269 /* Nothing is acceptable */ 270 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); 271 } 272 273 } else { 274 /* No Accept header. 275 * Treat as "application/pidf+xml" 276 */ 277 content_type = CONTENT_TYPE_PIDF; 278 } 279 280 /* Check that expires is not too short. */ 281 expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); 282 if (expires_hdr) { 283 if (expires_hdr->ivalue < 5) { 284 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF); 285 } 286 287 expires = expires_hdr->ivalue; 288 if (expires > PRES_DEFAULT_EXPIRES) 289 expires = PRES_DEFAULT_EXPIRES; 290 291 } else { 292 expires = PRES_DEFAULT_EXPIRES; 293 } 294 295 /* Lock dialog */ 296 pjsip_dlg_inc_lock(dlg); 297 298 299 /* Create server subscription */ 300 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, &sub); 301 if (status != PJ_SUCCESS) 302 goto on_return; 303 304 /* Create server presence subscription */ 305 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres)); 306 pres->dlg = dlg; 307 pres->sub = sub; 308 pres->content_type = content_type; 309 if (user_cb) 310 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); 311 312 /* Attach to evsub */ 313 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); 314 315 /* Done: */ 316 *p_evsub = sub; 317 318 on_return: 319 pjsip_dlg_dec_lock(dlg); 320 return status; 321 } 322 323 324 /* 325 * Create SUBSCRIBE 326 */ 327 PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub, 328 pj_int32_t expires, 329 pjsip_tx_data **p_tdata) 330 { 331 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires, 332 p_tdata); 333 } 334 335 336 /* 337 * Accept incoming subscription. 338 */ 339 PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub, 340 pjsip_rx_data *rdata, 341 int st_code, 342 const pjsip_hdr *hdr_list ) 343 { 344 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list ); 345 } 346 347 348 /* 349 * Get presence status. 350 */ 351 PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub, 352 pjsip_pres_status *status ) 353 { 354 pjsip_pres *pres; 355 356 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); 357 358 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 359 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); 360 361 if (pres->tmp_status._is_valid) 362 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status)); 363 else 364 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status)); 365 366 return PJ_SUCCESS; 367 } 368 369 370 /* 371 * Set presence status. 372 */ 373 PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, 374 const pjsip_pres_status *status ) 375 { 376 unsigned i; 377 pjsip_pres *pres; 378 379 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); 380 381 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 382 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); 383 384 for (i=0; i<status->info_cnt; ++i) { 385 pres->status.info[i].basic_open = status->info[i].basic_open; 386 if (status->info[i].id.slen == 0) { 387 pj_create_unique_string(pres->dlg->pool, 388 &pres->status.info[i].id); 392 389 } else { 393 return; 390 pj_strdup(pres->dlg->pool, 391 &pres->status.info[i].id, 392 &status->info[i].id); 394 393 } 395 396 (*cb.on_received_update)(pres, is_open); 397 } 398 } 399 394 pj_strdup(pres->dlg->pool, 395 &pres->status.info[i].contact, 396 &status->info[i].contact); 397 } 398 399 pres->status.info_cnt = status->info_cnt; 400 401 return PJ_SUCCESS; 402 } 403 404 405 /* 406 * Create PIDF document based on the presence info. 407 */ 408 static pjpidf_pres* pres_create_pidf( pj_pool_t *pool, 409 pjsip_pres *pres ) 410 { 411 pjpidf_pres *pidf; 412 unsigned i; 413 pj_str_t entity; 414 415 /* Get publisher URI */ 416 entity.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); 417 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 418 pres->dlg->local.info->uri, 419 entity.ptr, PJSIP_MAX_URL_SIZE); 420 if (entity.slen < 1) 421 return NULL; 422 423 /* Create <presence>. */ 424 pidf = pjpidf_create(pool, &entity); 425 426 /* Create <tuple> */ 427 for (i=0; i<pres->status.info_cnt; ++i) { 428 429 pjpidf_tuple *pidf_tuple; 430 pjpidf_status *pidf_status; 431 432 /* Add tuple id. */ 433 pidf_tuple = pjpidf_pres_add_tuple(pool, pidf, 434 &pres->status.info[i].id); 435 436 /* Set <contact> */ 437 if (pres->status.info[i].contact.slen) 438 pjpidf_tuple_set_contact(pool, pidf_tuple, 439 &pres->status.info[i].contact); 440 441 442 /* Set basic status */ 443 pidf_status = pjpidf_tuple_get_status(pidf_tuple); 444 pjpidf_status_set_basic_open(pidf_status, 445 pres->status.info[i].basic_open); 446 } 447 448 return pidf; 449 } 450 451 452 /* 453 * Create XPIDF document based on the presence info. 454 */ 455 static pjxpidf_pres* pres_create_xpidf( pj_pool_t *pool, 456 pjsip_pres *pres ) 457 { 458 /* Note: PJSIP implementation of XPIDF is not complete! 459 */ 460 pjxpidf_pres *xpidf; 461 pj_str_t publisher_uri; 462 463 PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported " 464 "by PJSIP")); 465 466 publisher_uri.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); 467 publisher_uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 468 pres->dlg->local.info->uri, 469 publisher_uri.ptr, 470 PJSIP_MAX_URL_SIZE); 471 if (publisher_uri.slen < 1) 472 return NULL; 473 474 /* Create XPIDF document. */ 475 xpidf = pjxpidf_create(pool, &publisher_uri); 476 477 /* Set basic status. */ 478 if (pres->status.info_cnt > 0) 479 pjxpidf_set_status( xpidf, pres->status.info[0].basic_open); 480 else 481 pjxpidf_set_status( xpidf, PJ_FALSE); 482 483 return xpidf; 484 } 485 486 487 /* 488 * Function to print XML message body. 489 */ 490 static int pres_print_body(struct pjsip_msg_body *msg_body, 491 char *buf, pj_size_t size) 492 { 493 return pj_xml_print(msg_body->data, buf, size, PJ_TRUE); 494 } 495 496 497 /* 498 * Function to clone XML document. 499 */ 500 static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len) 501 { 502 PJ_UNUSED_ARG(len); 503 return pj_xml_clone( pool, data); 504 } 505 506 507 /* 508 * Create message body. 509 */ 510 static pj_status_t pres_create_msg_body( pjsip_pres *pres, 511 pjsip_tx_data *tdata) 512 { 513 pjsip_msg_body *body; 514 515 body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body)); 516 517 if (pres->content_type == CONTENT_TYPE_PIDF) { 518 519 body->data = pres_create_pidf(tdata->pool, pres); 520 body->content_type.type = pj_str("application"); 521 body->content_type.subtype = pj_str("pidf+xml"); 522 523 } else if (pres->content_type == CONTENT_TYPE_XPIDF) { 524 525 body->data = pres_create_xpidf(tdata->pool, pres); 526 body->content_type.type = pj_str("application"); 527 body->content_type.subtype = pj_str("xpidf+xml"); 528 529 } else { 530 return PJSIP_SIMPLE_EBADCONTENT; 531 } 532 533 534 body->print_body = &pres_print_body; 535 body->clone_data = &xml_clone_data; 536 537 tdata->msg->body = body; 538 539 return PJ_SUCCESS; 540 } 541 542 543 /* 544 * Create NOTIFY 545 */ 546 PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub, 547 pjsip_evsub_state state, 548 const pj_str_t *state_str, 549 const pj_str_t *reason, 550 pjsip_tx_data **p_tdata) 551 { 552 pjsip_pres *pres; 553 pjsip_tx_data *tdata; 554 pj_status_t status; 555 556 /* Check arguments. */ 557 PJ_ASSERT_RETURN(sub, PJ_EINVAL); 558 559 /* Get the presence object. */ 560 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 561 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); 562 563 /* Must have at least one presence info. */ 564 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO); 565 566 567 /* Lock object. */ 568 pjsip_dlg_inc_lock(pres->dlg); 569 570 /* Create the NOTIFY request. */ 571 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata); 572 if (status != PJ_SUCCESS) 573 goto on_return; 574 575 576 /* Create message body to reflect the presence status. */ 577 status = pres_create_msg_body( pres, tdata ); 578 if (status != PJ_SUCCESS) 579 goto on_return; 580 581 582 /* Done. */ 583 *p_tdata = tdata; 584 585 586 on_return: 587 pjsip_dlg_dec_lock(pres->dlg); 588 return status; 589 } 590 591 592 /* 593 * Create NOTIFY that reflect current state. 594 */ 595 PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub, 596 pjsip_tx_data **p_tdata ) 597 { 598 pjsip_pres *pres; 599 pjsip_tx_data *tdata; 600 pj_status_t status; 601 602 /* Check arguments. */ 603 PJ_ASSERT_RETURN(sub, PJ_EINVAL); 604 605 /* Get the presence object. */ 606 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 607 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); 608 609 /* Must have at least one presence info. */ 610 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO); 611 612 613 /* Lock object. */ 614 pjsip_dlg_inc_lock(pres->dlg); 615 616 /* Create the NOTIFY request. */ 617 status = pjsip_evsub_current_notify( sub, &tdata); 618 if (status != PJ_SUCCESS) 619 goto on_return; 620 621 622 /* Create message body to reflect the presence status. */ 623 status = pres_create_msg_body( pres, tdata ); 624 if (status != PJ_SUCCESS) 625 goto on_return; 626 627 628 /* Done. */ 629 *p_tdata = tdata; 630 631 632 on_return: 633 pjsip_dlg_dec_lock(pres->dlg); 634 return status; 635 } 636 637 638 /* 639 * Send request. 640 */ 641 PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub, 642 pjsip_tx_data *tdata ) 643 { 644 return pjsip_evsub_send_request(sub, tdata); 645 } 646 647 648 /* 649 * This callback is called by event subscription when subscription 650 * state has changed. 651 */ 652 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) 653 { 654 pjsip_pres *pres; 655 656 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 657 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 658 659 if (pres->user_cb.on_evsub_state) 660 (*pres->user_cb.on_evsub_state)(sub, event); 661 } 662 663 /* 664 * Called when transaction state has changed. 665 */ 666 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, 667 pjsip_event *event) 668 { 669 pjsip_pres *pres; 670 671 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 672 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 673 674 if (pres->user_cb.on_tsx_state) 675 (*pres->user_cb.on_tsx_state)(sub, tsx, event); 676 } 677 678 679 /* 680 * Called when SUBSCRIBE is received. 681 */ 682 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, 683 pjsip_rx_data *rdata, 684 int *p_st_code, 685 pj_str_t **p_st_text, 686 pjsip_hdr *res_hdr, 687 pjsip_msg_body **p_body) 688 { 689 pjsip_pres *pres; 690 691 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 692 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 693 694 if (pres->user_cb.on_rx_refresh) { 695 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text, 696 res_hdr, p_body); 697 698 } else { 699 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */ 700 pjsip_tx_data *tdata; 701 pj_str_t timeout = { "timeout", 7}; 702 pj_status_t status; 703 704 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) { 705 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, 706 NULL, &timeout, &tdata); 707 } else { 708 status = pjsip_pres_current_notify(sub, &tdata); 709 } 710 711 if (status == PJ_SUCCESS) 712 pjsip_pres_send_request(sub, tdata); 713 } 714 } 715 716 /* 717 * Parse PIDF to info. 718 */ 719 static pj_status_t pres_parse_pidf( pjsip_pres *pres, 720 pjsip_rx_data *rdata, 721 pjsip_pres_status *pres_status) 722 { 723 pjpidf_pres *pidf; 724 pjpidf_tuple *pidf_tuple; 725 726 pidf = pjpidf_parse(rdata->tp_info.pool, 727 rdata->msg_info.msg->body->data, 728 rdata->msg_info.msg->body->len); 729 if (pidf == NULL) 730 return PJSIP_SIMPLE_EBADPIDF; 731 732 pres_status->info_cnt = 0; 733 734 pidf_tuple = pjpidf_pres_get_first_tuple(pidf); 735 while (pidf_tuple) { 736 pjpidf_status *pidf_status; 737 738 pj_strdup(pres->dlg->pool, 739 &pres_status->info[pres_status->info_cnt].id, 740 pjpidf_tuple_get_id(pidf_tuple)); 741 742 pj_strdup(pres->dlg->pool, 743 &pres_status->info[pres_status->info_cnt].contact, 744 pjpidf_tuple_get_contact(pidf_tuple)); 745 746 pidf_status = pjpidf_tuple_get_status(pidf_tuple); 747 if (pidf_status) { 748 pres_status->info[pres_status->info_cnt].basic_open = 749 pjpidf_status_is_basic_open(pidf_status); 750 } else { 751 pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE; 752 } 753 754 pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple ); 755 pres_status->info_cnt++; 756 } 757 758 return PJ_SUCCESS; 759 } 760 761 /* 762 * Parse XPIDF info. 763 */ 764 static pj_status_t pres_parse_xpidf( pjsip_pres *pres, 765 pjsip_rx_data *rdata, 766 pjsip_pres_status *pres_status) 767 { 768 pjxpidf_pres *xpidf; 769 770 xpidf = pjxpidf_parse(rdata->tp_info.pool, 771 rdata->msg_info.msg->body->data, 772 rdata->msg_info.msg->body->len); 773 if (xpidf == NULL) 774 return PJSIP_SIMPLE_EBADXPIDF; 775 776 pres_status->info_cnt = 1; 777 778 pj_strdup(pres->dlg->pool, 779 &pres_status->info[0].contact, 780 pjxpidf_get_uri(xpidf)); 781 pres_status->info[0].basic_open = pjxpidf_get_status(xpidf); 782 pres_status->info[0].id.slen = 0; 783 784 return PJ_SUCCESS; 785 } 786 787 788 /* 789 * Called when NOTIFY is received. 790 */ 791 static void pres_on_evsub_rx_notify( pjsip_evsub *sub, 792 pjsip_rx_data *rdata, 793 int *p_st_code, 794 pj_str_t **p_st_text, 795 pjsip_hdr *res_hdr, 796 pjsip_msg_body **p_body) 797 { 798 pjsip_ctype_hdr *ctype_hdr; 799 pjsip_pres *pres; 800 pj_status_t status; 801 802 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 803 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 804 805 /* Check Content-Type and msg body are present. */ 806 ctype_hdr = rdata->msg_info.ctype; 807 808 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) { 809 810 pjsip_warning_hdr *warn_hdr; 811 pj_str_t warn_text; 812 813 *p_st_code = PJSIP_SC_BAD_REQUEST; 814 815 warn_text = pj_str("Message body is not present"); 816 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399, 817 pjsip_endpt_name(pres->dlg->endpt), 818 &warn_text); 819 pj_list_push_back(res_hdr, warn_hdr); 820 821 return; 822 } 823 824 /* Parse content. */ 825 826 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && 827 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0) 828 { 829 status = pres_parse_pidf( pres, rdata, &pres->tmp_status); 830 } 831 else 832 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && 833 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0) 834 { 835 status = pres_parse_pidf( pres, rdata, &pres->tmp_status); 836 } 837 else 838 { 839 status = PJSIP_SIMPLE_EBADCONTENT; 840 } 841 842 if (status != PJ_SUCCESS) { 843 /* Unsupported or bad Content-Type */ 844 pjsip_accept_hdr *accept_hdr; 845 pjsip_warning_hdr *warn_hdr; 846 847 *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE; 848 849 /* Add Accept header */ 850 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool); 851 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML; 852 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML; 853 pj_list_push_back(res_hdr, accept_hdr); 854 855 /* Add Warning header */ 856 warn_hdr = pjsip_warning_hdr_create_from_status( 857 rdata->tp_info.pool, 858 pjsip_endpt_name(pres->dlg->endpt), 859 status); 860 pj_list_push_back(res_hdr, warn_hdr); 861 862 return; 863 } 864 865 /* If application calls pres_get_status(), redirect the call to 866 * retrieve the temporary status. 867 */ 868 pres->tmp_status._is_valid = PJ_TRUE; 869 870 /* Notify application. */ 871 if (pres->user_cb.on_rx_notify) { 872 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, 873 res_hdr, p_body); 874 } 875 876 877 /* If application responded NOTIFY with 2xx, copy temporary status 878 * to main status, and mark the temporary status as invalid. 879 */ 880 if ((*p_st_code)/100 == 2) { 881 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status)); 882 } 883 884 pres->tmp_status._is_valid = PJ_FALSE; 885 886 /* Done */ 887 } 888 889 /* 890 * Called when it's time to send SUBSCRIBE. 891 */ 892 static void pres_on_evsub_client_refresh(pjsip_evsub *sub) 893 { 894 pjsip_pres *pres; 895 896 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 897 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 898 899 if (pres->user_cb.on_client_refresh) { 900 (*pres->user_cb.on_client_refresh)(sub); 901 } else { 902 pj_status_t status; 903 pjsip_tx_data *tdata; 904 905 status = pjsip_pres_initiate(sub, -1, &tdata); 906 if (status == PJ_SUCCESS) 907 pjsip_pres_send_request(sub, tdata); 908 } 909 } 910 911 /* 912 * Called when no refresh is received after the interval. 913 */ 914 static void pres_on_evsub_server_timeout(pjsip_evsub *sub) 915 { 916 pjsip_pres *pres; 917 918 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 919 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 920 921 if (pres->user_cb.on_server_timeout) { 922 (*pres->user_cb.on_server_timeout)(sub); 923 } else { 924 pj_status_t status; 925 pjsip_tx_data *tdata; 926 pj_str_t reason = { "timeout", 7 }; 927 928 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, 929 NULL, &reason, &tdata); 930 if (status == PJ_SUCCESS) 931 pjsip_pres_send_request(sub, tdata); 932 } 933 } 934
Note: See TracChangeset
for help on using the changeset viewer.