From f9f26adc11d96367a40947642b20bfdda246875e Mon Sep 17 00:00:00 2001 From: Wolfgang Kampichler Date: Tue, 12 Jan 2021 11:56:47 +0100 Subject: [PATCH] lost: added Geolocation header value list support - the Geolocation header value parsing now supports a list of location URIs of type (cid, http and https). - types may be filtered and the list sequence may be changed via new module parameters. - besides: README update and code refactoring. --- src/modules/lost/doc/lost_admin.xml | 71 +++++- src/modules/lost/functions.c | 343 +++++++++++++++------------- src/modules/lost/lost.c | 14 +- src/modules/lost/pidf.c | 4 +- src/modules/lost/pidf.h | 2 +- src/modules/lost/utilities.c | 304 +++++++++++++++++++++++- src/modules/lost/utilities.h | 55 +++-- 7 files changed, 604 insertions(+), 189 deletions(-) diff --git a/src/modules/lost/doc/lost_admin.xml b/src/modules/lost/doc/lost_admin.xml index c5d4f5ad69a..8eda2b01d10 100644 --- a/src/modules/lost/doc/lost_admin.xml +++ b/src/modules/lost/doc/lost_admin.xml @@ -169,6 +169,62 @@ ... modparam("lost", "location_type, "civic geodetic locationURI") + ... + + + +
+ <varname>geoheader_type</varname> (int) + + A Geolocation header may include a list of locationValues pointing + to either a Presence Information Data Format Location Object + (PIDF-LO) in the SIP body using a content-indirection (cid:) URI + per RFC4483 (), + or an http(s) URI pointing to an external source. This parameter + supports filtering of the following types: + + + + 0 (any) - any URI (first or last) + + + 1 (cid) - cid URI (aka Location-by-Value) + + + 2 (http) - http URI (aka Location-by-Reference) + + + 3 (https) - https URI (aka Location-by-Reference) + + + + Default: 0 (any) + + + Set <varname>geoheader_type</varname> parameter + + ... + modparam("lost", "geoheader_type", 1) + ... + + +
+
+ <varname>geoheader_order</varname> (int) + + A Geolocation header may include a list of locationValues. This + parameter sets the order of the URI used to retrieve location + information, either the first element of a certain type or the + last. Values are 0 (first) or 1 (last). + + + Default: 0 (first) + + + Set <varname>geoheader_order</varname> parameter + + ... + modparam("lost", "geoheader_order", 0) ... @@ -328,11 +384,12 @@ xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $ . - Note: http_client_query exported by the http_client API returns by default the first line of - the HTTP response, but the lost module requires the complete response message, otherwise - dereferencing location via the HTTP URL provided with the Geolocation header causes an error. - Therefore, to work properly, it is required to set the http_client module parameter query_result - to 0. More details at: + Note: http_client_query exported by the http_client API returns by + default the first line of the HTTP response, but the lost module requires + the complete response message, otherwise dereferencing location via the + HTTP URL provided with the Geolocation header causes an error. Therefore, + to work properly, it is required to set the http_client module parameter + query_result to 0. More details at: . @@ -340,5 +397,9 @@ xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $ following link (search for "GET ACCESS"): . + + Note: in case modparam "geoheader_type" is set to 2 (http), the module may + use 3 (https) as fallback and vice versa. +
diff --git a/src/modules/lost/functions.c b/src/modules/lost/functions.c index 481d737071c..7e62a8dd7a3 100644 --- a/src/modules/lost/functions.c +++ b/src/modules/lost/functions.c @@ -54,6 +54,8 @@ extern httpc_api_t httpapi; +extern int lost_geoloc_type; +extern int lost_geoloc_order; extern int held_resp_time; extern int held_exact_type; extern str held_loc_type; @@ -83,7 +85,8 @@ char *lost_held_type(char *type, int *exact, int *lgth) if(strstr(type, HELD_TYPE_ANY)) { len = strlen(ret) + strlen(HELD_TYPE_ANY) + 1; tmp = pkg_realloc(ret, len); - if(tmp == NULL) goto err; + if(tmp == NULL) + goto err; ret = tmp; strcat(ret, HELD_TYPE_ANY); *exact = 0; @@ -91,7 +94,8 @@ char *lost_held_type(char *type, int *exact, int *lgth) if(strstr(type, HELD_TYPE_CIV)) { len = strlen(ret) + strlen(HELD_TYPE_CIV) + 1; tmp = pkg_realloc(ret, len); - if(tmp == NULL) goto err; + if(tmp == NULL) + goto err; ret = tmp; strcat(ret, HELD_TYPE_CIV); } @@ -99,13 +103,15 @@ char *lost_held_type(char *type, int *exact, int *lgth) if(strlen(ret) > 1) { len = strlen(ret) + strlen(HELD_TYPE_SEP) + 1; tmp = pkg_realloc(ret, len); - if(tmp == NULL) goto err; + if(tmp == NULL) + goto err; ret = tmp; strcat(ret, HELD_TYPE_SEP); } len = strlen(ret) + strlen(HELD_TYPE_GEO) + 1; tmp = pkg_realloc(ret, len); - if(tmp == NULL) goto err; + if(tmp == NULL) + goto err; ret = tmp; strcat(ret, HELD_TYPE_GEO); } @@ -113,13 +119,15 @@ char *lost_held_type(char *type, int *exact, int *lgth) if(strlen(ret) > 1) { len = strlen(ret) + strlen(HELD_TYPE_SEP) + 1; tmp = pkg_realloc(ret, len); - if(tmp == NULL) goto err; + if(tmp == NULL) + goto err; ret = tmp; strcat(ret, HELD_TYPE_SEP); } len = strlen(ret) + strlen(HELD_TYPE_URI) + 1; tmp = pkg_realloc(ret, len); - if(tmp == NULL) goto err; + if(tmp == NULL) + goto err; ret = tmp; strcat(ret, HELD_TYPE_URI); } @@ -130,7 +138,7 @@ char *lost_held_type(char *type, int *exact, int *lgth) err: LM_ERR("no more private memory\n"); - if (ret != NULL) { + if(ret != NULL) { pkg_free(ret); } *lgth = 0; @@ -287,8 +295,8 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, for(cur_node = root->children; cur_node; cur_node = cur_node->next) { if(cur_node->type == XML_ELEMENT_NODE) { - if(xmlStrcmp(cur_node->name, - (const xmlChar *)"locationUriSet") == 0) { + if(xmlStrcmp(cur_node->name, (const xmlChar *)"locationUriSet") + == 0) { LM_DBG("*** node '%s' found\n", cur_node->name); @@ -302,8 +310,8 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, geo.s = lost_trim_content(geo.s, &geo.len); } } - if(xmlStrcmp(cur_node->name, - (const xmlChar *)"presence") == 0) { + if(xmlStrcmp(cur_node->name, (const xmlChar *)"presence") + == 0) { LM_DBG("*** node '%s' found\n", cur_node->name); @@ -413,25 +421,29 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, pv_value_t pverr; p_loc_t loc = NULL; + p_geolist_t geolist = NULL; + int geotype; - xmlDocPtr doc = NULL; - xmlNodePtr root = NULL; - + str url = {NULL, 0}; str uri = {NULL, 0}; str urn = {NULL, 0}; str err = {NULL, 0}; - str res = {NULL, 0}; + str req = {NULL, 0}; str con = {NULL, 0}; str ret = {NULL, 0}; - str geo = {NULL, 0}; - str geohdr = {NULL, 0}; str name = {NULL, 0}; str pidf = {NULL, 0}; + str geohdr = {NULL, 0}; str pidfhdr = {NULL, 0}; struct msg_start *fl; char *search = NULL; + char *geoval = NULL; int curlres = 0; + int geoitems = 0; + + xmlDocPtr doc = NULL; + xmlNodePtr root = NULL; if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) { LM_ERR("invalid parameter\n"); @@ -462,7 +474,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, && ((*(search + 1) == 'r') || (*(search + 1) == 'R')) && ((*(search + 2) == 'n') || (*(search + 2) == 'N')) && (*(search + 3) == ':')) { - LM_INFO("### LOST urn [%.*s]\n", urn.len, urn.s); + LM_INFO("### LOST urn\t[%.*s]\n", urn.len, urn.s); } else { LM_ERR("service urn not found\n"); goto err; @@ -474,159 +486,178 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, /* pidf from parameter */ if(_pidf) { if(fixup_get_svalue(_m, (gparam_p)_pidf, &pidf) != 0) { - LM_ERR("cannot get pidf-lo\n"); - goto err; + LM_WARN("cannot get pidf-lo parameter\n"); + } else { + + LM_DBG("parsing pidf-lo from paramenter\n"); + + if(pidf.len > 0) { + + LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s); + + /* parse the pidf-lo */ + loc = lost_parse_pidf(pidf, urn); + /* free memory */ + pidf.s = NULL; + pidf.len = 0; + } else { + LM_WARN("no valid pidf parameter ...\n"); + } } } - /* pidf from geolocation header */ - if(pidf.len == 0) { - LM_WARN("no pidf parameter, trying geolocation header ...\n"); + + /* no pidf-lo so far ... check geolocation header */ + if(loc == NULL) { + + LM_DBG("looking for geolocation header ...\n"); + geohdr.s = lost_get_geolocation_header(_m, &geohdr.len); if(geohdr.len == 0) { LM_ERR("geolocation header not found\n"); goto err; - } else { + } - LM_DBG("geolocation header found\n"); - - /* pidf from multipart body, check cid scheme */ - if(geohdr.len > 6) { - search = geohdr.s; - if((*(search + 0) == '<') - && ((*(search + 1) == 'c') || (*(search + 1) == 'C')) - && ((*(search + 2) == 'i') || (*(search + 2) == 'I')) - && ((*(search + 3) == 'd') || (*(search + 3) == 'D')) - && (*(search + 4) == ':')) { - search += 4; - *search = '<'; - geo.s = search; - geo.len = geo.len - 4; - - LM_DBG("cid: [%.*s]\n", geo.len, geo.s); - - /* get body part - filter=>content id */ - pidf.s = get_body_part_by_filter( - _m, 0, 0, geo.s, NULL, &pidf.len); - if(pidf.len == 0) { - LM_ERR("no multipart body found\n"); - /* free memory */ - geo.s = NULL; - geo.len = 0; - lost_free_string(&geohdr); - goto err; - } - } - /* no pidf-lo so far ... check http(s) scheme */ - if(((*(search + 0) == 'h') || (*(search + 0) == 'H')) - && ((*(search + 1) == 't') || (*(search + 1) == 'T')) - && ((*(search + 2) == 't') || (*(search + 2) == 'T')) - && ((*(search + 3) == 'p') || (*(search + 3) == 'P'))) { - geo.s = geohdr.s; - geo.len = geohdr.len; + LM_DBG("geolocation header found\n"); - if(*(search + 4) == ':') { + /* parse Geolocation header */ + geolist = lost_new_geoheader_list(geohdr, &geoitems); + if(geoitems == 0) { + LM_ERR("invalid geolocation header\n"); + lost_free_string(&geohdr); + goto err; + } - LM_DBG("http url: [%.*s]\n", geo.len, geo.s); + LM_DBG("number of location URIs: %d\n", geoitems); - } else if(((*(search + 4) == 's') || (*(search + 4) == 'S')) - && (*(search + 5) == ':')) { + if(lost_geoloc_order == 0) { - LM_DBG("https url: [%.*s]\n", geo.len, geo.s); + LM_DBG("reversing location URI sequence\n"); - } else { - LM_ERR("invalid url: [%.*s]\n", geo.len, geo.s); - /* free memory */ - geo.s = NULL; - geo.len = 0; - lost_free_string(&geohdr); - goto err; - } + lost_reverse_geoheader_list(&geolist); + } - /* ! dereference pidf.lo at location server - HTTP GET */ - /* ! requires hack in http_client module */ - /* ! functions.c => http_client_query => query_params.oneline = 0; */ - curlres = httpapi.http_client_query( - _m, geo.s, &pidfhdr, NULL, NULL); - /* free memory */ - geo.s = NULL; - geo.len = 0; - lost_free_string(&geohdr); - /* only HTTP 2xx responses are accepted */ - if(curlres >= 300 || curlres < 100) { - LM_ERR("http GET failed with error: %d\n", curlres); - /* free memory */ - lost_free_string(&pidfhdr); - goto err; - } + switch(lost_geoloc_type) { + case ANY: /* type: 0 */ + geoval = lost_get_geoheader_value(geolist, ANY, &geotype); - LM_DBG("http GET returned: %d\n", curlres); + LM_DBG("geolocation header field (any): %s\n", geoval); - if(pidfhdr.len == 0) { - LM_ERR("dereferencing location failed\n"); - goto err; - } - pidf.s = pidfhdr.s; - pidf.len = pidfhdr.len; + break; + case CID: /* type: 1 */ + geoval = lost_get_geoheader_value(geolist, CID, &geotype); + + LM_DBG("geolocation header field (LbV): %s\n", geoval); + + break; + case HTTP: /* type: 2 */ + geoval = lost_get_geoheader_value(geolist, HTTP, &geotype); + /* fallback to https */ + if(geoval == NULL) { + LM_WARN("no valid http URL ... trying https\n"); + geoval = lost_get_geoheader_value(geolist, HTTPS, &geotype); } - } else { - LM_ERR("invalid geolocation header\n"); - goto err; - } - } - } - /* no pidf-lo return error */ - if(pidf.len == 0) { - LM_ERR("pidf-lo not found\n"); - goto err; - } + LM_DBG("geolocation header field (LbR): %s\n", geoval); - LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s); + break; + case HTTPS: /* type: 3 */ + /* prefer https */ + geoval = lost_get_geoheader_value(geolist, HTTPS, &geotype); + /* fallback to http */ + if(geoval == NULL) { + LM_WARN("no valid https URL ... trying http\n"); + geoval = lost_get_geoheader_value(geolist, HTTP, &geotype); + } - /* read and parse pidf-lo */ - doc = xmlReadMemory(pidf.s, pidf.len, 0, NULL, - XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA); + LM_DBG("geolocation header field (LbR): %s\n", geoval); - if(doc == NULL) { - LM_WARN("invalid xml (pidf-lo): [%.*s]\n", pidf.len, pidf.s); - doc = xmlRecoverMemory(pidf.s, pidf.len); - if(doc == NULL) { - LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len, - pidf.s); + break; + default: + LM_WARN("unknown module parameter value\n"); + geoval = lost_get_geoheader_value(geolist, UNKNOWN, &geotype); + + LM_DBG("geolocation header field (any): %s\n", geoval); + + break; + } + + if(geoval == NULL) { + LM_ERR("invalid geolocation header\n"); + /* free memory */ + lost_delete_geoheader_list(geolist); + lost_free_string(&geohdr); goto err; } - LM_DBG("xml (pidf-lo) recovered\n"); - } + LM_INFO("### LOST loc\t[%s]\n", geoval); - root = xmlDocGetRootElement(doc); - if(root == NULL) { - LM_ERR("empty pidf-lo document\n"); - goto err; - } - if((!xmlStrcmp(root->name, (const xmlChar *)"presence")) - || (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) { - /* get the geolocation: point or circle, urn, ... */ - loc = lost_new_loc(urn); - if(loc == NULL) { - LM_ERR("location object allocation failed\n"); - goto err; + /* use location by value */ + if(geotype == CID) { + + /* get body part - filter=>content-indirection */ + pidf.s = get_body_part_by_filter(_m, 0, 0, geoval, NULL, &pidf.len); + if(pidf.len > 0) { + + LM_DBG("LbV pidf-lo: [%.*s]\n", pidf.len, pidf.s); + + /* parse the pidf-lo */ + loc = lost_parse_pidf(pidf, urn); + /* free memory */ + pidf.s = NULL; + pidf.len = 0; + } else { + LM_WARN("no multipart body found\n"); + } } - if(lost_parse_location_info(root, loc) < 0) { - LM_ERR("location element not found\n"); - goto err; + + /* use location by reference */ + if((geotype == HTTPS) || (geotype == HTTP)) { + url.s = geoval; + url.len = strlen(geoval); + /* ! dereference pidf.lo at location server - HTTP GET */ + /* ! requires hack in http_client module */ + /* ! functions.c => http_client_query => query_params.oneline = 0; */ + curlres = + httpapi.http_client_query(_m, url.s, &pidfhdr, NULL, NULL); + /* free memory */ + url.s = NULL; + url.len = 0; + /* only HTTP 2xx responses are accepted */ + if(curlres >= 300 || curlres < 100) { + LM_ERR("http GET failed with error: %d\n", curlres); + /* free memory */ + lost_delete_geoheader_list(geolist); + lost_free_string(&pidfhdr); + lost_free_string(&geohdr); + goto err; + } + + pidf.s = pidfhdr.s; + pidf.len = pidfhdr.len; + + if(pidf.len > 0) { + + LM_DBG("LbR pidf-lo: [%.*s]\n", pidf.len, pidf.s); + + /* parse the pidf-lo */ + loc = lost_parse_pidf(pidf, urn); + /* free memory */ + pidf.s = NULL; + pidf.len = 0; + } else { + LM_WARN("dereferencing location failed\n"); + } } - } else { - LM_ERR("findServiceResponse or presence element not found in " - "[%.*s]\n", - pidf.len, pidf.s); - goto err; + /* free memory */ + lost_delete_geoheader_list(geolist); + lost_free_string(&geohdr); + lost_free_string(&pidfhdr); } - /* free memory */ - pidf.s = NULL; - pidf.len = 0; - lost_free_string(&pidfhdr); + if(loc == NULL) { + LM_ERR("location object not found\n"); + goto err; + } /* check if connection exits */ if(httpapi.http_connection_exists(&con) == 0) { @@ -634,22 +665,20 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, goto err; } /* assemble findService request */ - res.s = lost_find_service_request(loc, &res.len); + req.s = lost_find_service_request(loc, &req.len); /* free memory */ lost_free_loc(loc); loc = NULL; - xmlFreeDoc(doc); - doc = NULL; - if(res.len == 0) { + if(req.len == 0) { LM_ERR("lost request failed\n"); goto err; } - LM_DBG("findService request: [%.*s]\n", res.len, res.s); + LM_DBG("findService request: [%.*s]\n", req.len, req.s); /* send findService request to mapping server - HTTP POST */ - curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &res); + curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req); /* only HTTP 2xx responses are accepted */ if(curlres >= 300 || curlres < 100) { LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres); @@ -660,7 +689,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curlres); /* free memory */ - lost_free_string(&res); + lost_free_string(&req); if(ret.len == 0) { LM_ERR("findService request failed\n"); @@ -700,7 +729,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, lost_free_string(&ret); goto err; } - LM_INFO("### LOST uri [%.*s]\n", uri.len, uri.s); + LM_INFO("### LOST uri\t[%.*s]\n", uri.len, uri.s); /* get the displayName element */ name.s = lost_get_content(root, name_element, &name.len); if(name.len == 0) { @@ -709,7 +738,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, lost_free_string(&ret); goto err; } - LM_INFO("### LOST name [%.*s]\n", name.len, name.s); + LM_INFO("### LOST din\t[%.*s]\n", name.len, name.s); } else if((!xmlStrcmp(root->name, (const xmlChar *)"errors"))) { LM_DBG("findService error response received\n"); @@ -733,8 +762,6 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, /* free memory */ lost_free_string(&ret); - xmlFreeDoc(doc); - doc = NULL; /* set writable pvars */ pvname.rs = name; @@ -764,11 +791,13 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS; err: - if(loc != NULL) { - lost_free_loc(loc); - } + /* free memory */ if(doc != NULL) { xmlFreeDoc(doc); + doc = NULL; + } + if(loc != NULL) { + lost_free_loc(loc); } return LOST_CLIENT_ERROR; diff --git a/src/modules/lost/lost.c b/src/modules/lost/lost.c index 7661b60c6b9..a2dd2498ad6 100644 --- a/src/modules/lost/lost.c +++ b/src/modules/lost/lost.c @@ -49,6 +49,10 @@ MODULE_VERSION /* Module parameter variables */ httpc_api_t httpapi; +/* lost: any (0), cid (1), http (2) or https (3) (default: 0) */ +int lost_geoloc_type = 0; +/* lost: Geolocation header value order: first (0) or last (1) (default: 0) */ +int lost_geoloc_order = 0; /* held request: response time (default: 0 = no timeout) */ int held_resp_time = 0; /* held request: exact is true (1) or false (0) (default: false) */ @@ -96,16 +100,14 @@ static cmd_export_t cmds[] = { {"lost_query", (cmd_function)w_lost_query_all, 6, fixup_lost_query_all, fixup_free_lost_query_all, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE}, - {0, 0, 0, 0, 0, 0} -}; + {0, 0, 0, 0, 0, 0}}; /* Exported parameters */ -static param_export_t params[] = { - {"exact_type", PARAM_INT, &held_exact_type}, +static param_export_t params[] = {{"exact_type", PARAM_INT, &held_exact_type}, {"response_time", PARAM_INT, &held_resp_time}, {"location_type", PARAM_STR, &held_loc_type}, - {0, 0, 0} -}; + {"geoheader_type", PARAM_INT, &lost_geoloc_type}, + {"geoheader_order", PARAM_INT, &lost_geoloc_order}, {0, 0, 0}}; /* Module interface */ struct module_exports exports = { diff --git a/src/modules/lost/pidf.c b/src/modules/lost/pidf.c index 7f9f040ac7c..927d39fbe51 100644 --- a/src/modules/lost/pidf.c +++ b/src/modules/lost/pidf.c @@ -98,8 +98,8 @@ xmlNodePtr xmlNodeGetNodeByName( while(cur) { xmlNodePtr match = NULL; if(xmlStrcasecmp(cur->name, (unsigned char *)name) == 0) { - if(!ns || (cur->ns && - xmlStrcasecmp(cur->ns->prefix, (unsigned char *)ns) == 0)) + if(!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix, + (unsigned char *)ns) == 0)) return cur; } match = xmlNodeGetNodeByName(cur->children, name, ns); diff --git a/src/modules/lost/pidf.h b/src/modules/lost/pidf.h index 92d0379be88..f13b9a1755a 100755 --- a/src/modules/lost/pidf.h +++ b/src/modules/lost/pidf.h @@ -39,7 +39,7 @@ #include #include -#define BUFSIZE 128 /* temporary buffer to hold geolocation */ +#define BUFSIZE 128 /* temporary buffer to hold geolocation */ #define RANDSTRSIZE 16 /* temporary id in a findService request */ xmlNodePtr xmlNodeGetNodeByName( diff --git a/src/modules/lost/utilities.c b/src/modules/lost/utilities.c index 4e3450b21b2..257c0ee52b4 100644 --- a/src/modules/lost/utilities.c +++ b/src/modules/lost/utilities.c @@ -380,7 +380,7 @@ char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth) && (hf->name.len == LOST_GEOLOC_HEADER_SIZE - 2)) { /* possible hit */ if(strncasecmp(hf->name.s, LOST_GEOLOC_HEADER, - LOST_GEOLOC_HEADER_SIZE) == 0) { + LOST_GEOLOC_HEADER_SIZE) == 0) { res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char)); if(res == NULL) { @@ -427,7 +427,7 @@ char *lost_get_pai_header(struct sip_msg *msg, int *lgth) && (hf->name.len == LOST_PAI_HEADER_SIZE - 2)) { /* possible hit */ if(strncasecmp(hf->name.s, LOST_PAI_HEADER, - LOST_PAI_HEADER_SIZE) == 0) { + LOST_PAI_HEADER_SIZE) == 0) { LM_DBG("P-A-I body: [%.*s]\n", hf->body.len, hf->body.s); @@ -512,6 +512,303 @@ char *lost_get_from_header(struct sip_msg *msg, int *lgth) return res; } +/* + * lost_delete_geoheader_list(list) + * removes geoheader list from private memory + */ +void lost_delete_geoheader_list(p_geolist_t list) +{ + + p_geolist_t curr; + + while((curr = list) != NULL) { + list = curr->next; + if(curr->value != NULL) { + pkg_free(curr->value); + } + if(curr->param != NULL) { + pkg_free(curr->param); + } + pkg_free(curr); + } + + list = NULL; + + return; +} + +/* + * lost_get_geoheader_value(list, type, rtype) + * returns geoheader value and type (rtype) of given type + */ +char *lost_get_geoheader_value(p_geolist_t list, geotype_t type, int *rtype) +{ + + p_geolist_t head = list; + char *value = NULL; + + if(head == NULL) { + return value; + } + + /* type is not important, take first element value and type */ + if((type == ANY) || (type == UNKNOWN)) { + *rtype = head->type; + return head->value; + } + + /* take first element value and type of given type */ + while(head) { + if(type == head->type) { + value = head->value; + *rtype = head->type; + break; + } + head = head->next; + } + + return value; +} + + +/* + * lost_reverse_geoheader_list(list) + * reverses list order + */ +void lost_reverse_geoheader_list(p_geolist_t *head) +{ + + p_geolist_t prev = NULL; + p_geolist_t next = NULL; + p_geolist_t current = *head; + + while(current != NULL) { + next = current->next; + current->next = prev; + prev = current; + current = next; + } + + *head = prev; +} + +/* + * lost_copy_geoheader_value(src, len) + * returns a header vaule string (src to src + len) allocated in private memory + */ +char *lost_copy_geoheader_value(char *src, int len) +{ + + char *res = NULL; + + res = (char *)pkg_malloc((len + 1) * sizeof(char)); + if(res == NULL) { + LM_ERR("no more private memory\n"); + return res; + } else { + memset(res, 0, len + 1); + memcpy(res, src, len + 1); + res[len] = '\0'; + } + + return res; +} + +/* + * lost_new_geoheader_list(hdr, items) + * searches and parses Geolocation header and returns a list + * allocated in private memory and an item count + */ +p_geolist_t lost_new_geoheader_list(str hdr, int *items) +{ + + char *search = NULL; + char *cidptr = NULL; + char *urlptr = NULL; + char *ptr = NULL; + + int count = 0; + int len = 0; + + p_geolist_t list = NULL; + p_geolist_t new = NULL; + + LM_DBG("parsing geolocation header value ...\n"); + + /* search the complete header field */ + search = hdr.s; + for(int i = 0; i < hdr.len; i++) { + /* check for cid content */ + /* might be the shortest */ + if(strlen(search) > 6) { + if((*(search + 0) == '<') + && ((*(search + 1) == 'c') || (*(search + 1) == 'C')) + && ((*(search + 2) == 'i') || (*(search + 2) == 'I')) + && ((*(search + 3) == 'd') || (*(search + 3) == 'D')) + && (*(search + 4) == ':')) { + cidptr = search + 4; + *cidptr = LAQUOT; + ptr = cidptr; + len = 1; + while(*(ptr + len) != '>') { + if((len == strlen(ptr)) || (*(ptr + len) == '<')) { + LM_WARN("invalid cid: [%.*s]\n", hdr.len, hdr.s); + break; + } + len++; + } + if((*(ptr + len) == '>') && (len > 6)) { + new = (p_geolist_t)pkg_malloc(sizeof(s_geolist_t)); + if(new == NULL) { + LM_ERR("no more private memory\n"); + } else { + + LM_DBG("\t[%.*s]\n", len + 1, cidptr); + + new->value = lost_copy_geoheader_value(cidptr, len + 1); + new->param = NULL; + new->type = CID; + new->next = list; + list = new; + count++; + + LM_DBG("adding cid [%s]\n", new->value); + } + } else { + LM_WARN("invalid value: [%.*s]\n", hdr.len, hdr.s); + } + } + } + + /* check for http(s) content */ + /* might be the shortest */ + if(strlen(search) > 10) { + if((*(search + 0) == '<') + && ((*(search + 1) == 'h') || (*(search + 1) == 'H')) + && ((*(search + 2) == 't') || (*(search + 2) == 'T')) + && ((*(search + 3) == 't') || (*(search + 3) == 'T')) + && ((*(search + 4) == 'p') || (*(search + 4) == 'P'))) { + urlptr = search + 1; + ptr = urlptr; + len = 0; + while(*(ptr + len) != '>') { + if((len == strlen(ptr)) || (*(ptr + len) == '<')) { + LM_WARN("invalid url: [%.*s]\n", hdr.len, hdr.s); + break; + } + len++; + } + if((*(ptr + len) == '>') && (len > 10)) { + new = (p_geolist_t)pkg_malloc(sizeof(s_geolist_t)); + if(new == NULL) { + LM_ERR("no more private memory\n"); + } else { + + LM_DBG("\t[%.*s]\n", len, urlptr); + + new->value = lost_copy_geoheader_value(urlptr, len); + new->param = NULL; + if(*(search + 5) == ':') { + + LM_DBG("adding http url [%s]\n", new->value); + + new->type = HTTP; + } else if(((*(search + 5) == 's') + || (*(search + 5) == 'S')) + && (*(search + 6) == ':')) { + + LM_DBG("adding https url [%s]\n", new->value); + + new->type = HTTPS; + } + new->next = list; + list = new; + count++; + } + } else { + LM_WARN("invalid value: [%.*s]\n", hdr.len, hdr.s); + } + } + } + search++; + } + + *items = count; + + return list; +} + +/* + * lost_parse_pidf(pidf, urn) + * parses pidf and returns a new location object + */ +p_loc_t lost_parse_pidf(str pidf, str urn) +{ + + p_loc_t loc = NULL; + + xmlDocPtr doc = NULL; + xmlNodePtr root = NULL; + + /* read and parse pidf-lo */ + doc = xmlReadMemory(pidf.s, pidf.len, 0, NULL, + XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA); + + if(doc == NULL) { + LM_WARN("invalid xml (pidf-lo): [%.*s]\n", pidf.len, pidf.s); + doc = xmlRecoverMemory(pidf.s, pidf.len); + if(doc == NULL) { + LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len, + pidf.s); + goto err; + } + + LM_DBG("xml (pidf-lo) recovered\n"); + } + + root = xmlDocGetRootElement(doc); + if(root == NULL) { + LM_ERR("empty pidf-lo document\n"); + goto err; + } + if((!xmlStrcmp(root->name, (const xmlChar *)"presence")) + || (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) { + /* get the geolocation: point or circle, urn, ... */ + loc = lost_new_loc(urn); + if(loc == NULL) { + LM_ERR("location object allocation failed\n"); + goto err; + } + if(lost_parse_location_info(root, loc) < 0) { + LM_ERR("location element not found\n"); + goto err; + } + } else { + LM_ERR("findServiceResponse or presence element not found in " + "[%.*s]\n", + pidf.len, pidf.s); + goto err; + } + + /* free memory */ + xmlFreeDoc(doc); + doc = NULL; + + return loc; + +err: + LM_ERR("pidflo parsing error\n"); + /* free memory */ + if(doc != NULL) { + xmlFreeDoc(doc); + doc = NULL; + } + if(loc != NULL) { + lost_free_loc(loc); + } + return NULL; +} + /* * lost_parse_geo(node, loc) * parses locationResponse (pos|circle) and writes @@ -586,6 +883,7 @@ int lost_parse_geo(xmlNodePtr node, p_loc_t loc) LM_ERR("no more private memory\n"); return -1; } + /* * lost_xpath_location(doc, path, loc) * performs xpath expression on locationResponse and writes @@ -733,7 +1031,7 @@ int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc) memset(ptr, 0, buffersize); memcpy(ptr, (char *)(xmlbuff + remove), buffersize); ptr[buffersize] = '\0'; - + /* trim the result */ tmp = lost_trim_content(ptr, &len); diff --git a/src/modules/lost/utilities.h b/src/modules/lost/utilities.h index 84a517e83db..a77bd83af18 100644 --- a/src/modules/lost/utilities.h +++ b/src/modules/lost/utilities.h @@ -32,6 +32,8 @@ #ifndef LOST_UTILITIES_H #define LOST_UTILITIES_H +#define LAQUOT '<' + #define LOST_GEOLOC_HEADER "Geolocation: " #define LOST_GEOLOC_HEADER_SIZE strlen(LOST_GEOLOC_HEADER) #define LOST_PAI_HEADER "P-Asserted-Identity: " @@ -67,35 +69,54 @@ #define HELD_EXACT_TRUE 1 #define HELD_EXACT_FALSE 0 -#define BUFSIZE 128 /* temporary buffer to hold geolocation */ +#define BUFSIZE 128 /* temporary buffer to hold geolocation */ #define RANDSTRSIZE 16 /* temporary id in a findService request */ typedef struct LOC { - char *identity; /* location idendity (findServiceRequest) */ - char *urn; /* service URN (findServiceRequest) */ - char *xpath; /* civic address (findServiceRequest) */ - char *geodetic; /* geodetic location (findServiceRequest) */ - char *longitude; /* geo longitude */ - char *latitude; /* geo latitude */ - char *profile; /* location profile (findServiceRequest) */ - int radius; /* geo radius (findServiceRequest) */ - int recursive; /* recursion true|false (findServiceRequest)*/ - int boundary; /* boundary ref|value (findServiceRequest)*/ + char *identity; /* location idendity (findServiceRequest) */ + char *urn; /* service URN (findServiceRequest) */ + char *xpath; /* civic address (findServiceRequest) */ + char *geodetic; /* geodetic location (findServiceRequest) */ + char *longitude; /* geo longitude */ + char *latitude; /* geo latitude */ + char *profile; /* location profile (findServiceRequest) */ + int radius; /* geo radius (findServiceRequest) */ + int recursive; /* recursion true|false (findServiceRequest)*/ + int boundary; /* boundary ref|value (findServiceRequest)*/ } s_loc_t, *p_loc_t; typedef struct HELD { - char *identity; /* location idendity (locationRequest) */ - char *type; /* location type (locationRequest) */ - int time; /* response time (locationRequest) */ - int exact; /* exact true|false (locationRequest)*/ + char *identity; /* location idendity (locationRequest) */ + char *type; /* location type (locationRequest) */ + int time; /* response time (locationRequest) */ + int exact; /* exact true|false (locationRequest)*/ } s_held_t, *p_held_t; +typedef enum GEOTYPE +{ + ANY, /* any type */ + CID, /* content-indirection */ + HTTP, /* http uri */ + HTTPS, /* https uri */ + UNKNOWN = -1 /* unknown */ +} geotype_t; + +typedef struct GEOLIST +{ + char *value; /* geolocation header value */ + char *param; /* value parameter */ + geotype_t type; /* type */ + struct GEOLIST *next; +} s_geolist_t, *p_geolist_t; + void lost_rand_str(char *, size_t); void lost_free_loc(p_loc_t); void lost_free_held(p_held_t); void lost_free_string(str *); +void lost_reverse_geoheader_list(p_geolist_t *); +void lost_delete_geoheader_list(p_geolist_t); int lost_parse_location_info(xmlNodePtr, p_loc_t); int lost_xpath_location(xmlDocPtr, char *, p_loc_t); @@ -110,8 +131,12 @@ char *lost_get_from_header(struct sip_msg *, int *); char *lost_get_pai_header(struct sip_msg *, int *); char *lost_get_childname(xmlNodePtr, const char *, int *); char *lost_trim_content(char *, int *); +char *lost_copy_geoheader_value(char *, int); +char *lost_get_geoheader_value(p_geolist_t, geotype_t, int *); p_loc_t lost_new_loc(str); +p_loc_t lost_parse_pidf(str, str); p_held_t lost_new_held(str, str, int, int); +p_geolist_t lost_new_geoheader_list(str, int *); #endif