lost: added civic address parsing via xpath
authorwkampich <wolfgang.kampichler@gmail.com>
Sun, 8 Mar 2020 08:23:53 +0000 (09:23 +0100)
committerwkampich <wolfgang.kampichler@gmail.com>
Sun, 8 Mar 2020 08:23:53 +0000 (09:23 +0100)
- the pidf-lo parsing function now uses xpath to support
  geolocation (point, circle) and civic address.
- as fall-back (failing xpath or malformed pidf-lo) the
  the function looks for point or cirle attributes.
- besides: README update and code refactoring.

src/modules/lost/doc/lost.xml
src/modules/lost/doc/lost_admin.xml
src/modules/lost/functions.c
src/modules/lost/lost.c
src/modules/lost/pidf.c [changed mode: 0755->0644]
src/modules/lost/pidf.h
src/modules/lost/utilities.c
src/modules/lost/utilities.h

index effd90d..34fe9d0 100644 (file)
@@ -25,7 +25,7 @@
            </editor>
        </authorgroup>
        <copyright>
-               <year>20018-2019</year>
+               <year>20018-2020</year>
                <holder>Wolfgang Kampichler</holder>
        </copyright>
        </bookinfo>
index b5701d3..a3131a0 100644 (file)
@@ -17,9 +17,9 @@
         SIP requests may be forwarded based on a location provided with the
         request or retrieved from a specific location server using an identity
         (HELD). This module implements the basic functionality to get or parse
-        location information and to query a mapping service (LOST) in order to
-        get next hop based on location and service urn either specified or
-        provided with the request. 
+        location information (civic and geodetic) and to query a mapping service
+        (LOST) in order to get next hop based on location and service urn either
+        specified or provided with the request. 
         </para>
         <para>
         This module implements protocol functions that use the http_client api
@@ -42,7 +42,7 @@
         The function lost_query allows &kamailio; to assemble a LOST
         findService request  as defined in RFC5222
         (<ulink url="https://tools.ietf.org/html/rfc5255"/>) to query 
-        routing information for a given (geodetic) location and a service
+        routing information for a given (geodetic or civic) location and a service
         URN. Both, PIDF-LO and service URN may be provided as function parameter,
         or are taken from the request message if applicable. The findServiceResponse
         is parsed and represented asdisplay name and SIP URI typically used as next
