#include "rls_mod.h" #include "rls_handler.h" #include "rl_subscription.h" #include <cds/dstring.h> #include <cds/logger.h> #include <cds/sip_utils.h> #include "result_codes.h" #include "../../str.h" #include "../../id.h" #include "../../dprint.h" #include "../../mem/mem.h" #include "../../parser/parse_uri.h" #include "../../parser/parse_from.h" #include "../../parser/parse_to.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_event.h" #include "../../parser/parse_expires.h" #include "../../parser/parse_content.h" #include "../../data_lump_rpl.h" #include "../../usr_avp.h" #include <xcap/resource_list.h> /* static variables for sharing data loaded from XCAP */ typedef struct { xcap_query_params_t xcap_params; flat_list_t *flat_list; /* may be NULL for empty lists!!! */ int have_flat_list; /* added due to possibility of NULL flat list */ } rls_xcap_query_t; static rls_xcap_query_t query = { xcap_params: { xcap_root: {s: NULL, len: 0} }, flat_list: NULL, have_flat_list: 0 }; /* clears last data stored from one of query_... functions */ static void clear_last_query() { if (query.have_flat_list) { if (query.flat_list) free_flat_list(query.flat_list); query.flat_list = NULL; query.have_flat_list = 0; /* str_clear(&query.params.xcap_root); */ memset(&query.xcap_params, 0, sizeof(query.xcap_params)); } } static int send_reply(struct sip_msg* _m, int code, char *msg) { if (tmb.t_reply(_m, code, msg) == -1) { LOG(L_ERR, "send_reply(): Error while sending %d %s\n", code, msg); return -1; } else return 0; } static int parse_rls_headers(struct sip_msg* _m) { struct hdr_field *acc; if ( (parse_headers(_m, HDR_EOH_T, 0) == -1) || /* we need all Accept headers... */ (_m->from==0)||(_m->to==0)||(_m->event==0) ) { LOG(L_ERR, "parse_rls_headers(): Error while parsing headers\n"); return -1; } /* there is no parse_to_header function (only parse_to) if (parse_to_header(_m) < 0) { LOG(L_ERR, "parse_rls_headers(): To malformed or missing\n"); return -1; }*/ if (parse_from_header(_m) < 0) { LOG(L_ERR, "parse_rls_headers(): From malformed or missing\n"); return -1; } if (_m->expires) { if (parse_expires(_m->expires) < 0) { LOG(L_ERR, "parse_rls_headers(): Error parsing Expires header\n"); return -1; } } if (_m->event) { if (parse_event(_m->event) < 0) { LOG(L_ERR, "parse_rls_headers(): Error while parsing Event header field\n"); return -1; } } acc = _m->accept; while (acc) { /* parse all accept headers */ if (acc->type == HDR_ACCEPT_T) { /* DEBUG_LOG("parsing accept header: %.*s\n", FMT_STR(acc->body)); */ if (parse_accept_body(acc) < 0) { LOG(L_ERR, "parse_rls_headers(): Error while parsing Accept header field\n"); return -1; } } acc = acc->next; } return 0; } static int get_event(struct sip_msg *_m) { int et = 0; event_t *event = NULL; if (_m->event) { event = (event_t*)(_m->event->parsed); et = event->parsed; } else { LOG(L_ERR, "no event package for RLS - using EVENT_PRESENCE\n"); et = EVENT_PRESENCE; } return et; } /* returns 1 if package supported by RLS */ static int verify_event_package(struct sip_msg *m) { int et = get_event(m); switch (et) { case EVENT_PRESENCE: return 0; default: return -1; } return -1; } static int add_response_header(struct sip_msg *_m, char *hdr) { if (!add_lump_rpl(_m, hdr, strlen(hdr), LUMP_RPL_HDR)) return -1; return 0; } static int add_response_min_expires_header(struct sip_msg *_m) { char tmp[64]; sprintf(tmp, "Min-Expires: %d\r\n", rls_min_expiration); if (!add_lump_rpl(_m, tmp, strlen(tmp), LUMP_RPL_HDR)) return -1; return 0; } struct accepted_types { char *mime_txt; int mime; int needed; int found; }; /* marks mime_type as found */ void mark_accepted_type(struct accepted_types *types, int mime_type) { int i; for (i = 0; types[i].mime_txt; i++) if (mime_type == types[i].mime) types[i].found = 1; } static int check_message(struct sip_msg *_m, int send_err) { int *accepts_mimes = NULL; int i; struct hdr_field *acc; struct accepted_types accepts[] = { { "multipart/related", MIMETYPE(MULTIPART,RELATED), 1, 0 }, { "application/rlmi+xml", MIMETYPE(APPLICATION,RLMIXML), 1, 0 }, { "application/pidf+xml", MIMETYPE(APPLICATION, PIDFXML), 1, 0 }, { NULL, 0, 0, 0 } }; if (verify_event_package(_m) != 0) { /* allow only selected packages independently on rls document */ if (send_err) { ERR("unsupported events\n"); add_response_header(_m, "Allow-Events: presence\r\n"); send_reply(_m, 489, "Bad Event"); } return -1; } /* verify Accept: multipart/related, application/rlmi+xml, application/pidf+xml */ acc = _m->accept; while (acc) { /* go through all Accept headers */ if (acc->type == HDR_ACCEPT_T) { /* it MUST be parsed from parse_hdr !!! */ accepts_mimes = acc->parsed; /* go through all in accept mimes and test our */ for (i = 0; accepts_mimes[i]; i++) mark_accepted_type(accepts, accepts_mimes[i]); } acc = acc->next; } for (i = 0; accepts[i].mime_txt; i++) if ((!accepts[i].found) && (accepts[i].needed)) { if (send_err) { ERR("required type %s not in Accept headers\n", accepts[i].mime_txt); send_reply(_m, 400, "Bad Request"); } return -1; } /* verify Supported: eventlist */ return 0; } static int handle_new_subscription(struct sip_msg *m, rls_xcap_query_t *query, int send_error_responses) { rl_subscription_t *s; int res = 0; xcap_query_params_t *params = NULL; if (query) params = &query->xcap_params; rls_lock(); DBG("handle_new_subscription(rls)\n"); /* create a new subscription structure */ res = rls_create_subscription(m, &s, query->flat_list, params); if (res != RES_OK) { rls_unlock(); switch (res) { case RES_PARSE_HEADERS_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: Bad or missing headers\r\n"); send_reply(m, 400, "Bad Request"); break; case RES_SUBSCRIPTION_REJECTED: /* if (!send_error_responses) return -1; */ /* FIXME: authorization is done before XCAP query, so though it is NOT * resource-list subscription it may be marked as rejected !!! */ DEBUG_LOG("subscription rejected\n"); add_response_header(m, "Reason-Phrase: Subscription rejected\r\n"); send_reply(m, 403, "Forbidden"); break; case RES_EXPIRATION_INTERVAL_TOO_SHORT: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_min_expires_header(m); send_reply(m, 423, "Interval too small"); break; case RES_BAD_EVENT_PACKAGE_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ /* TODO: add_response_header(_m, "Allow-Events: \r\n"); */ send_reply(m, 489, "Bad Event"); break; case RES_BAD_GATEWAY_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ send_reply(m, 502, "Bad Gateway"); break; case RES_XCAP_QUERY_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: XCAP query error\r\n"); send_reply(m, 502, "Bad Gateway"); /*send_reply(m, 500, "Internal error"); */ break; case RES_XCAP_PARSE_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: XCAP result parsing error\r\n"); send_reply(m, 500, "Internal error"); break; default: if (!send_error_responses) return -1; /* "unprocessed" */ send_reply(m, 500, "Internal error"); } return 0; /* processed */ } /* send a response */ rls_prepare_subscription_response(s, m); send_reply(m, 200, "OK"); DEBUG_LOG("RLS subscription successfully handled\n"); /* create NOTIFY message * FIXME - this may be a nonsense for polling, because the notifier might not * catch up sent notification */ rls_generate_notify(s, 1); /* free subscription if only polling */ if (sm_subscription_terminated(&s->u.external) == 0) { rls_remove(s); } rls_unlock(); return 0; } static int handle_renew_subscription(struct sip_msg *m, int send_error_responses) { str *from_tag; str *to_tag; str *call_id; rl_subscription_t *s = NULL; int res; to_tag = &((struct to_body*)m->to->parsed)->tag_value; from_tag = &((struct to_body*)m->from->parsed)->tag_value; call_id = NULL; if (m->callid) call_id = &m->callid->body; DBG("handle_renew_subscription(rls)\n"); rls_lock(); res = rls_find_subscription(from_tag, to_tag, call_id, &s); if ((res != RES_OK) || (!s)) { rls_unlock(); if (send_error_responses) { WARN("can't refresh unknown subscription\n"); send_reply(m, 481, "Call/Transaction Does Not Exist"); } return -1; /* "unprocessed" */ } res = rls_refresh_subscription(m, s); if (res != RES_OK) { rls_unlock(); switch (res) { case RES_PARSE_HEADERS_ERR: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_header(m, "Reason-Phrase: Bad or missing headers\r\n"); send_reply(m, 400, "Bad Request"); break; case RES_EXPIRATION_INTERVAL_TOO_SHORT: if (!send_error_responses) return -1; /* "unprocessed" */ add_response_min_expires_header(m); send_reply(m, 423, "Interval too small"); break; case RES_SUBSCRIPTION_TERMINATED: send_reply(m, 481, "Subscription terminated"); break; default: if (!send_error_responses) return -1; /* "unprocessed" */ send_reply(m, 500, "Internal error"); } return 0; /* processed */ } /* send a response */ rls_prepare_subscription_response(s, m); send_reply(m, 200, "OK"); /* create NOTIFY message */ rls_generate_notify(s, 1); /* free subscription if only polling */ if (sm_subscription_terminated(&s->u.external) == 0) { rls_remove(s); } rls_unlock(); return 0; } int handle_rls_subscription(struct sip_msg* _m, char *send_bad_resp) { int res; long send_err = 1; PROF_START(rls_handle_subscription) send_err = (long)send_bad_resp; res = parse_rls_headers(_m); if (res == -1) { LOG(L_INFO, "handle_rls_subscription(): problems parsing headers.\n"); if (send_err) { add_response_header(_m, "Reason-Phrase: Bad or missing headers\r\n"); send_reply(_m, 400, "Bad Request"); } clear_last_query(); PROF_STOP(rls_handle_subscription) return -1; } if (check_message(_m, send_err) != 0) { DBG("check message failed\n"); clear_last_query(); PROF_STOP(rls_handle_subscription) return -1; } if (has_to_tag(_m)) { /* handle SUBSCRIBE for an existing subscription */ res = handle_renew_subscription(_m, send_err); } else { /* handle SUBSCRIBE for a new subscription */ res = handle_new_subscription(_m, &query, send_err); } clear_last_query(); PROF_STOP(rls_handle_subscription) if (res == 0) return 1; else return -1; } /*****************************************************************/ /* XCAP query functions accessible from the CFG script */ /* Get resource-list URI from SUBSCRIBE request */ static int get_dst_uri(struct sip_msg* _m, str* dst_uri) { /* FIXME: get raw request URI? or from TO? * FIXME: skip uri parameters and everything else, leave only * sip:xxx@yyy ???!!! */ str uri; if (_m->new_uri.s) { uri.s = _m->new_uri.s; uri.len = _m->new_uri.len; } else { uri.s = _m->first_line.u.request.uri.s; uri.len = _m->first_line.u.request.uri.len; } if (dst_uri) *dst_uri = uri; return RES_OK; } int query_rls_services(struct sip_msg* _m, char *a, char *b) { str uri; static str package = STR_STATIC_INIT("presence"); /* TODO: take package from Event header or allow packages * given as parameter? */ PROF_START(rls_query_rls_sevices) clear_last_query(); if (fill_xcap_params) fill_xcap_params(_m, &query.xcap_params); if (get_dst_uri(_m, &uri) < 0) { ERR("can't get destination URI\n"); clear_last_query(); PROF_STOP(rls_query_rls_sevices) return -1; } if (xcap_query_rls_services(&query.xcap_params, &uri, &package, &query.flat_list) < 0) { ERR("XCAP query problems for uri %.*s\n", FMT_STR(uri)); clear_last_query(); PROF_STOP(rls_query_rls_sevices) return -1; } query.have_flat_list = 1; PROF_STOP(rls_query_rls_sevices) return 1; } int query_resource_list(struct sip_msg* _m, char *list_name, char *b) { int res; str_t uid; PROF_START(rls_query_resource_list) clear_last_query(); if (fill_xcap_params) fill_xcap_params(_m, &query.xcap_params); if (get_from_uid(&uid, _m) < 0) { ERR("can't get From uid\n"); clear_last_query(); PROF_STOP(rls_query_resource_list) return -1; } res = get_resource_list_from_full_doc(&uid, NULL, /* TODO: filename */ &query.xcap_params, list_name, &query.flat_list); /* TODO: add function for real XCAP server */ if (res < 0) { ERR("XCAP query problems\n"); clear_last_query(); PROF_STOP(rls_query_resource_list) return -1; } query.have_flat_list = 1; PROF_STOP(rls_query_resource_list) return 1; } int have_flat_list(struct sip_msg* _m, char *a, char *b) { PROF_START(rls_have_flat_list) if (query.have_flat_list) { PROF_STOP(rls_have_flat_list) return 1; } else { PROF_STOP(rls_have_flat_list) return -1; } }