lost: adds HELD (RFC6155) and LOST (RFC5222) queries for location-based routing
authorwkampich <wolfgang.kampichler@gmail.com>
Mon, 12 Aug 2019 13:04:38 +0000 (15:04 +0200)
committerHenning Westerholt <hw@skalatan.de>
Fri, 16 Aug 2019 06:04:03 +0000 (08:04 +0200)
- lost_held_query() takes specific id (pvar, P-A-I, or From), assembles a
  locationRequest (XML), and sends it to an http_client connection, responses
  are parsed (pidf-lo and location reference are returned)
- lost_query() takes pidf-lo (pvar, msg body, or de-referenced), a service urn
  (pvar, request line) assembles a findService request (XML), and sends it to
  an http_client connection, responses are parsed (target uri and display name
  are returned)

12 files changed:
src/modules/lost/Makefile [new file with mode: 0755]
src/modules/lost/README [new file with mode: 0644]
src/modules/lost/doc/Makefile [new file with mode: 0755]
src/modules/lost/doc/lost.xml [new file with mode: 0644]
src/modules/lost/doc/lost_admin.xml [new file with mode: 0644]
src/modules/lost/functions.c [new file with mode: 0644]
src/modules/lost/functions.h [new file with mode: 0755]
src/modules/lost/lost.c [new file with mode: 0644]
src/modules/lost/pidf.c [new file with mode: 0755]
src/modules/lost/pidf.h [new file with mode: 0755]
src/modules/lost/utilities.c [new file with mode: 0644]
src/modules/lost/utilities.h [new file with mode: 0644]