@@ -217,19 +217,19 @@ $var(res) = lost_held_query("heldsrv", "$var(id)" , "$var(pidf)", "$var(url)", "
 # LOST findService request - pidf-lo and urn as parameter
 $var(id) = "urn:service:sos";
 $var(res) = lost_query("lostsrv", "$var(pidf)", "$var(urn)", "$var(uri)", "$var(name)", "$var(err)");
-xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(uri)\n");
+xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(name)\n");
 ...
 # LOST findService request - pidf-lo as parameter, urn taken from request line
 $var(res) = lost_query("lostsrv", "$var(pidf)", "", "$var(uri)", "$var(name)", "$var(err)");
-xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(uri)\n");
+xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(name)\n");
 ...
 # LOST findService request - urn as parameter, pidf-lo taken from message body
 $var(res) = lost_query("lostsrv", "", "$var(urn)", "$var(uri)", "$var(name)", "$var(err)");
-xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(uri)\n");
+xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(name\n");
 ...
 # LOST findService request - pidf-lo and urn taken from message
 $var(res) = lost_query("lostsrv", "$var(uri)", "$var(name)", "$var(err)");
-xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(uri)\n");
+xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(name)\n");
 ...
                                </programlisting>
                        </example>
@@ -259,7 +259,7 @@ xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $
             the HTTP response (query_params.oneline = 1), but the lost module requires the complete
             response message, which requires query_params.oneline set to 0. In the case lost_query
             is used with the default http_client API, dereferencing location via HTTP provided with
-                       the Geolocation header causes an error. Therefore, to work properlu=y, it requires to set
+                       the Geolocation header causes an error. Therefore, to work properly, it requires to set
                        <ulink url="https://www.kamailio.org/docs/modules/devel/modules/http_client.html#http_client.p.query_result">
                        http_client module parameter query_result to 0</ulink>.
         </para>
index 9f04265..d644604 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lost module functions
  *
- * Copyright (C) 2019 Wolfgang Kampichler
+ * Copyright (C) 2020 Wolfgang Kampichler
  * DEC112, FREQUENTIS AG
  *
  * This file is part of Kamailio, a free SIP server.
@@ -73,6 +73,8 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
        pv_value_t pvurl;
        pv_value_t pverr;
 
+       p_held_t held = NULL;
+
        xmlDocPtr doc = NULL;
        xmlNodePtr root = NULL;
 
@@ -84,6 +86,9 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
        str res = {NULL, 0};
        str idhdr = {NULL, 0};
 
+       str rtype = {HELD_TYPE, strlen(HELD_TYPE)};
+       str rtime = {HELD_TIME, strlen(HELD_TIME)};
+
        int curlres = 0;
 
        if(_con == NULL || _pidf == NULL || _url == NULL || _err == NULL) {
@@ -102,7 +107,7 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
                        goto err;
                }
                if(!did.s) {
-                       LM_ERR("no device found\n");
+                       LM_ERR("no device id found\n");
                        goto err;
                }
        } else {
@@ -115,7 +120,7 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
                        LM_WARN("P-A-I header not found, trying From header ...\n");
 
                        LM_DBG("parsing From header\n");
-                       
+
                        /* id from From header */
                        idhdr.s = lost_get_from_header(_m, &idhdr.len);
                        if(idhdr.len == 0) {
@@ -131,16 +136,26 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
        /* check if connection exists */
        if(httpapi.http_connection_exists(&con) == 0) {
                LM_ERR("connection: [%s] does not exist\n", con.s);
+               lost_free_string(&idhdr);
                goto err;
        }
 
        /* assemble locationRequest */
-       que.s = lost_held_location_request(did.s, &que.len);
+       held = lost_new_held(did, rtype, rtime, HELD_EXACT_TRUE);
+       if(!held) {
+               LM_ERR("held object allocation failed\n");
+               lost_free_string(&idhdr);
+               goto err;
+       }
+       que.s = lost_held_location_request(held, &que.len);
+
        /* free memory */
+       lost_free_held(held);
        lost_free_string(&idhdr);
        did.s = NULL;
        did.len = 0;
-       if(!que.s) {
+
+       if(que.len == 0) {
                LM_ERR("held request document error\n");
                goto err;
        }
@@ -149,7 +164,7 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
 
        /* send locationRequest to location server - HTTP POST */
        curlres = httpapi.http_connect(_m, &con, NULL, &res, mtheld, &que);
-       /* only HTTP 2xx responses are accepted */ 
+       /* only HTTP 2xx responses are accepted */
        if(curlres >= 300 || curlres < 100) {
                LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres);
                res.s = NULL;
@@ -168,8 +183,7 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
                LM_WARN("invalid xml document: [%.*s]\n", res.len, res.s);
                doc = xmlRecoverMemory(res.s, res.len);
                if(!doc) {
-                       LM_ERR("xml document recovery failed on: [%.*s]\n", res.len,
-                                       res.s);
+                       LM_ERR("xml document recovery failed on: [%.*s]\n", res.len, res.s);
                        goto err;
                }
 
@@ -198,8 +212,7 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
                /* get the error patterm */
                err.s = lost_get_property(root, (char *)"code", &err.len);
                if(!err.s) {
-                       LM_ERR("error - code property not found: [%.*s]\n", res.len,
-                                       res.s);
+                       LM_ERR("error - code property not found: [%.*s]\n", res.len, res.s);
                        goto err;
                }
                LM_WARN("locationRequest error response: [%.*s]\n", err.len, err.s);
@@ -208,6 +221,7 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
                goto err;
        }
        xmlFreeDoc(doc);
+       doc = NULL;
 
        /* set writeable pvars */
        pvpidf.rs = res;
@@ -239,9 +253,6 @@ int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
 err:
        if(doc)
                xmlFreeDoc(doc);
-       
-       lost_free_string(&idhdr);
-       lost_free_string(&que);
 
        return LOST_CLIENT_ERROR;
 }
@@ -281,7 +292,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
        struct msg_start *fl;
        char *search = NULL;
        int curlres = 0;
-       
+
        if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) {
                LM_ERR("invalid parameter\n");
                goto err;
@@ -331,7 +342,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
        if(pidf.len == 0) {
                LM_WARN("no pidf parameter, trying geolocation header ...\n");
                geohdr.s = lost_get_geolocation_header(_m, &geohdr.len);
-               if(!geohdr.s) {
+               if(geohdr.len == 0) {
                        LM_ERR("geolocation header not found\n");
                        goto err;
                } else {
@@ -356,8 +367,12 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
                                        /* get body part - filter=>content id */
                                        pidf.s = get_body_part_by_filter(
                                                        _m, 0, 0, geo.s, NULL, &pidf.len);
-                                       if(!pidf.s) {
+                                       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;
                                        }
                                }
@@ -370,30 +385,36 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
                                        geo.len = geohdr.len;
 
                                        if(*(search + 4) == ':') {
-                                       
+
                                                LM_DBG("http url: [%.*s]\n", geo.len, geo.s);
-                                       
+
                                        } else if(((*(search + 4) == 's') || (*(search + 4) == 'S'))
-                                                       && (*(search + 5) == ':')) {
-                                       
+                                                         && (*(search + 5) == ':')) {
+
                                                LM_DBG("https url: [%.*s]\n", geo.len, geo.s);
-                                       
+
                                        } 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;
                                        }
 
                                        /* ! 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);
+                                       curlres = httpapi.http_client_query(
+                                                       _m, geo.s, &pidfhdr, NULL, NULL);
                                        /* free memory */
-                                       lost_free_string(&geohdr);
                                        geo.s = NULL;
                                        geo.len = 0;
-                                       /* only HTTP 2xx responses are accepted */ 
+                                       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 */
                                                pidfhdr.s = NULL;
                                                pidfhdr.len = 0;
                                                goto err;
@@ -401,8 +422,10 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
 
                                        LM_DBG("http GET returned: %d\n", curlres);
 
-                                       if(!pidfhdr.s) {
+                                       if(pidfhdr.len == 0) {
                                                LM_ERR("dereferencing location failed\n");
+                                               /* free memory */
+
                                                goto err;
                                        }
                                        pidf.s = pidfhdr.s;
@@ -416,7 +439,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
        }
 
        /* no pidf-lo return error */
-       if(!pidf.s) {
+       if(pidf.len == 0) {
                LM_ERR("pidf-lo not found\n");
                goto err;
        }
@@ -431,8 +454,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
                LM_WARN("invalid xml (pidf-lo): [%.*s]\n", pidf.len, pidf.s);
                doc = xmlRecoverMemory(pidf.s, pidf.len);
                if(!doc) {
-                       LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len,
-                                       pidf.s);
+                       LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len, pidf.s);
                        goto err;
                }
 
@@ -450,7 +472,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
                loc = lost_new_loc(urn);
                if(!loc) {
                        LM_ERR("location object allocation failed\n");
-                       goto err;                       
+                       goto err;
                }
                if(lost_parse_location_info(root, loc) < 0) {
                        LM_ERR("location element not found\n");
@@ -476,14 +498,13 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
        /* assemble findService request */
        res.s = lost_find_service_request(loc, &res.len);
        /* free memory */
-       if(loc) {
+       if(loc)
                lost_free_loc(loc);
-               loc = NULL;
-       }
+
        xmlFreeDoc(doc);
        doc = NULL;
 
-       if(!res.s) {
+       if(res.len == 0) {
                LM_ERR("lost request failed\n");
                goto err;
        }
@@ -492,7 +513,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
 
        /* send findService request to mapping server - HTTP POST */
        curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &res);
-       /* only HTTP 2xx responses are accepted */ 
+       /* only HTTP 2xx responses are accepted */
        if(curlres >= 300 || curlres < 100) {
                LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres);
                ret.s = NULL;
@@ -520,8 +541,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
                LM_ERR("invalid xml document: [%.*s]\n", ret.len, ret.s);
                doc = xmlRecoverMemory(ret.s, ret.len);
                if(!doc) {
-                       LM_ERR("xml document recovery failed on: [%.*s]\n", ret.len,
-                                       ret.s);
+                       LM_ERR("xml document recovery failed on: [%.*s]\n", ret.len, ret.s);
                        goto err;
                }
 
@@ -530,21 +550,27 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
        root = xmlDocGetRootElement(doc);
        if(!root) {
                LM_ERR("empty xml document: [%.*s]\n", ret.len, ret.s);
+               /* free memory */
+               lost_free_string(&ret);
                goto err;
        }
        /* check the root element, shall be findServiceResponse, or errors */
        if((!xmlStrcmp(root->name, (const xmlChar *)"findServiceResponse"))) {
                /* get the uri element */
                uri.s = lost_get_content(root, uri_element, &uri.len);
-               if(!uri.s) {
+               if(uri.len == 0) {
                        LM_ERR("uri element not found: [%.*s]\n", ret.len, ret.s);
+                       /* free memory */
+                       lost_free_string(&ret);
                        goto err;
                }
                LM_INFO("### LOST uri [%.*s]\n", uri.len, uri.s);
                /* get the displayName element */
                name.s = lost_get_content(root, name_element, &name.len);
-               if(!name.s) {
+               if(name.len == 0) {
                        LM_ERR("displayName element not found: [%.*s]\n", ret.len, ret.s);
+                       /* free memory */
+                       lost_free_string(&ret);
                        goto err;
                }
                LM_INFO("### LOST name [%.*s]\n", name.len, name.s);
@@ -554,21 +580,25 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
 
                /* get the error patterm */
                err.s = lost_get_childname(root, errors_element, &err.len);
-               if(!err.s) {
-                       LM_ERR("error pattern element not found: [%.*s]\n", ret.len,
-                                       ret.s);
+               LM_DBG("findService error response: [%.*s]\n", err.len, err.s);
+               if(err.len == 0) {
+                       LM_ERR("error pattern element not found: [%.*s]\n", ret.len, ret.s);
+                       /* free memory */
+                       lost_free_string(&ret);
                        goto err;
                }
                LM_WARN("findService error response: [%.*s]\n", err.len, err.s);
        } else {
                LM_ERR("root element is not valid: [%.*s]\n", ret.len, ret.s);
+               /* free memory */
+               lost_free_string(&ret);
                goto err;
        }
 
        /* free memory */
+       lost_free_string(&ret);
        xmlFreeDoc(doc);
        doc = NULL;
-       lost_free_string(&ret);
 
        /* set writable pvars */
        pvname.rs = name;
@@ -603,9 +633,5 @@ err:
        if(doc)
                xmlFreeDoc(doc);
 
-       lost_free_string(&pidfhdr);
-       lost_free_string(&geohdr);
-       lost_free_string(&ret);
-
        return LOST_CLIENT_ERROR;
 }
index ae5da27..5aa2d5d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lost module
  *
- * Copyright (C) 2019 Wolfgang Kampichler
+ * Copyright (C) 2020 Wolfgang Kampichler
  * DEC112, FREQUENTIS AG
  *
  * This file is part of Kamailio, a free SIP server.
old mode 100755 (executable)
new mode 100644 (file)
index 25302cd..7f9f040
@@ -100,7 +100,7 @@ xmlNodePtr xmlNodeGetNodeByName(
                if(xmlStrcasecmp(cur->name, (unsigned char *)name) == 0) {
                        if(!ns || (cur->ns &&
                                xmlStrcasecmp(cur->ns->prefix, (unsigned char *)ns) == 0))
-                       return cur;
+                               return cur;
                }
                match = xmlNodeGetNodeByName(cur->children, name, ns);
                if(match)
@@ -135,3 +135,82 @@ char *xmlDocGetNodeContentByName(
        else
                return NULL;
 }
+
+xmlXPathObjectPtr xmlGetNodeSet(xmlDocPtr doc, xmlChar *xpath, xmlChar *ns)
+{
+
+       xmlXPathContextPtr context = NULL;
+       xmlXPathObjectPtr result = NULL;
+
+       context = xmlXPathNewContext(doc);
+       if(context == NULL) {
+               return NULL;
+       }
+
+       if((ns != NULL) && (xmlRegisterNamespaces(context, ns) < 0)) {
+               xmlXPathFreeContext(context);
+               return NULL;
+       }
+
+       result = xmlXPathEvalExpression(xpath, context);
+       xmlXPathFreeContext(context);
+
+       if(result == NULL) {
+               LM_ERR("xmlXPathEvalExpression() failed\n");
+               return NULL;
+       }
+       if(xmlXPathNodeSetIsEmpty(result->nodesetval)) {
+               xmlXPathFreeObject(result);
+               LM_DBG("xmlXPathEvalExpression() returned no result\n");
+               return NULL;
+       }
+
+       return result;
+}
+
+int xmlRegisterNamespaces(xmlXPathContextPtr context, const xmlChar *ns)
+{
+       xmlChar *nsListDup;
+       xmlChar *prefix;
+       xmlChar *href;
+       xmlChar *next;
+
+       nsListDup = xmlStrdup(ns);
+       if(nsListDup == NULL) {
+               return -1;
+       }
+
+       next = nsListDup;
+       while(next != NULL) {
+               /* skip spaces */
+               while((*next) == ' ')
+                       next++;
+               if((*next) == '\0')
+                       break;
+
+               /* find prefix */
+               prefix = next;
+               next = (xmlChar *)xmlStrchr(next, '=');
+               if(next == NULL) {
+                       xmlFree(nsListDup);
+                       return -1;
+               }
+               *(next++) = '\0';
+
+               /* find href */
+               href = next;
+               next = (xmlChar *)xmlStrchr(next, ' ');
+               if(next != NULL) {
+                       *(next++) = '\0';
+               }
+
+               /* register namespace */
+               if(xmlXPathRegisterNs(context, prefix, href) != 0) {
+                       xmlFree(nsListDup);
+                       return -1;
+               }
+       }
+
+       xmlFree(nsListDup);
+       return 0;
+}
\ No newline at end of file
index c0b1e4b..92d0379 100755 (executable)
 
 #include "../../core/str.h"
 #include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define BUFSIZE 128    /* temporary buffer to hold geolocation */
+#define RANDSTRSIZE 16 /* temporary id in a findService request */
 
 xmlNodePtr xmlNodeGetNodeByName(
                xmlNodePtr node, const char *name, const char *ns);
 xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns);
 xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name);
+xmlXPathObjectPtr xmlGetNodeSet(xmlDocPtr doc, xmlChar *xpath, xmlChar *ns);
 
 char *xmlDocGetNodeContentByName(
                xmlDocPtr doc, const char *name, const char *ns);
@@ -47,4 +54,6 @@ char *xmlNodeGetNodeContentByName(
                xmlNodePtr root, const char *name, const char *ns);
 char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name);
 
+int xmlRegisterNamespaces(xmlXPathContextPtr context, const xmlChar *ns);
+
 #endif
index 542bae0..5e7e271 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lost module utility functions
  *
- * Copyright (C) 2019 Wolfgang Kampichler
+ * Copyright (C) 2020 Wolfgang Kampichler
  * DEC112, FREQUENTIS AG
  *
  * This file is part of Kamailio, a free SIP server.
@@ -35,8 +35,6 @@
 #include <ctype.h>
 #include <time.h>
 
-#include <libxml/xmlmemory.h>
-#include <libxml/parser.h>
 #include "../../core/parser/msg_parser.h"
 #include "../../core/parser/parse_content.h"
 #include "../../core/parser/parse_uri.h"
@@ -93,41 +91,14 @@ void lost_rand_str(char *dest, size_t lgth)
        *dest = '\0';
 }
 
-/*
- * lost_free_loc(ptr)
- * frees a location object
- */
-void lost_free_loc(p_loc_t ptr)
-{
-       pkg_free(ptr->identity);
-       pkg_free(ptr->urn);
-       pkg_free(ptr->longitude);
-       pkg_free(ptr->latitude);
-       pkg_free(ptr);
-}
-
-/*
- * lost_free_string(ptr)
- * frees and resets a string
- */
-void lost_free_string(str *string)
-{
-       str ptr = *string;
-
-       if(ptr.s) {
-               pkg_free(ptr.s);
-               ptr.s = NULL;
-               ptr.len = 0;
-       }
-}
-
 /*
  * lost_new_loc(urn)
  * creates a new location object in private memory and returns a pointer
  */
 p_loc_t lost_new_loc(str rurn)
 {
-       s_loc_t *ptr = NULL;;
+       s_loc_t *ptr = NULL;
+
        char *id = NULL;
        char *urn = NULL;
 
@@ -144,8 +115,8 @@ p_loc_t lost_new_loc(str rurn)
 
        urn = (char *)pkg_malloc(rurn.len + 1);
        if(urn == NULL) {
-               pkg_free(ptr);
                pkg_free(id);
+               pkg_free(ptr);
                goto err;
        }
 
@@ -159,16 +130,134 @@ p_loc_t lost_new_loc(str rurn)
        ptr->urn = urn;
        ptr->longitude = NULL;
        ptr->latitude = NULL;
+       ptr->geodetic = NULL;
+       ptr->civic = NULL;
+       ptr->profile = NULL;
        ptr->radius = 0;
-       ptr->recursive = 0;
+       ptr->recursive = LOST_RECURSION_TRUE;   /* set recursion to true */
+       ptr->boundary = 0;      /* set boundary to reference */
+
+       return ptr;
+
+err:
+       LM_ERR("no more private memory\n");
+       return NULL;
+}
+
+/*
+ * lost_new_held(uri, type, time, exact)
+ * creates a new held object in private memory and returns a pointer
+ */
+p_held_t lost_new_held(str s_uri, str s_type, str s_time, int exact)
+{
+       s_held_t *ptr = NULL;
+       
+       char *uri = NULL;
+       char *type = NULL;
+       char *time = NULL;
+
+       ptr = (s_held_t *)pkg_malloc(sizeof(s_held_t));
+       if(ptr == NULL) {
+               goto err;
+       }
+
+       uri = (char *)pkg_malloc(s_uri.len + 1);
+       if(uri == NULL) {
+               pkg_free(ptr);
+               goto err;
+       }
+
+       type = (char *)pkg_malloc(s_type.len + 1);
+       if(type == NULL) {
+               pkg_free(uri);
+               pkg_free(ptr);
+               goto err;
+       }
+
+       time = (char *)pkg_malloc(s_time.len + 1);
+       if(type == NULL) {
+               pkg_free(type);
+               pkg_free(uri);
+               pkg_free(ptr);
+               goto err;
+       }
+
+       memset(uri, 0, s_uri.len + 1);
+       memcpy(uri, s_uri.s, s_uri.len);
+       uri[s_uri.len] = '\0';
+
+       memset(type, 0, s_type.len + 1);
+       memcpy(type, s_type.s, s_type.len);
+       type[s_type.len] = '\0';
+
+       memset(time, 0, s_time.len + 1);
+       memcpy(time, s_time.s, s_time.len);
+       time[s_time.len] = '\0';
+
+       ptr->identity = uri;
+       ptr->type = type;
+       ptr->time = time;
+       ptr->exact = exact;
 
        return ptr;
 
-err:   
+err:
        LM_ERR("no more private memory\n");
        return NULL;
 }
 
+/*
+ * lost_free_loc(ptr)
+ * frees a location object
+ */
+void lost_free_loc(p_loc_t ptr)
+{
+       pkg_free(ptr->identity);
+       pkg_free(ptr->urn);
+       if (ptr->civic)
+       pkg_free(ptr->civic);
+    if (ptr->geodetic)
+               pkg_free(ptr->geodetic);
+    if (ptr->longitude)
+               pkg_free(ptr->longitude);
+    if (ptr->latitude)
+               pkg_free(ptr->latitude);
+    if (ptr->profile)
+           pkg_free(ptr->profile);
+       
+       pkg_free(ptr);
+       ptr = NULL;
+}
+
+/*
+ * lost_free_loc(ptr)
+ * frees a held location request object
+ */
+void lost_free_held(p_held_t ptr)
+{
+       pkg_free(ptr->identity);
+       pkg_free(ptr->type);
+       pkg_free(ptr->time);
+
+       pkg_free(ptr);
+       ptr = NULL;
+}
+
+/*
+ * lost_free_string(ptr)
+ * frees and resets a string
+ */
+void lost_free_string(str *string)
+{
+       str ptr = *string;
+
+       if(ptr.s) {
+               pkg_free(ptr.s);
+               ptr.s = NULL;
+               ptr.len = 0;
+       }
+}
+
 /*
  * lost_get_content(node, name, lgth)
  * gets a nodes "name" content and returns string allocated in private memory
@@ -176,13 +265,14 @@ err:
 char *lost_get_content(xmlNodePtr node, const char *name, int *lgth)
 {
        xmlNodePtr cur = node;
-       char *content;
+       char *content = NULL;
        char *cnt = NULL;
        int len;
 
        *lgth = 0;
+
        content = xmlNodeGetNodeContentByName(cur, name, NULL);
-       if (content == NULL) {
+       if(content == NULL) {
                LM_ERR("could not get XML node content\n");
                return cnt;
        } else {
@@ -216,8 +306,9 @@ char *lost_get_property(xmlNodePtr node, const char *name, int *lgth)
        int len;
 
        *lgth = 0;
+
        content = xmlNodeGetAttrContentByName(cur, name);
-       if (content == NULL) {
+       if(content == NULL) {
                LM_ERR("could not get XML node content\n");
                return cnt;
        } else {
@@ -252,23 +343,32 @@ char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth)
        int len;
 
        *lgth = 0;
+
        parent = xmlNodeGetNodeByName(cur, name, NULL);
+       if(parent == NULL) {
+               LM_ERR("xmlNodeGetNodeByName() failed\n");
+               return cnt;
+       }
+
        child = parent->children;
+       if(child == NULL) {
+               LM_ERR("%s has no children '%s'\n", parent->name, name);
+               return cnt;
+       }
 
-       if(child) {
-               len = strlen((char *)child->name);
-               cnt = (char *)pkg_malloc((len + 1) * sizeof(char));
-               if(cnt == NULL) {
-                       LM_ERR("no more private memory\n");
-                       return cnt;
-               }
+       len = strlen((char *)child->name);
+       cnt = (char *)pkg_malloc((len + 1) * sizeof(char));
+       if(cnt == NULL) {
+               LM_ERR("no more private memory\n");
+               return cnt;
+       }
 
-               memset(cnt, 0, len + 1);
-               memcpy(cnt, child->name, len);
-               cnt[len] = '\0';
+       memset(cnt, 0, len + 1);
+       memcpy(cnt, child->name, len);
+       cnt[len] = '\0';
+
+       *lgth = strlen(cnt);
 
-               *lgth = strlen(cnt);
-       }
        return cnt;
 }
 
@@ -293,9 +393,8 @@ char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth)
                if((hf->type == HDR_OTHER_T)
                                && (hf->name.len == LOST_GEOLOC_HEADER_SIZE - 2)) {
                        /* possible hit */
-                       if(strncasecmp(
-                                          hf->name.s, LOST_GEOLOC_HEADER, LOST_GEOLOC_HEADER_SIZE)
-                                       == 0) {
+                       if(strncasecmp(hf->name.s, LOST_GEOLOC_HEADER,
+                                                       LOST_GEOLOC_HEADER_SIZE) == 0) {
 
                                res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));
                                if(res == NULL) {
@@ -315,6 +414,7 @@ char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth)
                        break;
                }
        }
+
        return res;
 }
 
@@ -331,7 +431,7 @@ char *lost_get_pai_header(struct sip_msg *msg, int *lgth)
 
        *lgth = 0;
 
-       if (parse_headers(msg, HDR_PAI_F, 0) == -1) {
+       if(parse_headers(msg, HDR_PAI_F, 0) == -1) {
                LM_ERR("could not parse P-A-I header\n");
                return res;
        }
@@ -340,27 +440,28 @@ char *lost_get_pai_header(struct sip_msg *msg, int *lgth)
                if((hf->type == HDR_PAI_T)
                                && (hf->name.len == LOST_PAI_HEADER_SIZE - 2)) {
                        /* possible hit */
-                       if(strncasecmp(hf->name.s, LOST_PAI_HEADER, LOST_PAI_HEADER_SIZE)
-                                       == 0) {
+                       if(strncasecmp(hf->name.s, LOST_PAI_HEADER,
+                                                       LOST_PAI_HEADER_SIZE) == 0) {
 
                                LM_DBG("P-A-I body:  [%.*s]\n", hf->body.len, hf->body.s);
 
                                /* first, get some memory */
                                pai_body = pkg_malloc(sizeof(to_body_t));
-                               if (pai_body == NULL) {
+                               if(pai_body == NULL) {
                                        LM_ERR("no more private memory\n");
                                        return res;
                                }
                                /* parse P-A-I body */
                                memset(pai_body, 0, sizeof(to_body_t));
                                parse_to(hf->body.s, hf->body.s + hf->body.len + 1, pai_body);
-                               if (pai_body->error == PARSE_ERROR) {
+                               if(pai_body->error == PARSE_ERROR) {
                                        LM_ERR("bad P-A-I header\n");
                                        pkg_free(pai_body);
                                        return res;
                                }
-                               if (pai_body->error == PARSE_OK) {
-                                       res = (char *)pkg_malloc((pai_body->uri.len + 1) * sizeof(char));
+                               if(pai_body->error == PARSE_OK) {
+                                       res = (char *)pkg_malloc(
+                                                       (pai_body->uri.len + 1) * sizeof(char));
                                        if(res == NULL) {
                                                LM_ERR("no more private memory\n");
                                                pkg_free(pai_body);
@@ -381,6 +482,7 @@ char *lost_get_pai_header(struct sip_msg *msg, int *lgth)
                        break;
                }
        }
+
        return res;
 }
 
@@ -420,63 +522,267 @@ char *lost_get_from_header(struct sip_msg *msg, int *lgth)
 
                *lgth = strlen(res);
        }
+
        return res;
 }
 
 /*
- * lost_parse_location_info(node, loc)
- * parses locationResponse and writes results to location object
+ * lost_parse_geo(node, loc)
+ * parses locationResponse (pos|circle) and writes 
+ * results to location object
  */
-int lost_parse_location_info(xmlNodePtr node, p_loc_t loc)
+int lost_parse_geo(xmlNodePtr node, p_loc_t loc)
 {
+       xmlNodePtr cur = NULL;
+
        char bufLat[BUFSIZE];
        char bufLon[BUFSIZE];
-       int iRadius;
        char *content = NULL;
-       int ret = -1;
 
-       xmlNodePtr cur = node;
+       char s_profile[] = LOST_PRO_GEO2D;
 
+       int iRadius = 0;
+       int len = 0;
+
+       cur = node;
+       /* find <pos> element */
        content = xmlNodeGetNodeContentByName(cur, "pos", NULL);
-       if(content) {
-               sscanf(content, "%s %s", bufLat, bufLon);
 
-               loc->latitude = (char *)pkg_malloc(strlen((char *)bufLat) + 1);
-               snprintf(loc->latitude, strlen((char *)bufLat) + 1, "%s",
-                               (char *)bufLat);
+       if(content == NULL) {
+               LM_WARN("could not find pos element\n");
+               return -1;
+       }
+
+       sscanf(content, "%s %s", bufLat, bufLon);
+       xmlFree(content);
+
+       len = strlen((char *)bufLat);
+       loc->latitude = (char *)pkg_malloc(len + 1);
+       if(loc->latitude == NULL)
+               goto err;
 
-               loc->longitude = (char *)pkg_malloc(strlen((char *)bufLon) + 1);
-               snprintf(loc->longitude, strlen((char *)bufLon) + 1, "%s",
-                               (char *)bufLon);
+       snprintf(loc->latitude, len, "%s", (char *)bufLat);
 
-               loc->radius = 0;
-               ret = 0;
+       len = strlen((char *)bufLon);
+       loc->longitude = (char *)pkg_malloc(len + 1);
+       if(loc->longitude == NULL) {
+               pkg_free(loc->latitude);
+               goto err;
        }
 
-       content = xmlNodeGetNodeContentByName(cur, "radius", NULL);
-       if(content) {
-               iRadius = 0;
+       snprintf(loc->longitude, len, "%s", (char *)bufLon);
+
+       len = strlen((char *)bufLat) + strlen((char *)bufLon) + 1;
+       loc->geodetic = (char *)pkg_malloc(len + 1);
+       if(loc->longitude == NULL) {
+               pkg_free(loc->latitude);
+               pkg_free(loc->longitude);
+               goto err;
+       }
+       
+       snprintf(loc->geodetic, len, "%s %s", (char *)bufLat, (char *)bufLon);
 
+       /* find <radius> element */
+       content = xmlNodeGetNodeContentByName(cur, "radius", NULL);
+       if(content != NULL) {
                sscanf(content, "%d", &iRadius);
-               loc->radius = iRadius;
-               ret = 0;
+               xmlFree(content);
+       }
+
+       /* write results */
+       loc->radius = iRadius;
+       loc->profile = (char *)pkg_malloc(strlen(s_profile) + 1);
+    strcpy(loc->profile, s_profile);
+
+       return 0;
+
+err:
+       LM_ERR("no more private memory\n");
+       return -1;
+}
+/*
+ * lost_xpath_location(doc, path, loc)
+ * performs xpath expression on locationResponse and writes 
+ * results (location-info child element) to location object
+ */
+int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc)
+{
+       xmlXPathObjectPtr result = NULL;
+       xmlNodeSetPtr nodes = NULL;
+       xmlNodePtr root = NULL;
+       xmlNodePtr cur = NULL;
+       xmlDocPtr new = NULL;
+       xmlChar *xpath = NULL;
+       xmlChar *xmlbuff = NULL;
+       xmlChar *cname = NULL;
+
+       const unsigned char s_point[] = LOST_PNT;
+       const unsigned char s_circle[] = LOST_CIR;
+       const unsigned char s_civic[] = LOST_CIV;
+
+       char *ptr = NULL;
+       char *s_profile = NULL;
+
+       int buffersize = 0;
+       int remove = 0;
+       int size = 0;
+       int len = 0;
+       int i = 0;
+       int nok = -1;
+
+       xpath = (xmlChar *)path;
+       /* get location via xpath expression */
+       result = xmlGetNodeSet(doc, xpath, BAD_CAST XPATH_NS);
+
+       if(result == NULL) {
+               LM_DBG("xmlGetNodeSet() returned no result\n");
+               return -1;
+       }
+
+       nodes = result->nodesetval;
+       if(nodes) {
+               size = (nodes) ? nodes->nodeNr : 0;
+               for(i = 0; i < size; ++i) {
+                       if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
+                               cur = nodes->nodeTab[i];
+                               /* check if child element is point, circle or civic */
+                               while(nok < LOST_XPATH_DPTH) {
+                                       if(cur->children) {
+                                               nok++;
+                                               cname = BAD_CAST cur->name;
+                                               if(xmlStrcasecmp(cname, s_point) == 0) {
+                                                       s_profile = LOST_PRO_GEO2D;
+                                                       break;
+                                               }
+                                               if(xmlStrcasecmp(cname, s_circle) == 0) {
+                                                       s_profile = LOST_PRO_GEO2D;
+                                                       break;
+                                               }
+                                               if(xmlStrcasecmp(cname, s_civic) == 0) {
+                                                       s_profile = LOST_PRO_CIVIC;
+                                                       break;
+                                               }
+                                       }
+                                       /* nothing found ... try next DOM level */
+                                       cur = cur->children;
+                               }
+
+                               if(nok == 0) {
+                                       LM_DBG("xpath '%s' returned valid element (level %d/%d)\n",
+                                                       xpath, nok, LOST_XPATH_DPTH - 1);
+                               } else if(nok < LOST_XPATH_DPTH) {
+                                       /* malformed pidf-lo but still ok */
+                                       LM_WARN("xpath '%s' returned malformed pidf-lo (level "
+                                                       "%d/%d)\n",
+                                                       xpath, nok, LOST_XPATH_DPTH - 1);
+                               } else {
+                                       /* really bad pidf-lo */
+                                       LM_WARN("xpath '%s' failed (level %d/%d)\n", xpath, nok,
+                                                       LOST_XPATH_DPTH - 1);
+                                       xmlXPathFreeObject(result);
+                                       return -1;
+                               }
+                               nok = -1;
+
+                               if(!cur) {
+                                       LM_ERR("xpath xmlCopyNode() failed\n");
+                                       xmlXPathFreeObject(result);
+                                       return -1;
+                               }
+
+                               root = xmlCopyNode(cur, 1);
+                               if(!root) {
+                                       LM_ERR("xpath xmlCopyNode() failed\n");
+                                       xmlXPathFreeObject(result);
+                                       return -1;
+                               }
+                               new = xmlNewDoc(BAD_CAST "1.0");
+                               if(!new) {
+                                       LM_ERR("xpath xmlNewDoc() failed\n");
+                                       xmlXPathFreeObject(result);
+                                       return -1;
+                               }
+                               xmlDocSetRootElement(new, root);
+                               xmlDocDumpFormatMemory(new, &xmlbuff, &buffersize, 0);
+                               if(!xmlbuff) {
+                                       LM_ERR("xpath xmlDocDumpFormatMemory() failed\n");
+                                       xmlFreeDoc(new);
+                                       xmlXPathFreeObject(result);
+                                       return -1;
+                               }
+                               /* take the first location-info element only */
+                               if(i == 0) {
+                                       remove = strlen("<?xml version='1.0'?>\n");
+                                       buffersize = buffersize - remove;
+                                       ptr = (char *)pkg_malloc((buffersize + 1) * sizeof(char));
+                                       if(ptr == NULL) {
+                                               xmlFree(xmlbuff);
+                                               xmlFreeDoc(new);
+                                               xmlXPathFreeObject(result);
+                                               goto err;
+                                       }
+
+                                       loc->profile = (char *)pkg_malloc(strlen(s_profile) + 1);
+                                       if(loc->profile == NULL) {
+                                               pkg_free(ptr);
+                                               xmlFree(xmlbuff);
+                                               xmlFreeDoc(new);
+                                               xmlXPathFreeObject(result);
+                                               goto err;
+                                       }
+
+                                       memset(ptr, 0, buffersize);
+                                       memcpy(ptr, (char *)(xmlbuff + remove), buffersize);
+                                       ptr[buffersize] = '\0';
+                                       loc->civic = lost_trim_content(ptr, &len);
+
+                                       memset(loc->profile, 0, strlen(s_profile) + 1);
+                                       memcpy(loc->profile, (char *)s_profile, strlen(s_profile));
+                               } else {
+                                       LM_WARN("xpath location-info element(%d) ignored\n", i + 1);
+                               }
+                               xmlFree(xmlbuff);
+                               xmlFreeDoc(new);
+                       }
+               }
+       }
+       xmlXPathFreeObject(result);
+
+       return 0;
+
+err:
+       LM_ERR("no more private memory\n");
+       return -1;
+}
+
+/*
+ * lost_parse_location_info(node, loc)
+ * wrapper to call xpath or simple pos|circle parser (last resort)
+ */
+int lost_parse_location_info(xmlNodePtr root, p_loc_t loc)
+{
+
+       if(lost_xpath_location(root->doc, LOST_XPATH_GP, loc) == 0) {
+               return 0;
        }
 
-       if(ret < 0) {
-               LM_ERR("could not parse location information\n");
+       LM_WARN("xpath expression failed ... trying pos|circle\n");
+
+       if(lost_parse_geo(root, loc) == 0) {
+               return 0;
        }
-       return ret;
+
+       return -1;
 }
 
 /*
- * lost_held_location_request(id, lgth)
+ * lost_held_location_request(id, lgth, responsetime, exact, type)
  * assembles and returns locationRequest string (allocated in private memory)
  */
-char *lost_held_location_request(char *id, int *lgth)
+char *lost_held_location_request(p_held_t held, int *lgth)
 {
        int buffersize = 0;
 
-       char buf[BUFSIZE];
        char *doc = NULL;
 
        xmlChar *xmlbuff = NULL;
@@ -518,12 +824,15 @@ https://tools.ietf.org/html/rfc6155
        /* properties */
        xmlNewProp(ptrLocationRequest, BAD_CAST "xmlns",
                        BAD_CAST "urn:ietf:params:xml:ns:geopriv:held");
-       xmlNewProp(ptrLocationRequest, BAD_CAST "responseTime", BAD_CAST "8");
+       xmlNewProp(ptrLocationRequest, BAD_CAST "responseTime",
+                               BAD_CAST held->time);
        /* locationType - element */
        ptrLocationType = xmlNewChild(ptrLocationRequest, NULL,
-                       BAD_CAST "locationType", BAD_CAST "geodetic locationURI");
+                       BAD_CAST "locationType", BAD_CAST held->type);
        /* properties */
-       xmlNewProp(ptrLocationType, BAD_CAST "exact", BAD_CAST "false");
+       xmlNewProp(ptrLocationType, BAD_CAST "exact",
+                       (held->exact == HELD_EXACT_TRUE) ?
+                       BAD_CAST "true" : BAD_CAST "false");
        /* device - element */
        ptrDevice = xmlNewChild(ptrLocationRequest, NULL, BAD_CAST "device", NULL);
        if(!ptrDevice) {
@@ -535,8 +844,7 @@ https://tools.ietf.org/html/rfc6155
        xmlNewProp(ptrDevice, BAD_CAST "xmlns",
                        BAD_CAST "urn:ietf:params:xml:ns:geopriv:held:id");
        /* uri - element */
-       snprintf(buf, BUFSIZE, "%s", id);
-       xmlNewChild(ptrDevice, NULL, BAD_CAST "uri", BAD_CAST buf);
+       xmlNewChild(ptrDevice, NULL, BAD_CAST "uri", BAD_CAST held->identity);
 
        xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);
        if(!xmlbuff) {
@@ -584,8 +892,10 @@ char *lost_find_service_request(p_loc_t loc, int *lgth)
        xmlNodePtr ptrPoint = NULL;
        xmlNodePtr ptrCircle = NULL;
        xmlNodePtr ptrRadius = NULL;
+       xmlNodePtr ptrNode = NULL;
 
        xmlKeepBlanksDefault(1);
+
        *lgth = 0;
 
        /*
@@ -624,17 +934,30 @@ https://tools.ietf.org/html/rfc5222
                        BAD_CAST "urn:ietf:params:xml:ns:lost1");
        xmlNewProp(ptrFindService, BAD_CAST "xmlns:p2",
                        BAD_CAST "http://www.opengis.net/gml");
-       xmlNewProp(
-                       ptrFindService, BAD_CAST "serviceBoundary", BAD_CAST "reference");
-       xmlNewProp(ptrFindService, BAD_CAST "recursive", BAD_CAST "true");
+       xmlNewProp(ptrFindService, BAD_CAST "serviceBoundary",
+                       (loc->boundary == 1) ? BAD_CAST "value" : BAD_CAST "reference");
+       xmlNewProp(ptrFindService, BAD_CAST "recursive",
+                       (loc->recursive == 1) ? BAD_CAST "true" : BAD_CAST "false");
        /* location - element */
        ptrLocation = xmlNewChild(ptrFindService, NULL, BAD_CAST "location", NULL);
        xmlNewProp(ptrLocation, BAD_CAST "id", BAD_CAST loc->identity);
-       xmlNewProp(ptrLocation, BAD_CAST "profile", BAD_CAST "geodetic-2d");
+       xmlNewProp(ptrLocation, BAD_CAST "profile", BAD_CAST loc->profile);
        /* set pos */
        snprintf(buf, BUFSIZE, "%s %s", loc->latitude, loc->longitude);
+       /* xpath result */
+       if(loc->civic) {
+               xmlParseInNodeContext(
+                               ptrLocation, loc->civic, strlen(loc->civic), 0, &ptrNode);
+               if(!ptrNode) {
+                       LM_ERR("locationRequest xmlParseInNodeContext() failed\n");
+                       xmlFreeDoc(request);
+                       return doc;
+               } else {
+                       xmlAddChild(ptrLocation, ptrNode);
+               }
+       }
        /* Point */
-       if(loc->radius == 0) {
+       else if(loc->radius == 0) {
                ptrPoint = xmlNewChild(ptrLocation, NULL, BAD_CAST "Point", NULL);
                if(!ptrPoint) {
                        LM_ERR("locationRequest xmlNewChild() failed\n");
@@ -647,8 +970,9 @@ https://tools.ietf.org/html/rfc5222
                                BAD_CAST "urn:ogc:def:crs:EPSG::4326");
                /* pos */
                xmlNewChild(ptrPoint, NULL, BAD_CAST "pos", BAD_CAST buf);
-       } else {
-               /* circle - Point */
+       }
+       /* circle - Point */
+       else {
                ptrCircle = xmlNewChild(ptrLocation, NULL, BAD_CAST "gs:Circle", NULL);
                if(!ptrCircle) {
                        LM_ERR("locationRequest xmlNewChild() failed\n");
index cb8f529..1f0f59c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lost module utility functions
  *
- * Copyright (C) 2019 Wolfgang Kampichler
+ * Copyright (C) 2020 Wolfgang Kampichler
  * DEC112, FREQUENTIS AG
  *
  * This file is part of Kamailio, a free SIP server.
 #define LOST_PAI_HEADER "P-Asserted-Identity: "
 #define LOST_PAI_HEADER_SIZE strlen(LOST_PAI_HEADER)
 
+#define LOST_RECURSION_TRUE 1
+#define LOST_RECURSION_FALSE 0
+#define LOST_XPATH_DPTH 3
+#define LOST_XPATH_GP "//gp:location-info/*"
+
+#define XPATH_NS                                         \
+       "gp=urn:ietf:params:xml:ns:pidf:geopriv10"           \
+       " "                                                  \
+       "xmlns=urn:ietf:params:xml:ns:pidf"                  \
+       " "                                                  \
+       "ca=urn:ietf:params:xml:ns:pidf:geopriv10:civicAddr" \
+       " "                                                  \
+       "gm=http://www.opengis.net/gml"
+
+#define LOST_PRO_GEO2D "geodetic-2d"
+#define LOST_PRO_CIVIC "civic"
+
+#define LOST_PNT "Point"
+#define LOST_CIR "Circle"
+#define LOST_CIV "civicAddress"
+
+#define HELD_TYPE "geodetic locationURI"
+#define HELD_TIME "3"
+#define HELD_EXACT_TRUE 1
+#define HELD_EXACT_FALSE 0
+
 #define BUFSIZE 128    /* temporary buffer to hold geolocation */
 #define RANDSTRSIZE 16 /* temporary id in a findService request */
 
-#define LOSTFREE(x) pkg_free(x); x = NULL;
-
-typedef struct
+typedef struct LOC
 {
-       char *identity;
-       char *urn;
-       char *longitude;
-       char *latitude;
-       char *uri;
-       char *ref;
-       int radius;
-       int recursive;
+       char *identity;         /* location idendity (findServiceRequest) */
+       char *urn;                      /* service URN (findServiceRequest) */ 
+       char *civic;            /* 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) */ 
+       char *time;                     /* response time (locationRequest) */
+       int exact;                      /* exact true|false (locationRequest)*/
+} s_held_t, *p_held_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 *);
 
-int lost_get_location_object(p_loc_t, xmlDocPtr, xmlNodePtr);
-int lost_parse_location_info(xmlNodePtr node, p_loc_t loc);
+int lost_parse_location_info(xmlNodePtr, p_loc_t);
+int lost_xpath_location(xmlDocPtr, char *, p_loc_t);
+int lost_parse_geo(xmlNodePtr, p_loc_t);
 
 char *lost_find_service_request(p_loc_t, int *);
-char *lost_held_location_request(char *, int *);
+char *lost_held_location_request(p_held_t, int *);
 char *lost_get_content(xmlNodePtr, const char *, int *);
 char *lost_get_property(xmlNodePtr, const char *, int *);
 char *lost_get_geolocation_header(struct sip_msg *, int *);
@@ -72,5 +109,6 @@ char *lost_get_childname(xmlNodePtr, const char *, int *);
 char *lost_trim_content(char *, int *);
 
 p_loc_t lost_new_loc(str);
+p_held_t lost_new_held(str, str, str, int);
 
 #endif