diff --git a/src/modules/lost/Makefile b/src/modules/lost/Makefile
new file mode 100755 (executable)
index 0000000..aad0522
--- /dev/null
@@ -0,0 +1,23 @@
+#
+# lost module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=lost.so
+
+ifeq ($(CROSS_COMPILE),)
+XML2CFG=$(shell which xml2-config)
+endif
+
+ifneq ($(XML2CFG),)
+       DEFS += $(shell $(XML2CFG) --cflags )
+       LIBS += $(shell $(XML2CFG) --libs)
+else
+       DEFS+=-I$(LOCALBASE)/include/libxml2 -I$(LOCALBASE)/include
+       LIBS+=-L$(LOCALBASE)/lib -lxml2
+endif
+
+include ../../Makefile.modules
diff --git a/src/modules/lost/README b/src/modules/lost/README
new file mode 100644 (file)
index 0000000..9ea55a6
--- /dev/null
@@ -0,0 +1,238 @@
+LOST Module
+
+Wolfgang Kampichler
+
+   Frequentis AG
+   DEC112 (funded by netidee)
+
+Edited by
+
+Wolfgang Kampichler
+
+   Copyright © 20018-2019 Wolfgang Kampichler
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+        4. Functions
+
+              4.1. lost_held_query(con, [id,] pidf-lo, url, error)
+              4.2. lost_query(con, [pidf-lo, urn,] uri, name, error)
+
+        5. Counters
+        6. Remarks
+
+   List of Examples
+
+   1.1. lost_held_query() usage
+   1.2. lost() usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+   4. Functions
+
+        4.1. lost_held_query(con, [id,] pidf-lo, url, error)
+        4.2. lost_query(con, [pidf-lo, urn,] uri, name, error)
+
+   5. Counters
+   6. Remarks
+
+1. Overview
+
+   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.
+
+   This module implements protocol functions that use the http_client api
+   to fetch data from external LOST and HELD servers. The module is using
+   the http_client concept of "connections" to define properties of HTTP
+   sessions. A connection has one or multiple servers and a set of
+   settings that apply to the specific connection.
+
+   The function lost_held_query allows Kamailio to assemble a HELD
+   locationRequest as defined in RFC6155
+   (https://tools.ietf.org/html/rfc6155) to query location information
+   represented as PIDF-LO for a given identity (in most cases a SIP URI).
+   The identity may be a specific parameter or taken from the
+   P-Asserted-Identity or From header. The locationRequest response is
+   parsed and represented as PIDF-LO and location URI to dereference
+   location via HTTP(S).
+
+   The function lost_query allows Kamailio to assemble a LOST findService
+   request as defined in RFC5222 (https://tools.ietf.org/html/rfc5255) to
+   query routing information for a given (geodetic) 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 hop in a Route header.
+
+   The http_client module use the CURL library setting up connections. The
+   CURL library by default use the system configured DNS resolvers, not
+   the Kamailio resolver.
+
+   The module is limited to using HTTP and HTTPS protocols.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * HTTP_CLIENT - the http_client module should be loaded first in
+       order to initialize connections properly.
+     * TLS - if you use TLS connections (https) the tls module should be
+       loaded first in order to initialize OpenSSL properly.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * libcurl
+     * libxml2
+
+3. Parameters
+
+   This module has no specific paramater but uses http_client therfore
+   according parameters may apply.
+
+4. Functions
+
+   4.1. lost_held_query(con, [id,] pidf-lo, url, error)
+   4.2. lost_query(con, [pidf-lo, urn,] uri, name, error)
+
+4.1.  lost_held_query(con, [id,] pidf-lo, url, error)
+
+   Sends a HELD locationRequest to a given connection. The device identity
+   is either specified, or the P-A-I header value, or the From header
+   value.
+     * con - the name of an existing HTTP connection, definied by a
+       httpcon modparam
+     * id - the device id used in the HELD locationRequest
+     * pidf-lo - the PIDF-LO returned in the HELD locationRequest response
+     * url - the location reference returned in the HELD locationRequest
+       response - this reference may be added as Geolocation header value
+       and forwarded downstream
+     * error - any error code returned in the HELD locationRequest
+       response
+
+   The return value is 200 on success, 400 if an internal error occured,
+   or 500 if an error code is returned in the HELD locationRequest
+   response.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, and BRANCH_ROUTE.
+
+   Example 1.1. lost_held_query() usage
+...
+modparam("http_client", "httpcon", "heldsrv=>http://service.org/api/held");
+...
+# HELD location request
+$var(id) = "sip:alice@atlanta";
+$var(res) = lost_held_query("heldsrv", "$var(id)" , "$var(pidf)", "$var(url)", "
+$var(err)");
+xlog("L_INFO", "HELD locationRequest: Result code $var(res)\nUrl: $var(url)\n$va
+r(pidf)");
+...
+$var(res) = lost_held_query("heldsrv", "$var(pidf)", "$var(url)"", "$var(err)");
+xlog("L_INFO", "HELD locationRequest: Result code $var(res)\nUrl: $var(url)\n$va
+r(pidf)\n");
+...
+
+4.2.  lost_query(con, [pidf-lo, urn,] uri, name, error)
+
+   Sends a LOST findService request to a given connection. PIDF-LO and URN
+   are either specified, or, if omitted, parsed from the message body
+   (PIDF-LO) and request line (URN). Either "pidf-lo" or "urn" can be set
+   to an empty string in order to be ignored.
+     * con - the name of an existing HTTP connection definied by a httpcon
+       modparam
+     * pidf-lo - the PIDF-LO used to create the LOST findService request
+     * urn - the URN used to create the LOST findService request
+     * uri - the SIP uri returned in the LOST findServiceResponse
+     * name - the display name returned in the LOST findServiceResponse
+     * error - any error code returned in the LOST findServiceResponse
+
+   The return value is 200 on success, 400 if an internal error occured,
+   or 500 if an error code is returned in the LOST findServiceResponse.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, and BRANCH_ROUTE.
+
+   Example 1.2. lost() usage
+...
+modparam("http_client", "httpcon", "heldsrv=>http://service.org/api/held");
+modparam("http_client", "httpcon", "lostsrv=>http://service.org/api/lost");
+...
+# HELD location request
+$var(id) = "sip:alice@atlanta";
+$var(res) = lost_held_query("heldsrv", "$var(id)" , "$var(pidf)", "$var(url)", "
+$var(err)");
+...
+# 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");
+...
+# 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");
+...
+# 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");
+...
+# 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");
+...
+
+5. Counters
+
+   This module has no specific counters but uses http_client therfore
+   according counters may apply.
+
+6. Remarks
+
+   Note: libcurl leak in CentOS 6 - this module uses libcurl library (via
+   http_client) and in case if you are using CentOS 6, be aware that
+   standard libcurl-7.19.7-52 has a memory leak. To fix this memory,
+   install libcurl from city-fan repository. More details at:
+   https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in
+   -centos6
+
+   Note: http_client_query exported by the http_client API returns the
+   first line of 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.
diff --git a/src/modules/lost/doc/Makefile b/src/modules/lost/doc/Makefile
new file mode 100755 (executable)
index 0000000..084e200
--- /dev/null
@@ -0,0 +1,4 @@
+docs = lost.xml
+
+docbook_dir = ../../../../doc/docbook
+include $(docbook_dir)/Makefile.module
diff --git a/src/modules/lost/doc/lost.xml b/src/modules/lost/doc/lost.xml
new file mode 100644 (file)
index 0000000..effd90d
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general Kamailio documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+       <bookinfo>
+       <title>LOST Module</title>
+       <productname class="trade">&kamailioname;</productname>
+       <authorgroup>
+               <author>
+               <firstname>Wolfgang</firstname>
+               <surname>Kampichler</surname>
+               <affiliation><orgname>Frequentis AG</orgname></affiliation>
+               <affiliation><orgname>DEC112 (funded by netidee)</orgname></affiliation>
+               </author>
+           <editor>
+               <firstname>Wolfgang</firstname>
+               <surname>Kampichler</surname>
+           </editor>
+       </authorgroup>
+       <copyright>
+               <year>20018-2019</year>
+               <holder>Wolfgang Kampichler</holder>
+       </copyright>
+       </bookinfo>
+       <toc></toc>
+       
+       <xi:include href="lost_admin.xml"/>
+
+</book>
diff --git a/src/modules/lost/doc/lost_admin.xml b/src/modules/lost/doc/lost_admin.xml
new file mode 100644 (file)
index 0000000..9acfc3e
--- /dev/null
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+       <title>&adminguide;</title>
+       <section>
+        <title>Overview</title>
+        <para>
+        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. 
+        </para>
+        <para>
+        This module implements protocol functions that use the http_client api
+        to fetch data from external LOST and HELD servers. The module is using
+        the http_client concept of "connections" to define properties of HTTP
+        sessions. A connection has one or multiple servers and a set of settings
+        that apply to the specific connection.
+        </para>
+        <para>
+        The function lost_held_query allows &kamailio; to assemble a HELD
+        locationRequest as defined in RFC6155
+        (<ulink url="https://tools.ietf.org/html/rfc6155"/>) to query location
+        information represented as PIDF-LO for a given identity (in most cases
+        a SIP URI). The identity may be a specific parameter or taken from the
+        P-Asserted-Identity or From header. The locationRequest response is
+        parsed and represented as PIDF-LO and location URI to dereference
+        location via HTTP(S).
+        </para>
+        <para>
+        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
+        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
+        hop in a Route header.
+        </para>
+        <para>
+        The http_client module use the CURL library setting up connections.
+        The CURL library by default use the system configured DNS resolvers,
+        not the Kamailio resolver.
+        </para>
+        <para>
+        The module is limited to using HTTP and HTTPS protocols.
+        </para>
+       </section>
+       <section>
+           <title>Dependencies</title>
+           <section>
+                   <title>&kamailio; Modules</title>
+                   <para>
+                       The following modules must be loaded before this module:
+                <itemizedlist>
+                <listitem><para>
+                HTTP_CLIENT - the http_client module should be
+                loaded first in order to initialize connections properly.
+                </para></listitem>
+                <listitem><para>
+                TLS - if you use TLS connections (https) the tls module should be
+                loaded first in order to initialize &openssl; properly.
+                </para></listitem>
+                           </itemizedlist>
+            </para>
+        </section>
+        <section>
+            <title>External Libraries or Applications</title>
+            <para>
+            The following libraries or applications must be
+            installed before running &kamailio; with this module loaded:
+                <itemizedlist>
+                <listitem>
+                <para>
+                    <emphasis>&libcurl;</emphasis>
+                </para>
+                </listitem>
+                <listitem>
+                <para>
+                    <emphasis>libxml2</emphasis>
+                </para>
+                </listitem>
+                </itemizedlist>
+            </para>
+        </section>
+       </section>
+       <section id="lost.p.general">
+               <title>Parameters</title>
+        <para>
+        This module has no specific paramater but uses <emphasis>http_client</emphasis> therfore according
+        parameters may apply.
+        </para>
+       </section>
+       <section>
+           <title>Functions</title>
+               <section id="lost.f.lost_held_query">
+                       <title>
+                               <function moreinfo="none">lost_held_query(con, [id,] pidf-lo, url, error)</function>
+                       </title>
+                       <para>
+                       Sends a HELD locationRequest to a given connection. The device identity is either specified,
+            or the P-A-I header value, or the From header value.
+               </para>
+                           <itemizedlist>
+                                   <listitem><para>
+                                               <emphasis>con</emphasis> - the name of an existing
+                                               HTTP connection, definied by a httpcon modparam
+                                   </para></listitem>
+                    <listitem><para>
+                                               <emphasis>id</emphasis> - the device id used in the HELD
+                        locationRequest
+                    </para></listitem>
+                    <listitem><para>
+                                               <emphasis>pidf-lo</emphasis> - the PIDF-LO returned in the
+                        HELD locationRequest response
+                    </para></listitem>
+                    <listitem><para>
+                                               <emphasis>url</emphasis> - the location reference returned
+                        in the HELD locationRequest response - this reference may be
+                        added as Geolocation header value and forwarded downstream
+                    </para></listitem>
+                    <listitem><para>
+                                               <emphasis>error</emphasis> - any error code returned in the
+                        HELD locationRequest response
+                    </para></listitem>
+                           </itemizedlist>
+                       <para>
+                       The return value is 200 on success, 400 if an internal error occured, or 500 if an
+            error code is returned in the HELD locationRequest response.
+               </para>
+                       <para>
+                       This function can be used from REQUEST_ROUTE,
+                       ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE.
+                       </para>
+                       <example>
+                               <title><function>lost_held_query()</function> usage</title>
+                               <programlisting format="linespecific">
+...
+modparam("http_client", "httpcon", "heldsrv=>http://service.org/api/held");
+...
+# HELD location request
+$var(id) = "sip:alice@atlanta";
+$var(res) = lost_held_query("heldsrv", "$var(id)" , "$var(pidf)", "$var(url)", "$var(err)");
+xlog("L_INFO", "HELD locationRequest: Result code $var(res)\nUrl: $var(url)\n$var(pidf)");
+...
+$var(res) = lost_held_query("heldsrv", "$var(pidf)", "$var(url)"", "$var(err)");
+xlog("L_INFO", "HELD locationRequest: Result code $var(res)\nUrl: $var(url)\n$var(pidf)\n");
+...
+                               </programlisting>
+                       </example>
+               </section>
+               <section id="lost.f.lost_query">
+                       <title>
+                               <function moreinfo="none">lost_query(con, [pidf-lo, urn,] uri, name, error)</function>
+                       </title>
+                       <para>
+                       Sends a LOST findService request to a given connection. PIDF-LO and URN are either specified,
+            or, if omitted, parsed from the message body (PIDF-LO) and request line (URN). Either "pidf-lo"
+            or "urn" can be set to an empty string in order to be ignored. 
+               </para>
+                       <itemizedlist>
+                <listitem><para>
+                    <emphasis>con</emphasis> - the name of an existing
+                    HTTP connection definied by a httpcon modparam
+                </para></listitem>
+                <listitem><para>
+                    <emphasis>pidf-lo</emphasis> - the PIDF-LO used to create the
+                    LOST findService request
+                </para></listitem>
+                <listitem><para>
+                    <emphasis>urn</emphasis> - the URN used to create the
+                    LOST findService request
+                </para></listitem>
+                <listitem><para>
+                    <emphasis>uri</emphasis> - the SIP uri returned in the
+                    LOST findServiceResponse
+                </para></listitem>
+                <listitem><para>
+                    <emphasis>name</emphasis> - the display name returned in the
+                    LOST findServiceResponse
+                </para></listitem>
+                <listitem><para>
+                    <emphasis>error</emphasis> - any error code returned in the
+                    LOST findServiceResponse
+                </para></listitem>
+                       </itemizedlist>
+                       <para>
+                       The return value is 200 on success, 400 if an internal error occured, or 500 if an
+            error code is returned in the LOST findServiceResponse.
+               </para>
+                       <para>
+                       This function can be used from REQUEST_ROUTE,
+                       ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE.
+                       </para>
+                       <example>
+                               <title><function>lost()</function> usage</title>
+                               <programlisting format="linespecific">
+...
+modparam("http_client", "httpcon", "heldsrv=>http://service.org/api/held");
+modparam("http_client", "httpcon", "lostsrv=>http://service.org/api/lost");
+...
+# HELD location request
+$var(id) = "sip:alice@atlanta";
+$var(res) = lost_held_query("heldsrv", "$var(id)" , "$var(pidf)", "$var(url)", "$var(err)");
+...
+# 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");
+...
+# 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");
+...
+# 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");
+...
+# 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");
+...
+                               </programlisting>
+                       </example>
+               </section>
+
+    </section>
+    <section id="lost.s.counters">
+           <title>Counters</title>
+        <para>
+            This module has no specific counters but uses http_client therfore
+            according counters may apply.
+        </para>
+       </section>
+
+    <section id="lost.s.remarks">
+        <title>Remarks</title>
+        <para>
+            Note: libcurl leak in CentOS 6 - this module uses libcurl library
+            (via http_client) and in case if you are using CentOS 6, be aware that
+            standard libcurl-7.19.7-52 has a memory leak. To fix this memory, install
+            libcurl from city-fan repository. More details at:
+            <ulink url="https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6">
+            https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6</ulink>
+        </para>
+        <para>
+            Note: http_client_query exported by the http_client API returns the first line of
+            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.
+        </para>
+    </section>
+</chapter>
diff --git a/src/modules/lost/functions.c b/src/modules/lost/functions.c
new file mode 100644 (file)
index 0000000..b7380dd
--- /dev/null
@@ -0,0 +1,551 @@
+/*\r
+ * lost module functions\r
+ *\r
+ * Copyright (C) 2019 Wolfgang Kampichler\r
+ * DEC112, FREQUENTIS AG\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ */\r
+\r
+/*!\r
+ * \file\r
+ * \brief Kamailio lost :: functions\r
+ * \ingroup lost\r
+ * Module: \ref lost\r
+ */\r
+/*****************/\r
+\r
+#include "../../modules/http_client/curl_api.h"\r
+\r
+#include "../../core/mod_fix.h"\r
+#include "../../core/pvar.h"\r
+#include "../../core/route_struct.h"\r
+#include "../../core/ut.h"\r
+#include "../../core/trim.h"\r
+#include "../../core/mem/mem.h"\r
+#include "../../core/parser/msg_parser.h"\r
+#include "../../core/parser/parse_body.h"\r
+#include "../../core/lvalue.h"\r
+\r
+#include "pidf.h"\r
+#include "utilities.h"\r
+\r
+#define LOST_SUCCESS 200\r
+#define LOST_CLIENT_ERROR 400\r
+#define LOST_SERVER_ERROR 500\r
+\r
+extern httpc_api_t httpapi;\r
+\r
+char mtheld[] = "application/held+xml;charset=utf-8";\r
+char mtlost[] = "application/lost+xml;charset=utf-8";\r
+\r
+char uri_element[] = "uri";\r
+char name_element[] = "displayName";\r
+char errors_element[] = "errors";\r
+\r
+/*\r
+ * lost_function_held(msg, con, pidf, url, err, id)\r
+ * assembles and runs HELD locationRequest, parses results\r
+ */\r
+int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,\r
+               char *_err, char *_id)\r
+{\r
+       pv_spec_t *pspidf;\r
+       pv_spec_t *psurl;\r
+       pv_spec_t *pserr;\r
+\r
+       pv_value_t pvpidf;\r
+       pv_value_t pvurl;\r
+       pv_value_t pverr;\r
+\r
+       xmlDocPtr doc = NULL;\r
+       xmlNodePtr root = NULL;\r
+\r
+       str did = {NULL, 0};\r
+       str que = {NULL, 0};\r
+       str con = {NULL, 0};\r
+       str geo = {NULL, 0};\r
+       str err = {NULL, 0};\r
+       str res = {NULL, 0};\r
+       str idhdr = {NULL, 0};\r
+\r
+       if(_con == NULL || _pidf == NULL || _url == NULL || _err == NULL) {\r
+               LM_ERR("invalid parameter\n");\r
+               goto err;\r
+       }\r
+       /* connection from parameter */\r
+       if(fixup_get_svalue(_m, (gparam_p)_con, &con) != 0) {\r
+               LM_ERR("cannot get connection string\n");\r
+               goto err;\r
+       }\r
+       /* id from parameter */\r
+       if(_id) {\r
+               if(fixup_get_svalue(_m, (gparam_p)_id, &did) != 0) {\r
+                       LM_ERR("cannot get device id\n");\r
+                       goto err;\r
+               }\r
+               if(!did.s) {\r
+                       LM_ERR("device id not found\n");\r
+                       goto err;\r
+               }\r
+       } else {\r
+               /* id from P-A-I header */\r
+               idhdr.s = lost_get_pai_header(_m, &idhdr.len);\r
+               if(idhdr.len == 0) {\r
+                       LM_WARN("P-A-I header not found, trying From header ...\n");\r
+                       /* id from From header */\r
+                       idhdr.s = lost_get_from_header(_m, &idhdr.len);\r
+                       if(idhdr.len == 0) {\r
+                               LM_ERR("device id not found\n");\r
+                               goto err;\r
+                       }\r
+               }\r
+               did.s = idhdr.s;\r
+               did.len = idhdr.len;\r
+       }\r
+       LM_INFO("id - [%.*s]\n", did.len, did.s);\r
+\r
+       /* check if connection exists */\r
+       if(httpapi.http_connection_exists(&con) == 0) {\r
+               LM_ERR("connection: [%s] does not exist\n", con.s);\r
+               goto err;\r
+       }\r
+\r
+       /* assemble locationRequest */\r
+       que.s = lost_held_location_request(did.s, &que.len);\r
+       /* free memory */\r
+       if(idhdr.s) {\r
+               pkg_free(idhdr.s);\r
+               idhdr.len = 0;\r
+               did.s = NULL;\r
+               did.len = 0;\r
+       }\r
+       if(!que.s) {\r
+               LM_ERR("held request document error\n");\r
+               goto err;\r
+       }\r
+\r
+       LM_DBG("held location request: \n[%s]\n", que.s);\r
+\r
+       /* send locationRequest to location server - HTTP POST */\r
+       httpapi.http_connect(_m, &con, NULL, &res, mtheld, &que);\r
+       /* free memory */\r
+       pkg_free(que.s);\r
+       que.len = 0;\r
+       /* read and parse the returned xml */\r
+       doc = xmlReadMemory(res.s, res.len, 0, NULL,\r
+                       XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_NONET\r
+                                       | XML_PARSE_NOCDATA);\r
+       if(!doc) {\r
+               LM_WARN("invalid xml document: \n[%.*s]\n", res.len, res.s);\r
+               doc = xmlRecoverMemory(res.s, res.len);\r
+               if(!doc) {\r
+                       LM_ERR("xml document recovery failed on: \n[%.*s]\n", res.len,\r
+                                       res.s);\r
+                       goto err;\r
+               }\r
+               LM_DBG("xml document recovered\n");\r
+       }\r
+       root = xmlDocGetRootElement(doc);\r
+       if(!root) {\r
+               LM_ERR("empty xml document\n");\r
+               goto err;\r
+       }\r
+       /* check the root element, shall be locationResponse, or errors */\r
+       if(!xmlStrcmp(root->name, (const xmlChar *)"locationResponse")) {\r
+\r
+               LM_DBG("HELD location response: \n[%.*s]\n", res.len, res.s);\r
+\r
+               /* get the locationUri element */\r
+               geo.s = lost_get_content(root, (char *)"locationURI", &geo.len);\r
+               if(!geo.s) {\r
+                       LM_ERR("no locationURI element found\n");\r
+                       goto err;\r
+               }\r
+       } else if(!xmlStrcmp(root->name, (const xmlChar *)"error")) {\r
+\r
+               LM_DBG("HELD error response: \n[%.*s]\n", res.len, res.s);\r
+\r
+               /* get the error patterm */\r
+               err.s = lost_get_property(root, (char *)"code", &err.len);\r
+               if(!err.s) {\r
+                       LM_ERR("error - code property not found: \n[%.*s]\n", res.len,\r
+                                       res.s);\r
+                       goto err;\r
+               }\r
+               LM_WARN("locationRequest error response: [%.*s]\n", err.len, err.s);\r
+       } else {\r
+               LM_ERR("root element is not valid: \n[%.*s]\n", res.len, res.s);\r
+               goto err;\r
+       }\r
+       xmlFreeDoc(doc);\r
+\r
+       /* set writeable pvars */\r
+       pvpidf.rs = res;\r
+       pvpidf.rs.s = res.s;\r
+       pvpidf.rs.len = res.len;\r
+\r
+       pvpidf.flags = PV_VAL_STR;\r
+       pspidf = (pv_spec_t *)_pidf;\r
+       pspidf->setf(_m, &pspidf->pvp, (int)EQ_T, &pvpidf);\r
+\r
+       pvurl.rs = geo;\r
+       pvurl.rs.s = geo.s;\r
+       pvurl.rs.len = geo.len;\r
+\r
+       pvurl.flags = PV_VAL_STR;\r
+       psurl = (pv_spec_t *)_url;\r
+       psurl->setf(_m, &psurl->pvp, (int)EQ_T, &pvurl);\r
+\r
+       pverr.rs = err;\r
+       pverr.rs.s = err.s;\r
+       pverr.rs.len = err.len;\r
+\r
+       pverr.flags = PV_VAL_STR;\r
+       pserr = (pv_spec_t *)_err;\r
+       pserr->setf(_m, &pserr->pvp, (int)EQ_T, &pverr);\r
+\r
+       return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;\r
+\r
+err:\r
+       if(doc)\r
+               xmlFreeDoc(doc);\r
+       return LOST_CLIENT_ERROR;\r
+}\r
+\r
+/*\r
+ * lost_function(msg, con, pidf, uri, name, err, pidf, urn)\r
+ * assembles and runs LOST findService request, parses results\r
+ */\r
+int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,\r
+               char *_err, char *_pidf, char *_urn)\r
+{\r
+       pv_spec_t *psname;\r
+       pv_spec_t *psuri;\r
+       pv_spec_t *pserr;\r
+\r
+       pv_value_t pvname;\r
+       pv_value_t pvuri;\r
+       pv_value_t pverr;\r
+\r
+       p_loc_t loc = NULL;\r
+\r
+       xmlDocPtr doc = NULL;\r
+       xmlNodePtr root = NULL;\r
+\r
+       str uri = {NULL, 0};\r
+       str urn = {NULL, 0};\r
+       str err = {NULL, 0};\r
+       str res = {NULL, 0};\r
+       str con = {NULL, 0};\r
+       str ret = {NULL, 0};\r
+       str geo = {NULL, 0};\r
+       str name = {NULL, 0};\r
+       str pidf = {NULL, 0};\r
+       str pidf_h = {NULL, 0};\r
+\r
+       char *search = NULL;\r
+       struct msg_start *fl;\r
+\r
+       if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) {\r
+               LM_ERR("invalid parameter\n");\r
+               goto err;\r
+       }\r
+       if(fixup_get_svalue(_m, (gparam_p)_con, &con) != 0) {\r
+               LM_ERR("cannot get connection string\n");\r
+               goto err;\r
+       }\r
+       /* urn from parameter */\r
+       if(_urn) {\r
+               if(fixup_get_svalue(_m, (gparam_p)_urn, &urn) != 0) {\r
+                       LM_ERR("cannot get service urn\n");\r
+                       goto err;\r
+               }\r
+       }\r
+       /* urn from request line */\r
+       if(urn.len == 0) {\r
+               LM_WARN("no sevice urn parameter, trying request line ...\n");\r
+               fl = &(_m->first_line);\r
+               urn.len = fl->u.request.uri.len;\r
+               urn.s = fl->u.request.uri.s;\r
+       }\r
+       /* check urn scheme */\r
+       if(urn.len > 3) {\r
+               search = urn.s;\r
+               if(((*(search + 0) == 'u') || (*(search + 0) == 'U'))\r
+                               && ((*(search + 1) == 'r') || (*(search + 1) == 'R'))\r
+                               && ((*(search + 2) == 'n') || (*(search + 2) == 'N'))\r
+                               && (*(search + 3) == ':')) {\r
+                       LM_INFO("urn - [%.*s]\n", urn.len, urn.s);\r
+               } else {\r
+                       LM_ERR("service urn not found\n");\r
+                       goto err;\r
+               }\r
+       } else {\r
+               LM_ERR("service urn not found\n");\r
+               goto err;\r
+       }\r
+       /* pidf from parameter */\r
+       if(_pidf) {\r
+               if(fixup_get_svalue(_m, (gparam_p)_pidf, &pidf) != 0) {\r
+                       LM_ERR("cannot get pidf-lo\n");\r
+                       goto err;\r
+               }\r
+       }\r
+       /* pidf from geolocation header */\r
+       if(pidf.len == 0) {\r
+               LM_WARN("no pidf parameter, trying Geolocation header ...\n");\r
+               geo.s = lost_get_geolocation_header(_m, &geo.len);\r
+               if(!geo.s) {\r
+                       LM_ERR("geolocation header not found\n");\r
+                       goto err;\r
+               } else {\r
+                       /* pidf from multipart body, check cid scheme */\r
+                       search = geo.s;\r
+                       if((*(search + 0) == '<')\r
+                                       && ((*(search + 1) == 'c') || (*(search + 1) == 'C'))\r
+                                       && ((*(search + 2) == 'i') || (*(search + 2) == 'I'))\r
+                                       && ((*(search + 3) == 'd') || (*(search + 3) == 'D'))\r
+                                       && (*(search + 4) == ':')) {\r
+                               search += 4;\r
+                               *search = '<';\r
+                               geo.s = search;\r
+                               geo.len = geo.len - 4;\r
+\r
+                               LM_DBG("cid: \n[%.*s]\n", geo.len, geo.s);\r
+\r
+                               /* get body part - filter=>content id */\r
+                               pidf.s = get_body_part_by_filter(\r
+                                               _m, 0, 0, geo.s, NULL, &pidf.len);\r
+                               if(!pidf.s) {\r
+                                       LM_ERR("no multipart body found\n");\r
+                                       goto err;\r
+                               }\r
+                       }\r
+                       /* no pidf-lo so far ... check http(s) scheme */\r
+                       if(((*(search + 0) == 'h') || (*(search + 0) == 'H'))\r
+                                       && ((*(search + 1) == 't') || (*(search + 1) == 'T'))\r
+                                       && ((*(search + 2) == 't') || (*(search + 2) == 'T'))\r
+                                       && ((*(search + 3) == 'p') || (*(search + 3) == 'P'))\r
+                                       && (*(search + 4) == ':')) {\r
+\r
+                               LM_DBG("url: \n[%.*s]\n", geo.len, geo.s);\r
+\r
+                               /* ! dereference pidf.lo at location server - HTTP GET */\r
+                               /* ! requires hack in http_client module */\r
+                               /* ! functions.c => http_client_query => query_params.oneline = 0; */\r
+                               httpapi.http_client_query(_m, geo.s, &pidf_h, NULL, NULL);\r
+\r
+                               /* free memory */\r
+                               pkg_free(geo.s);\r
+                               geo.len = 0;\r
+\r
+                               if(!pidf_h.s) {\r
+                                       LM_ERR("dereferencing location failed\n");\r
+                                       goto err;\r
+                               }\r
+                               pidf.s = pidf_h.s;\r
+                               pidf.len = pidf_h.len;\r
+                       }\r
+               }\r
+       }\r
+\r
+       /* no pidf-lo return error */\r
+       if(!pidf.s) {\r
+               LM_ERR("pidf-lo not found\n");\r
+               goto err;\r
+       }\r
+\r
+       LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s);\r
+\r
+       /* read and parse pidf-lo */\r
+       doc = xmlReadMemory(pidf.s, pidf.len, 0, NULL,\r
+                       XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_NONET\r
+                                       | XML_PARSE_NOCDATA);\r
+\r
+       if(!doc) {\r
+               LM_WARN("invalid xml (pidf-lo): \n[%.*s]\n", pidf.len, pidf.s);\r
+               doc = xmlRecoverMemory(pidf.s, pidf.len);\r
+               if(!doc) {\r
+                       LM_ERR("xml (pidf-lo) recovery failed on: \n[%.*s]\n", pidf.len,\r
+                                       pidf.s);\r
+                       goto err;\r
+               }\r
+\r
+               LM_DBG("xml (pidf-lo) recovered\n");\r
+       }\r
+\r
+       root = xmlDocGetRootElement(doc);\r
+       if(!root) {\r
+               LM_ERR("empty pidf-lo document\n");\r
+               goto err;\r
+       }\r
+       if((!xmlStrcmp(root->name, (const xmlChar *)"presence"))\r
+                       || (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) {\r
+               /* get the geolocation: point or circle, urn, ... */\r
+               loc = lost_new_loc(urn);\r
+               if(lost_parse_location_info(root, loc) < 0) {\r
+                       LM_ERR("location element not found\n");\r
+                       goto err;\r
+               }\r
+       } else {\r
+               LM_ERR("findServiceResponse or presence element not found in "\r
+                          "\n[%.*s]\n",\r
+                               pidf.len, pidf.s);\r
+               goto err;\r
+       }\r
+\r
+       /* free memory */\r
+       if(pidf_h.s) {\r
+               pkg_free(pidf_h.s);\r
+               pidf_h.len = 0;\r
+               pidf.s = NULL;\r
+               pidf.len = 0;\r
+       }\r
+\r
+       /* check if connection exits */\r
+       if(httpapi.http_connection_exists(&con) == 0) {\r
+               LM_ERR("connection: [%.*s] does not exist\n", con.len, con.s);\r
+               goto err;\r
+       }\r
+       /* assemble findService request */\r
+       res.s = lost_find_service_request(loc, &res.len);\r
+       /* free memory */\r
+       lost_free_loc(loc);\r
+       xmlFreeDoc(doc);\r
+\r
+       if(!res.s) {\r
+               LM_ERR("lost request failed\n");\r
+               goto err;\r
+       }\r
+\r
+       LM_DBG("findService request: \n[%.*s]\n", res.len, res.s);\r
+\r
+       /* send findService request to mapping server - HTTP POST */\r
+       httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &res);\r
+       pkg_free(res.s);\r
+       res.len = 0;\r
+       if(!ret.s) {\r
+               LM_ERR("findService request failed\n");\r
+               goto err;\r
+       }\r
+\r
+       LM_DBG("findService response: \n[%.*s]\n", ret.len, ret.s);\r
+\r
+       /* read and parse the returned xml */\r
+       doc = xmlReadMemory(ret.s, ret.len, 0, 0,\r
+                       XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA);\r
+\r
+       if(!doc) {\r
+               LM_ERR("invalid xml document: \n[%.*s]\n", ret.len, ret.s);\r
+               doc = xmlRecoverMemory(ret.s, ret.len);\r
+               if(!doc) {\r
+                       LM_ERR("xml document recovery failed on: \n[%.*s]\n", ret.len,\r
+                                       ret.s);\r
+                       goto err;\r
+               }\r
+\r
+               LM_DBG("xml document recovered\n");\r
+       }\r
+       root = xmlDocGetRootElement(doc);\r
+       if(!root) {\r
+               LM_ERR("empty xml document: \n[%.*s]\n", ret.len, ret.s);\r
+               goto err;\r
+       }\r
+       /* check the root element, shall be findServiceResponse, or errors */\r
+       if((!xmlStrcmp(root->name, (const xmlChar *)"findServiceResponse"))) {\r
+               /* get the uri element */\r
+               uri.s = lost_get_content(root, uri_element, &uri.len);\r
+               if(!uri.s) {\r
+                       LM_ERR("uri element not found: \n[%.*s]\n", ret.len, ret.s);\r
+                       goto err;\r
+               }\r
+               LM_INFO("uri - [%.*s]\n", uri.len, uri.s);\r
+               /* get the displayName element */\r
+               name.s = lost_get_content(root, name_element, &name.len);\r
+               if(!name.s) {\r
+                       LM_ERR("displayName element not found: \n[%.*s]\n", ret.len, ret.s);\r
+                       goto err;\r
+               }\r
+               LM_INFO("name - [%.*s]\n", name.len, name.s);\r
+       } else if((!xmlStrcmp(root->name, (const xmlChar *)"errors"))) {\r
+\r
+               LM_DBG("findService error response received\n");\r
+\r
+               /* get the error patterm */\r
+               err.s = lost_get_childname(root, errors_element, &err.len);\r
+               if(!err.s) {\r
+                       LM_ERR("error pattern element not found: \n[%.*s]\n", ret.len,\r
+                                       ret.s);\r
+                       goto err;\r
+               }\r
+               LM_WARN("findService error response: [%.*s]\n", err.len, err.s);\r
+       } else {\r
+               LM_ERR("root element is not valid: \n[%.*s]\n", ret.len, ret.s);\r
+               goto err;\r
+       }\r
+\r
+       /* free memory */\r
+       xmlFreeDoc(doc);\r
+       if(ret.s) {\r
+               pkg_free(ret.s);\r
+               ret.len = 0;\r
+       }\r
+\r
+       /* set writeable pvars */\r
+       pvname.rs = name;\r
+       pvname.rs.s = name.s;\r
+       pvname.rs.len = name.len;\r
+\r
+       pvname.flags = PV_VAL_STR;\r
+       psname = (pv_spec_t *)_name;\r
+       psname->setf(_m, &psname->pvp, (int)EQ_T, &pvname);\r
+\r
+       pvuri.rs = uri;\r
+       pvuri.rs.s = uri.s;\r
+       pvuri.rs.len = uri.len;\r
+\r
+       pvuri.flags = PV_VAL_STR;\r
+       psuri = (pv_spec_t *)_uri;\r
+       psuri->setf(_m, &psuri->pvp, (int)EQ_T, &pvuri);\r
+\r
+       pverr.rs = err;\r
+       pverr.rs.s = err.s;\r
+       pverr.rs.len = err.len;\r
+\r
+       pverr.flags = PV_VAL_STR;\r
+       pserr = (pv_spec_t *)_err;\r
+       pserr->setf(_m, &pserr->pvp, (int)EQ_T, &pverr);\r
+\r
+       return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;\r
+\r
+err:\r
+       if(loc)\r
+               lost_free_loc(loc);\r
+       if(doc)\r
+               xmlFreeDoc(doc);\r
+       if(pidf_h.s) {\r
+               pkg_free(pidf_h.s);\r
+               pidf_h.len = 0;\r
+       }\r
+       if(ret.s) {\r
+               pkg_free(ret.s);\r
+               ret.len = 0;\r
+       }\r
+       return LOST_CLIENT_ERROR;\r
+}\r
diff --git a/src/modules/lost/functions.h b/src/modules/lost/functions.h
new file mode 100755 (executable)
index 0000000..52b8b1b
--- /dev/null
@@ -0,0 +1,40 @@
+/*\r
+ * lost module functions\r
+ *\r
+ * Copyright (C) 2019 Wolfgang Kampichler\r
+ * DEC112, FREQUENTIS AG\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ */\r
+\r
+/*!\r
+ * \file\r
+ * \brief Kamailio lost :: functions\r
+ * \ingroup lost\r
+ * Module: \ref lost\r
+ */\r
+\r
+#ifndef LOST_FUNCTIONS_H\r
+#define LOST_FUNCTIONS_H\r
+\r
+int lost_function_held(\r
+               struct sip_msg *, char *, char *, char *, char *, char *);\r
+int lost_function(\r
+               struct sip_msg *, char *, char *, char *, char *, char *, char *);\r
+\r
+#endif\r
diff --git a/src/modules/lost/lost.c b/src/modules/lost/lost.c
new file mode 100644 (file)
index 0000000..c4b5e66
--- /dev/null
@@ -0,0 +1,343 @@
+/*\r
+ * lost module\r
+ *\r
+ * Copyright (C) 2019 Wolfgang Kampichler\r
+ * DEC112, FREQUENTIS AG\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ */\r
+\r
+/*!\r
+ * \file\r
+ * \brief Kamailio lost ::\r
+ * \ingroup lost\r
+ * Module: \ref lost\r
+ */\r
+\r
+#include "../../modules/http_client/curl_api.h"\r
+\r
+#include "../../core/mod_fix.h"\r
+#include "../../core/sr_module.h"\r
+#include "../../core/ut.h"\r
+#include "../../core/locking.h"\r
+\r
+#include "../../core/pvar.h"\r
+#include "../../core/mem/mem.h"\r
+#include "../../core/dprint.h"\r
+\r
+#include "../../core/script_cb.h"\r
+\r
+#include "functions.h"\r
+\r
+MODULE_VERSION\r
+\r
+/* Module parameter variables */\r
+httpc_api_t httpapi;\r
+\r
+/* Module management function prototypes */\r
+static int mod_init(void);\r
+static int child_init(int);\r
+static void destroy(void);\r
+\r
+/* Fixup functions to be defined later */\r
+static int fixup_lost_held_query(void **param, int param_no);\r
+static int fixup_free_lost_held_query(void **param, int param_no);\r
+static int fixup_lost_held_query_id(void **param, int param_no);\r
+static int fixup_free_lost_held_query_id(void **param, int param_no);\r
+\r
+static int fixup_lost_query(void **param, int param_no);\r
+static int fixup_free_lost_query(void **param, int param_no);\r
+static int fixup_lost_query_all(void **param, int param_no);\r
+static int fixup_free_lost_query_all(void **param, int param_no);\r
+\r
+/* Wrappers for http_query to be defined later */\r
+static int w_lost_held_query(\r
+               struct sip_msg *_m, char *_con, char *_pidf, char *_url, char *_err);\r
+static int w_lost_held_query_id(struct sip_msg *_m, char *_con, char *_id,\r
+               char *_pidf, char *_url, char *_err);\r
+static int w_lost_query(\r
+               struct sip_msg *_m, char *_con, char *_uri, char *_name, char *_err);\r
+static int w_lost_query_all(struct sip_msg *_m, char *_con, char *_pidf,\r
+               char *_urn, char *_uri, char *_name, char *_err);\r
+\r
+/* Exported functions */\r
+static cmd_export_t cmds[] = {\r
+               {"lost_held_query", (cmd_function)w_lost_held_query, 4,\r
+                               fixup_lost_held_query, fixup_free_lost_held_query,\r
+                               REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},\r
+               {"lost_held_query", (cmd_function)w_lost_held_query_id, 5,\r
+                               fixup_lost_held_query_id, fixup_free_lost_held_query_id,\r
+                               REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},\r
+               {"lost_query", (cmd_function)w_lost_query, 4, fixup_lost_query,\r
+                               fixup_free_lost_query,\r
+                               REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},\r
+               {"lost_query", (cmd_function)w_lost_query_all, 6, fixup_lost_query_all,\r
+                               fixup_free_lost_query_all,\r
+                               REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},\r
+               {0, 0, 0, 0, 0, 0}};\r
+\r
+\r
+/* Module interface */\r
+struct module_exports exports = {\r
+               "lost",                  /* module name*/\r
+               DEFAULT_DLFLAGS, /* dlopen flags */\r
+               cmds,                    /* exported functions */\r
+               0,                               /* exported parameters */\r
+               0,                               /* RPC method exports */\r
+               0,                               /* exported pseudo-variables */\r
+               0,                               /* response handling function */\r
+               mod_init,                /* module initialization function */\r
+               child_init,              /* per-child init function */\r
+               destroy                  /* module destroy function */\r
+};\r
+\r
+/* Module initialization function */\r
+static int mod_init(void)\r
+{\r
+       LM_DBG("init lost module\n");\r
+\r
+       if(httpc_load_api(&httpapi) != 0) {\r
+               LM_ERR("Can not bind to http_client API \n");\r
+               return -1;\r
+       }\r
+\r
+       LM_DBG("**** init lost module done.\n");\r
+\r
+       return 0;\r
+}\r
+\r
+/* Child initialization function */\r
+static int child_init(int rank)\r
+{\r
+       if(rank == PROC_INIT || rank == PROC_MAIN || rank == PROC_TCP_MAIN) {\r
+               return 0; /* do nothing for the main process */\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+static void destroy(void)\r
+{\r
+       ;\r
+       /* do nothing */\r
+}\r
+\r
+/*\r
+ * Fix 4 lost_held_query params: con (string/pvar)\r
+ * and pidf, url, err (writable pvar).\r
+ */\r
+static int fixup_lost_held_query(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_spve_null(param, 1);\r
+       }\r
+       if((param_no == 2) || (param_no == 3) || (param_no == 4)) {\r
+               if(fixup_pvar_null(param, 1) != 0) {\r
+                       LM_ERR("failed to fixup result pvar\n");\r
+                       return -1;\r
+               }\r
+               if(((pv_spec_t *)(*param))->setf == NULL) {\r
+                       LM_ERR("result pvar is not writeble\n");\r
+                       return -1;\r
+               }\r
+               return 0;\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Free lost_held_query params.\r
+ */\r
+static int fixup_free_lost_held_query(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               /* char strings don't need freeing */\r
+               return 0;\r
+       }\r
+       if((param_no == 2) || (param_no == 3) || (param_no == 4)) {\r
+               return fixup_free_pvar_null(param, 1);\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Fix 5 lost_held_query_id params: con (string/pvar) id (string that may contain\r
+ * pvars) and pidf, url, err (writable pvar).\r
+ */\r
+static int fixup_lost_held_query_id(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_spve_null(param, 1);\r
+       }\r
+       if(param_no == 2) {\r
+               return fixup_spve_null(param, 1);\r
+       }\r
+       if((param_no == 3) || (param_no == 4) || (param_no == 5)) {\r
+               if(fixup_pvar_null(param, 1) != 0) {\r
+                       LM_ERR("failed to fixup result pvar\n");\r
+                       return -1;\r
+               }\r
+               if(((pv_spec_t *)(*param))->setf == NULL) {\r
+                       LM_ERR("result pvar is not writeble\n");\r
+                       return -1;\r
+               }\r
+               return 0;\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Free lost_held_query_id params.\r
+ */\r
+static int fixup_free_lost_held_query_id(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_free_spve_null(param, 1);\r
+       }\r
+       if(param_no == 2) {\r
+               return fixup_free_spve_null(param, 1);\r
+       }\r
+       if((param_no == 3) || (param_no == 4) || (param_no == 5)) {\r
+               return fixup_free_pvar_null(param, 1);\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Fix 4 lost_query params: con (string/pvar)\r
+ * and uri, name, err (writable pvar).\r
+ */\r
+static int fixup_lost_query(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_spve_null(param, 1);\r
+       }\r
+       if((param_no == 2) || (param_no == 3) || (param_no == 4)) {\r
+               if(fixup_pvar_null(param, 1) != 0) {\r
+                       LM_ERR("failed to fixup result pvar\n");\r
+                       return -1;\r
+               }\r
+               if(((pv_spec_t *)(*param))->setf == NULL) {\r
+                       LM_ERR("result pvar is not writeble\n");\r
+                       return -1;\r
+               }\r
+               return 0;\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Free lost_held_query_id params.\r
+ */\r
+static int fixup_free_lost_query(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_free_spve_null(param, 1);\r
+       }\r
+       if((param_no == 2) || (param_no == 3) || (param_no == 4)) {\r
+               return fixup_free_pvar_null(param, 1);\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Fix 6 lost_query params: con (string/pvar) pidf, urn (string that may contain\r
+ * pvars) and uri, name, err (writable pvar).\r
+ */\r
+static int fixup_lost_query_all(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_spve_null(param, 1);\r
+       }\r
+       if((param_no == 2) || (param_no == 3)) {\r
+               return fixup_spve_null(param, 1);\r
+       }\r
+       if((param_no == 4) || (param_no == 5) || (param_no == 6)) {\r
+               if(fixup_pvar_null(param, 1) != 0) {\r
+                       LM_ERR("failed to fixup result pvar\n");\r
+                       return -1;\r
+               }\r
+               if(((pv_spec_t *)(*param))->setf == NULL) {\r
+                       LM_ERR("result pvar is not writeble\n");\r
+                       return -1;\r
+               }\r
+               return 0;\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Free lost_held_query_id params.\r
+ */\r
+static int fixup_free_lost_query_all(void **param, int param_no)\r
+{\r
+       if(param_no == 1) {\r
+               return fixup_free_spve_null(param, 1);\r
+       }\r
+       if((param_no == 2) || (param_no == 3)) {\r
+               return fixup_free_spve_null(param, 1);\r
+       }\r
+       if((param_no == 4) || (param_no == 5) || (param_no == 6)) {\r
+               return fixup_free_pvar_null(param, 1);\r
+       }\r
+       LM_ERR("invalid parameter number <%d>\n", param_no);\r
+       return -1;\r
+}\r
+\r
+/*\r
+ * Wrapper for lost_held_query w/o id\r
+ */\r
+static int w_lost_held_query(\r
+               struct sip_msg *_m, char *_con, char *_pidf, char *_url, char *_err)\r
+{\r
+       return lost_function_held(_m, _con, _pidf, _url, _err, NULL);\r
+}\r
+\r
+/*\r
+ * Wrapper for lost_held_query with id\r
+ */\r
+static int w_lost_held_query_id(struct sip_msg *_m, char *_con, char *_id,\r
+               char *_pidf, char *_url, char *_err)\r
+{\r
+       return lost_function_held(_m, _con, _pidf, _url, _err, _id);\r
+}\r
+\r
+/*\r
+ * Wrapper for lost_query w/o pudf, urn\r
+ */\r
+static int w_lost_query(\r
+               struct sip_msg *_m, char *_con, char *_uri, char *_name, char *_err)\r
+{\r
+       return lost_function(_m, _con, _uri, _name, _err, NULL, NULL);\r
+}\r
+\r
+/*\r
+ * Wrapper for lost_query with pidf, urn\r
+ */\r
+static int w_lost_query_all(struct sip_msg *_m, char *_con, char *_pidf,\r
+               char *_urn, char *_uri, char *_name, char *_err)\r
+{\r
+       return lost_function(_m, _con, _uri, _name, _err, _pidf, _urn);\r
+}\r
diff --git a/src/modules/lost/pidf.c b/src/modules/lost/pidf.c
new file mode 100755 (executable)
index 0000000..e418583
--- /dev/null
@@ -0,0 +1,143 @@
+/*\r
+ * $Id: pidf.c 1953 2007-04-04 08:50:33Z anca_vamanu $\r
+ *\r
+ * presence module - presence server implementation\r
+ *\r
+ * Copyright (C) 2006 Voice Sistem S.R.L.\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ * History:\r
+ * --------\r
+ *  2007-04-14  initial version (anca)\r
+ */\r
+\r
+/*! \file\r
+ * \brief Kamailio lost ::  PIDF handling\r
+ * \ingroup lost\r
+ */\r
+\r
+\r
+/**\r
+ * make strptime available\r
+ * use 600 for 'Single UNIX Specification, Version 3'\r
+ * _XOPEN_SOURCE creates conflict in header definitions in Solaris\r
+ */\r
+#ifndef __OS_solaris\r
+#define _XOPEN_SOURCE 600 /* glibc2 on linux, bsd */\r
+#define _BSD_SOURCE \\r
+       1                                         /* needed on linux to "fix" the effect\r
+                                                                                 of the above define on\r
+                                                                                 features.h/unistd.h syscall() */\r
+#define _DEFAULT_SOURCE 1 /* _BSD_SOURCE is deprecated */\r
+#define _DARWIN_C_SOURCE 1\r
+#else\r
+#define _XOPEN_SOURCE_EXTENDED 1 /* solaris */\r
+#endif\r
+\r
+#include <time.h>\r
+\r
+#undef _XOPEN_SOURCE\r
+#undef _XOPEN_SOURCE_EXTENDED\r
+\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <libxml/parser.h>\r
+\r
+#include "../../core/mem/mem.h"\r
+#include "../../core/dprint.h"\r
+\r
+#include "pidf.h"\r
+\r
+xmlAttrPtr xmlNodeGetAttrByName(xmlNodePtr node, const char *name)\r
+{\r
+       xmlAttrPtr attr = node->properties;\r
+       while(attr) {\r
+               if(xmlStrcasecmp(attr->name, (unsigned char *)name) == 0)\r
+                       return attr;\r
+               attr = attr->next;\r
+       }\r
+       return NULL;\r
+}\r
+\r
+char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name)\r
+{\r
+       xmlAttrPtr attr = xmlNodeGetAttrByName(node, name);\r
+       if(attr)\r
+               return (char *)xmlNodeGetContent(attr->children);\r
+       else\r
+               return NULL;\r
+}\r
+\r
+xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name)\r
+{\r
+       xmlNodePtr cur = node->children;\r
+       while(cur) {\r
+               if(xmlStrcasecmp(cur->name, (unsigned char *)name) == 0)\r
+                       return cur;\r
+               cur = cur->next;\r
+       }\r
+       return NULL;\r
+}\r
+\r
+xmlNodePtr xmlNodeGetNodeByName(\r
+               xmlNodePtr node, const char *name, const char *ns)\r
+{\r
+       xmlNodePtr cur = node;\r
+       while(cur) {\r
+               xmlNodePtr match = NULL;\r
+               if(xmlStrcasecmp(cur->name, (unsigned char *)name) == 0) {\r
+                       if(!ns\r
+                                       || (cur->ns\r
+                                                          && xmlStrcasecmp(\r
+                                                                                 cur->ns->prefix, (unsigned char *)ns)\r
+                                                                                 == 0))\r
+                               return cur;\r
+               }\r
+               match = xmlNodeGetNodeByName(cur->children, name, ns);\r
+               if(match)\r
+                       return match;\r
+               cur = cur->next;\r
+       }\r
+       return NULL;\r
+}\r
+\r
+char *xmlNodeGetNodeContentByName(\r
+               xmlNodePtr root, const char *name, const char *ns)\r
+{\r
+       xmlNodePtr node = xmlNodeGetNodeByName(root, name, ns);\r
+       if(node)\r
+               return (char *)xmlNodeGetContent(node->children);\r
+       else\r
+               return NULL;\r
+}\r
+\r
+xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns)\r
+{\r
+       xmlNodePtr cur = doc->children;\r
+       return xmlNodeGetNodeByName(cur, name, ns);\r
+}\r
+\r
+char *xmlDocGetNodeContentByName(\r
+               xmlDocPtr doc, const char *name, const char *ns)\r
+{\r
+       xmlNodePtr node = xmlDocGetNodeByName(doc, name, ns);\r
+       if(node)\r
+               return (char *)xmlNodeGetContent(node->children);\r
+       else\r
+               return NULL;\r
+}\r
diff --git a/src/modules/lost/pidf.h b/src/modules/lost/pidf.h
new file mode 100755 (executable)
index 0000000..e2f62fd
--- /dev/null
@@ -0,0 +1,53 @@
+/*\r
+ * $Id: pidf.h 1401 2006-12-14 11:12:42Z anca_vamanu $\r
+ *\r
+ * presence module - presence server implementation\r
+ *\r
+ * Copyright (C) 2006 Voice Sistem S.R.L.\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ * History:\r
+ * --------\r
+ *  2006-08-15  initial version (anca)\r
+ */\r
+\r
+/*! \file\r
+ * \brief Kamailio Presence_XML :: PIDF handling\r
+ * \ref pidf.c\r
+ * \ingroup presence_xml\r
+ */\r
+\r
+\r
+#ifndef PIDF_H\r
+#define PIDF_H\r
+\r
+#include "../../core/str.h"\r
+#include <libxml/parser.h>\r
+\r
+xmlNodePtr xmlNodeGetNodeByName(\r
+               xmlNodePtr node, const char *name, const char *ns);\r
+xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns);\r
+xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name);\r
+\r
+char *xmlDocGetNodeContentByName(\r
+               xmlDocPtr doc, const char *name, const char *ns);\r
+char *xmlNodeGetNodeContentByName(\r
+               xmlNodePtr root, const char *name, const char *ns);\r
+char *xmlNodeGetAttrContentByName(xmlNodePtr node, const char *name);\r
+\r
+#endif\r
diff --git a/src/modules/lost/utilities.c b/src/modules/lost/utilities.c
new file mode 100644 (file)
index 0000000..2df1075
--- /dev/null
@@ -0,0 +1,592 @@
+/*\r
+ * lost module utility functions\r
+ *\r
+ * Copyright (C) 2019 Wolfgang Kampichler\r
+ * DEC112, FREQUENTIS AG\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ */\r
+\r
+/*!\r
+ * \file\r
+ * \brief Kamailio lost :: utilities\r
+ * \ingroup lost\r
+ * Module: \ref lost\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <time.h>\r
+\r
+#include <libxml/xmlmemory.h>\r
+#include <libxml/parser.h>\r
+#include "../../core/parser/msg_parser.h"\r
+#include "../../core/parser/parse_content.h"\r
+#include "../../core/parser/parse_uri.h"\r
+#include "../../core/parser/parse_from.h"\r
+#include "../../core/parser/parse_ppi_pai.h"\r
+#include "../../core/dprint.h"\r
+#include "../../core/mem/mem.h"\r
+#include "../../core/mem/shm_mem.h"\r
+\r
+#include "pidf.h"\r
+#include "utilities.h"\r
+\r
+/*\r
+ * lost_trim_content(dest, lgth)\r
+ * removes whitespace that my occur in a content of an xml element\r
+ */\r
+char *lost_trim_content(char *str, int *lgth)\r
+{\r
+       char *end;\r
+\r
+       while(isspace(*str))\r
+               str++;\r
+\r
+       if(*str == 0)\r
+               return NULL;\r
+\r
+       end = str + strlen(str) - 1;\r
+\r
+       while(end > str && isspace(*end))\r
+               end--;\r
+\r
+       *(end + 1) = '\0';\r
+\r
+       *lgth = (end + 1) - str;\r
+\r
+       return str;\r
+}\r
+\r
+/*\r
+ * lost_rand_str(dest, length)\r
+ * creates a random string used as temporary id in a findService request\r
+ */\r
+void lost_rand_str(char *dest, size_t lgth)\r
+{\r
+       size_t index;\r
+       char charset[] = "0123456789"\r
+                                        "abcdefghijklmnopqrstuvwxyz"\r
+                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
+       srand(time(NULL));\r
+       while(lgth-- > 0) {\r
+               index = (double)rand() / RAND_MAX * (sizeof charset - 1);\r
+               *dest++ = charset[index];\r
+       }\r
+       *dest = '\0';\r
+}\r
+\r
+/*\r
+ * lost_free_loc(ptr)\r
+ * freess a location object\r
+ */\r
+void lost_free_loc(p_loc_t ptr)\r
+{\r
+       pkg_free(ptr->identity);\r
+       pkg_free(ptr->urn);\r
+       pkg_free(ptr->longitude);\r
+       pkg_free(ptr->latitude);\r
+       pkg_free(ptr);\r
+}\r
+\r
+/*\r
+ * lost_new_loc(urn)\r
+ * creates a new location object in private memory and returns a pointer\r
+ */\r
+p_loc_t lost_new_loc(str rurn)\r
+{\r
+       s_loc_t *ptr;\r
+       char *id;\r
+       char *urn;\r
+\r
+       ptr = (s_loc_t *)pkg_malloc(sizeof(s_loc_t));\r
+       if(ptr == NULL) {\r
+               LM_ERR("no more private memory\n");\r
+       }\r
+\r
+       id = (char *)pkg_malloc(RANDSTRSIZE * sizeof(char) + 1);\r
+       if(id == NULL) {\r
+               LM_ERR("no more private memory\n");\r
+       }\r
+\r
+       urn = (char *)pkg_malloc(rurn.len + 1);\r
+       if(urn == NULL) {\r
+               LM_ERR("no more private memory\n");\r
+       }\r
+\r
+       memset(urn, 0, rurn.len + 1);\r
+       memcpy(urn, rurn.s, rurn.len);\r
+       urn[rurn.len] = '\0';\r
+\r
+       lost_rand_str(id, RANDSTRSIZE);\r
+\r
+       ptr->identity = id;\r
+       ptr->urn = urn;\r
+       ptr->longitude = NULL;\r
+       ptr->latitude = NULL;\r
+       ptr->radius = 0;\r
+       ptr->recursive = 0;\r
+\r
+       return ptr;\r
+}\r
+\r
+/*\r
+ * lost_get_content(node, name, lgth)\r
+ * gets a nodes "name" content and returns string allocated in private memory\r
+ */\r
+char *lost_get_content(xmlNodePtr node, const char *name, int *lgth)\r
+{\r
+       xmlNodePtr cur = node;\r
+       char *content;\r
+       char *cnt = NULL;\r
+\r
+       int len;\r
+\r
+       *lgth = 0;\r
+       content = xmlNodeGetNodeContentByName(cur, name, NULL);\r
+       len = strlen(content);\r
+\r
+       cnt = (char *)pkg_malloc((len + 1) * sizeof(char));\r
+       if(cnt == NULL) {\r
+               LM_ERR("No more private memory\n");\r
+       }\r
+\r
+       memset(cnt, 0, len + 1);\r
+       memcpy(cnt, content, len);\r
+       cnt[len] = '\0';\r
+\r
+       *lgth = strlen(cnt);\r
+\r
+       xmlFree(content);\r
+\r
+       return cnt;\r
+}\r
+\r
+/*\r
+ * lost_get_property(node, name, lgth)\r
+ * gets a nodes property "name" and returns string allocated in private memory\r
+ */\r
+char *lost_get_property(xmlNodePtr node, const char *name, int *lgth)\r
+{\r
+       xmlNodePtr cur = node;\r
+       char *content;\r
+       char *cnt = NULL;\r
+\r
+       int len;\r
+\r
+       *lgth = 0;\r
+       content = xmlNodeGetAttrContentByName(cur, name);\r
+       len = strlen(content);\r
+\r
+       cnt = (char *)pkg_malloc((len + 1) * sizeof(char));\r
+       if(cnt == NULL) {\r
+               LM_ERR("No more private memory\n");\r
+       }\r
+\r
+       memset(cnt, 0, len + 1);\r
+       memcpy(cnt, content, len);\r
+       cnt[len] = '\0';\r
+\r
+       *lgth = strlen(cnt);\r
+\r
+       xmlFree(content);\r
+\r
+       return cnt;\r
+}\r
+\r
+/*\r
+ * lost_get_childname(name, lgth)\r
+ * gets a nodes child name and returns string allocated in private memory\r
+ */\r
+char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth)\r
+{\r
+       xmlNodePtr cur = node;\r
+       xmlNodePtr parent = NULL;\r
+       xmlNodePtr child = NULL;\r
+\r
+       char *cnt = NULL;\r
+       int len;\r
+\r
+       *lgth = 0;\r
+\r
+       parent = xmlNodeGetNodeByName(cur, name, NULL);\r
+       child = parent->children;\r
+\r
+       if(child) {\r
+               len = strlen((char *)child->name);\r
+\r
+               cnt = (char *)pkg_malloc((len + 1) * sizeof(char));\r
+               if(cnt == NULL) {\r
+                       LM_ERR("no more private memory\n");\r
+               }\r
+\r
+               memset(cnt, 0, len + 1);\r
+               memcpy(cnt, child->name, len);\r
+               cnt[len] = '\0';\r
+\r
+               *lgth = strlen(cnt);\r
+       }\r
+       return cnt;\r
+}\r
+\r
+/*\r
+ * lost_get_geolocation_header(msg, lgth)\r
+ * gets the Geolocation header value and returns string allocated in\r
+ * private memory\r
+ */\r
+char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth)\r
+{\r
+       struct hdr_field *hf;\r
+       char *res = NULL;\r
+\r
+       *lgth = 0;\r
+\r
+       parse_headers(msg, HDR_EOH_F, 0);\r
+\r
+       for(hf = msg->headers; hf; hf = hf->next) {\r
+               if((hf->type == HDR_OTHER_T)\r
+                               && (hf->name.len == LOST_GEOLOC_HEADER_SIZE - 2)) {\r
+                       /* possible hit */\r
+                       if(strncasecmp(\r
+                                          hf->name.s, LOST_GEOLOC_HEADER, LOST_GEOLOC_HEADER_SIZE)\r
+                                       == 0) {\r
+\r
+                               res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));\r
+                               if(res == NULL) {\r
+                                       LM_ERR("no more private memory\n");\r
+                               } else {\r
+                                       memset(res, 0, hf->body.len + 1);\r
+                                       memcpy(res, hf->body.s, hf->body.len + 1);\r
+                                       res[hf->body.len] = '\0';\r
+\r
+                                       *lgth = strlen(res);\r
+                               }\r
+                       } else {\r
+                               LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s,\r
+                                               hf->body.len);\r
+                       }\r
+                       break;\r
+               }\r
+       }\r
+       return res;\r
+}\r
+\r
+/*\r
+ * lost_get_pai_header(msg, lgth)\r
+ * gets the P-A-I header value and returns string allocated in\r
+ * private memory\r
+ */\r
+char *lost_get_pai_header(struct sip_msg *msg, int *lgth)\r
+{\r
+       struct hdr_field *hf;\r
+       char *res = NULL;\r
+\r
+       *lgth = 0;\r
+\r
+       parse_headers(msg, HDR_PAI_F, 0);\r
+\r
+       for(hf = msg->headers; hf; hf = hf->next) {\r
+               if((hf->type == HDR_PAI_T)\r
+                               && (hf->name.len == LOST_PAI_HEADER_SIZE - 2)) {\r
+                       /* possible hit */\r
+                       if(strncasecmp(hf->name.s, LOST_PAI_HEADER, LOST_PAI_HEADER_SIZE)\r
+                                       == 0) {\r
+                               res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));\r
+                               if(res == NULL) {\r
+                                       LM_ERR("no more private memory\n");\r
+                               } else {\r
+\r
+                                       memset(res, 0, hf->body.len + 1);\r
+                                       memcpy(res, hf->body.s, hf->body.len + 1);\r
+                                       res[hf->body.len] = '\0';\r
+\r
+                                       *lgth = strlen(res);\r
+                               }\r
+                       } else {\r
+                               LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s,\r
+                                               hf->body.len);\r
+                       }\r
+                       break;\r
+               }\r
+       }\r
+       return res;\r
+}\r
+\r
+/*\r
+ * lost_get_from_header(msg, lgth)\r
+ * gets the From header value and returns string allocated in\r
+ * private memory\r
+ */\r
+char *lost_get_from_header(struct sip_msg *msg, int *lgth)\r
+{\r
+       to_body_t *f_body;\r
+       char *res = NULL;\r
+\r
+       *lgth = 0;\r
+\r
+       parse_headers(msg, HDR_FROM_F, 0);\r
+\r
+       if(msg->from == NULL || get_from(msg) == NULL) {\r
+               LM_ERR("From header not found\n");\r
+               return res;\r
+       }\r
+       f_body = get_from(msg);\r
+       res = (char *)pkg_malloc((f_body->uri.len + 1) * sizeof(char));\r
+       if(res == NULL) {\r
+               LM_ERR("no more private memory\n");\r
+       } else {\r
+               memset(res, 0, f_body->uri.len + 1);\r
+               memcpy(res, f_body->uri.s, f_body->uri.len + 1);\r
+               res[f_body->uri.len] = '\0';\r
+\r
+               *lgth = strlen(res);\r
+       }\r
+       return res;\r
+}\r
+\r
+\r
+/*\r
+ * lost_parse_location_info(node, loc)\r
+ * parses locationResponse and writes results to location object\r
+ */\r
+int lost_parse_location_info(xmlNodePtr node, p_loc_t loc)\r
+{\r
+       char bufLat[BUFSIZE];\r
+       char bufLon[BUFSIZE];\r
+       int iRadius;\r
+       char *content = NULL;\r
+       int ret = -1;\r
+\r
+       xmlNodePtr cur = node;\r
+\r
+       content = xmlNodeGetNodeContentByName(cur, "pos", NULL);\r
+       if(content) {\r
+               sscanf(content, "%s %s", bufLat, bufLon);\r
+\r
+               loc->latitude = (char *)pkg_malloc(strlen((char *)bufLat) + 1);\r
+               snprintf(loc->latitude, strlen((char *)bufLat) + 1, "%s",\r
+                               (char *)bufLat);\r
+\r
+               loc->longitude = (char *)pkg_malloc(strlen((char *)bufLon) + 1);\r
+               snprintf(loc->longitude, strlen((char *)bufLon) + 1, "%s",\r
+                               (char *)bufLon);\r
+\r
+               loc->radius = 0;\r
+               ret = 0;\r
+       }\r
+\r
+       content = xmlNodeGetNodeContentByName(cur, "radius", NULL);\r
+       if(content) {\r
+               iRadius = 0;\r
+\r
+               sscanf(content, "%d", &iRadius);\r
+               loc->radius = iRadius;\r
+               ret = 0;\r
+       }\r
+\r
+       if(ret < 0) {\r
+               LM_ERR("could not parse location information\n");\r
+       }\r
+       return ret;\r
+}\r
+\r
+/*\r
+ * lost_held_location_request(id, lgth)\r
+ * assembles and returns locationRequest string (allocated in private memory)\r
+ */\r
+char *lost_held_location_request(char *id, int *lgth)\r
+{\r
+       int buffersize = 0;\r
+\r
+       char buf[BUFSIZE];\r
+       char *doc = NULL;\r
+\r
+       xmlChar *xmlbuff = NULL;\r
+       xmlDocPtr request = NULL;\r
+\r
+       xmlNodePtr ptrLocationRequest = NULL;\r
+       xmlNodePtr ptrLocationType = NULL;\r
+       xmlNodePtr ptrDevice = NULL;\r
+\r
+       xmlKeepBlanksDefault(1);\r
+       *lgth = 0;\r
+\r
+       /*\r
+https://tools.ietf.org/html/rfc6155\r
+\r
+<?xml version="1.0" encoding="UTF-8"?>\r
+<locationRequest xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="8">\r
+    <locationType exact="true">geodetic locationURI</locationType>\r
+    <device xmlns="urn:ietf:params:xml:ns:geopriv:held:id">\r
+        <uri>sip:user@example.net</uri>\r
+    </device>\r
+</locationRequest>\r
+*/\r
+\r
+       /* create request */\r
+       request = xmlNewDoc(BAD_CAST "1.0");\r
+       /* locationRequest - element */\r
+       ptrLocationRequest = xmlNewNode(NULL, BAD_CAST "locationRequest");\r
+       xmlDocSetRootElement(request, ptrLocationRequest);\r
+       /* properties */\r
+       xmlNewProp(ptrLocationRequest, BAD_CAST "xmlns",\r
+                       BAD_CAST "urn:ietf:params:xml:ns:geopriv:held");\r
+       xmlNewProp(ptrLocationRequest, BAD_CAST "responseTime", BAD_CAST "8");\r
+       /* locationType - element */\r
+       ptrLocationType = xmlNewChild(ptrLocationRequest, NULL,\r
+                       BAD_CAST "locationType", BAD_CAST "geodetic locationURI");\r
+       /* properties */\r
+       xmlNewProp(ptrLocationType, BAD_CAST "exact", BAD_CAST "false");\r
+       /* device - element */\r
+       ptrDevice = xmlNewChild(ptrLocationRequest, NULL, BAD_CAST "device", NULL);\r
+       /* properties */\r
+       xmlNewProp(ptrDevice, BAD_CAST "xmlns",\r
+                       BAD_CAST "urn:ietf:params:xml:ns:geopriv:held:id");\r
+       /* uri - element */\r
+       snprintf(buf, BUFSIZE, "%s", id);\r
+       xmlNewChild(ptrDevice, NULL, BAD_CAST "uri", BAD_CAST buf);\r
+\r
+       xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);\r
+\r
+       doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char));\r
+       if(doc == NULL) {\r
+               LM_ERR("no more private memory\n");\r
+       }\r
+\r
+       memset(doc, 0, buffersize + 1);\r
+       memcpy(doc, (char *)xmlbuff, buffersize);\r
+       doc[buffersize] = '\0';\r
+\r
+       *lgth = strlen(doc);\r
+\r
+       xmlFree(xmlbuff);\r
+       xmlFreeDoc(request);\r
+\r
+       return doc;\r
+}\r
+\r
+/*\r
+ * lost_find_service_request(loc, lgth)\r
+ * assembles and returns findService request string (allocated in private memory)\r
+ */\r
+char *lost_find_service_request(p_loc_t loc, int *lgth)\r
+{\r
+       int buffersize = 0;\r
+\r
+       char buf[BUFSIZE];\r
+       char *doc = NULL;\r
+\r
+       xmlChar *xmlbuff = NULL;\r
+       xmlDocPtr request = NULL;\r
+\r
+       xmlNodePtr ptrFindService = NULL;\r
+       xmlNodePtr ptrLocation = NULL;\r
+       xmlNodePtr ptrPoint = NULL;\r
+       xmlNodePtr ptrCircle = NULL;\r
+       xmlNodePtr ptrRadius = NULL;\r
+\r
+       xmlKeepBlanksDefault(1);\r
+       *lgth = 0;\r
+\r
+       /*\r
+https://tools.ietf.org/html/rfc5222\r
+\r
+<?xml version="1.0" encoding="UTF-8"?>\r
+<findService\r
+ xmlns="urn:ietf:params:xml:ns:lost1"\r
+ xmlns:p2="http://www.opengis.net/gml"\r
+ serviceBoundary="value"\r
+ recursive="true">\r
+    <location id="6020688f1ce1896d" profile="geodetic-2d">\r
+        <p2:Point id="point1" srsName="urn:ogc:def:crs:EPSG::4326">\r
+            <p2:pos>37.775 -122.422</p2:pos>\r
+        </p2:Point>\r
+    </location>\r
+    <service>urn:service:sos.police</service>\r
+</findService>\r
+ */\r
+       /* create request */\r
+       request = xmlNewDoc(BAD_CAST "1.0");\r
+       /* findService - element */\r
+       ptrFindService = xmlNewNode(NULL, BAD_CAST "findService");\r
+       xmlDocSetRootElement(request, ptrFindService);\r
+       /* set properties */\r
+       xmlNewProp(ptrFindService, BAD_CAST "xmlns",\r
+                       BAD_CAST "urn:ietf:params:xml:ns:lost1");\r
+       xmlNewProp(ptrFindService, BAD_CAST "xmlns:p2",\r
+                       BAD_CAST "http://www.opengis.net/gml");\r
+       xmlNewProp(\r
+                       ptrFindService, BAD_CAST "serviceBoundary", BAD_CAST "reference");\r
+       xmlNewProp(ptrFindService, BAD_CAST "recursive", BAD_CAST "true");\r
+       /* location - element */\r
+       ptrLocation = xmlNewChild(ptrFindService, NULL, BAD_CAST "location", NULL);\r
+       xmlNewProp(ptrLocation, BAD_CAST "id", BAD_CAST loc->identity);\r
+       xmlNewProp(ptrLocation, BAD_CAST "profile", BAD_CAST "geodetic-2d");\r
+       /* set pos */\r
+       snprintf(buf, BUFSIZE, "%s %s", loc->latitude, loc->longitude);\r
+       /* Point */\r
+       if(loc->radius == 0) {\r
+               ptrPoint = xmlNewChild(ptrLocation, NULL, BAD_CAST "Point", NULL);\r
+               xmlNewProp(ptrPoint, BAD_CAST "xmlns",\r
+                               BAD_CAST "http://www.opengis.net/gml");\r
+               xmlNewProp(ptrPoint, BAD_CAST "srsName",\r
+                               BAD_CAST "urn:ogc:def:crs:EPSG::4326");\r
+               /* pos */\r
+               xmlNewChild(ptrPoint, NULL, BAD_CAST "pos", BAD_CAST buf);\r
+       } else {\r
+               /* circle - Point */\r
+               ptrCircle = xmlNewChild(ptrLocation, NULL, BAD_CAST "gs:Circle", NULL);\r
+               xmlNewProp(ptrCircle, BAD_CAST "xmlns:gml",\r
+                               BAD_CAST "http://www.opengis.net/gml");\r
+               xmlNewProp(ptrCircle, BAD_CAST "xmlns:gs",\r
+                               BAD_CAST "http://www.opengis.net/pidflo/1.0");\r
+               xmlNewProp(ptrCircle, BAD_CAST "srsName",\r
+                               BAD_CAST "urn:ogc:def:crs:EPSG::4326");\r
+               /* pos */\r
+               xmlNewChild(ptrCircle, NULL, BAD_CAST "gml:pos", BAD_CAST buf);\r
+               /* circle - radius */\r
+               snprintf(buf, BUFSIZE, "%d", loc->radius);\r
+               ptrRadius = xmlNewChild(\r
+                               ptrCircle, NULL, BAD_CAST "gs:radius", BAD_CAST buf);\r
+               xmlNewProp(ptrRadius, BAD_CAST "uom",\r
+                               BAD_CAST "urn:ogc:def:uom:EPSG::9001");\r
+       }\r
+       /* service - element */\r
+       snprintf(buf, BUFSIZE, "%s", loc->urn);\r
+       xmlNewChild(ptrFindService, NULL, BAD_CAST "service", BAD_CAST buf);\r
+\r
+       xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);\r
+\r
+       doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char));\r
+       if(doc == NULL) {\r
+               LM_ERR("no more private memory\n");\r
+       }\r
+\r
+       memset(doc, 0, buffersize + 1);\r
+       memcpy(doc, (char *)xmlbuff, buffersize);\r
+       doc[buffersize] = '\0';\r
+\r
+       *lgth = strlen(doc);\r
+\r
+       xmlFree(xmlbuff);\r
+       xmlFreeDoc(request);\r
+\r
+       return doc;\r
+}
\ No newline at end of file
diff --git a/src/modules/lost/utilities.h b/src/modules/lost/utilities.h
new file mode 100644 (file)
index 0000000..f6bf935
--- /dev/null
@@ -0,0 +1,73 @@
+/*\r
+ * lost module utility functions\r
+ *\r
+ * Copyright (C) 2019 Wolfgang Kampichler\r
+ * DEC112, FREQUENTIS AG\r
+ *\r
+ * This file is part of Kamailio, a free SIP server.\r
+ *\r
+ * Kamailio is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version\r
+ *\r
+ * Kamailio is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ *\r
+ */\r
+\r
+/*!\r
+ * \file\r
+ * \brief Kamailio lost :: functions\r
+ * \ingroup lost\r
+ * Module: \ref lost\r
+ */\r
+\r
+#ifndef LOST_UTILITIES_H\r
+#define LOST_UTILITIES_H\r
+\r
+#define LOST_GEOLOC_HEADER "Geolocation: "\r
+#define LOST_GEOLOC_HEADER_SIZE strlen(LOST_GEOLOC_HEADER)\r
+#define LOST_PAI_HEADER "P-Asserted-Identity: "\r
+#define LOST_PAI_HEADER_SIZE strlen(LOST_PAI_HEADER)\r
+\r
+#define BUFSIZE 128    /* temporary buffer to hold geolocation */\r
+#define RANDSTRSIZE 16 /* temporary id in a findService request */\r
+\r
+typedef struct\r
+{\r
+       char *identity;\r
+       char *urn;\r
+       char *longitude;\r
+       char *latitude;\r
+       char *uri;\r
+       char *ref;\r
+       int radius;\r
+       int recursive;\r
+} s_loc_t, *p_loc_t;\r
+\r
+void lost_rand_str(char *, size_t);\r
+void lost_free_loc(p_loc_t);\r
+\r
+int lost_get_location_object(p_loc_t, xmlDocPtr, xmlNodePtr);\r
+int lost_parse_location_info(xmlNodePtr node, p_loc_t loc);\r
+\r
+char *lost_find_service_request(p_loc_t, int *);\r
+char *lost_held_location_request(char *, int *);\r
+char *lost_get_content(xmlNodePtr, const char *, int *);\r
+char *lost_get_property(xmlNodePtr, const char *, int *);\r
+char *lost_get_geolocation_header(struct sip_msg *, int *);\r
+char *lost_get_from_header(struct sip_msg *, int *);\r
+char *lost_get_pai_header(struct sip_msg *, int *);\r
+char *lost_get_childname(xmlNodePtr, const char *, int *);\r
+char *lost_trim_content(char *, int *);\r
+\r
+p_loc_t lost_new_loc(str);\r
+\r
+#endif\r