Ver código fonte

Adding the curl module with status "Development"

Anyone that wants to help is welcome to help. I've listed a few
todo's in the TODO.txt file. We will have to decide which of these
that we need for release and which that can be done in the future.
Olle E. Johansson 10 anos atrás
pai
commit
9adec4ca8b

+ 42 - 0
modules/curl/Makefile

@@ -0,0 +1,42 @@
+#
+# curl module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=curl.so
+
+ifeq ($(CROSS_COMPILE),)
+XML2CFG=$(shell which xml2-config)
+CURL_BUILDER=$(shell \
+	if pkg-config --exists libcurl; then \
+		echo 'pkg-config libcurl'; \
+	else \
+		which curl-config; \
+	fi)
+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
+
+ifneq ($(CURL_BUILDER),)
+	DEFS += $(shell $(CURL_BUILDER) --cflags )
+	LIBS += $(shell $(CURL_BUILDER) --libs)
+else
+	DEFS+=-I$(LOCALBASE)/include
+	LIBS+=-L$(LOCALBASE)/lib -lcurl
+endif
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+#SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+include ../../Makefile.modules

+ 342 - 0
modules/curl/README

@@ -0,0 +1,342 @@
+curl
+
+Olle E. Johansson
+
+   Edvina AB
+
+Juha Heinanen
+
+   TutPro Inc.
+
+Carsten Bock
+
+   ng-voice GmbH
+
+   Copyright © 2008-2009 Juha Heinanen
+
+   Copyright © 2013 Carsten Bock, ng-voice GmbH
+
+   Copyright © 2015 Olle E. Johansson, Edvina AB
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. httpredirect (int)
+              3.2. useragent (string)
+              3.3. connection_timeout (int)
+              3.4. tlsclientcert (string)
+              3.5. tlsclientkey (string)
+              3.6. tlscacert (string)
+              3.7. tlsverifyserver (int)
+              3.8. curlcon (string)
+
+        4. Functions
+
+              4.1. curl_http_query(url, [post-data], result)
+
+        5. Pseudovariables
+
+              5.1. $curlerror(error)
+
+        6. Counters
+
+              6.1. curl.connections
+              6.2. curl.connok
+              6.3. curl.connfail
+
+   List of Examples
+
+   1.1. Set httpredirect parameter
+   1.2. Set useragent parameter
+   1.3. Set connection_timeout parameter
+   1.4. Set tlsclientcert parameter
+   1.5. Set tlsclientkey parameter
+   1.6. Set tlscacert parameter
+   1.7. Set tlsverifyserver parameter
+   1.8. Set curlcon parameter
+   1.9. curl_http_query() usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. httpredirect (int)
+        3.2. useragent (string)
+        3.3. connection_timeout (int)
+        3.4. tlsclientcert (string)
+        3.5. tlsclientkey (string)
+        3.6. tlscacert (string)
+        3.7. tlsverifyserver (int)
+        3.8. curlcon (string)
+
+   4. Functions
+
+        4.1. curl_http_query(url, [post-data], result)
+
+   5. Pseudovariables
+
+        5.1. $curlerror(error)
+
+   6. Counters
+
+        6.1. curl.connections
+        6.2. curl.connok
+        6.3. curl.connfail
+
+1. Overview
+
+   This module implements protocol functions that use the libcurl library
+   to fetch data from external HTTP servers or post data to HTTP servers.
+   The module is using a concept of "connections" to define properties of
+   sessions in a simple way.
+
+   Function http_query allows Kamailio to issue an HTTP GET request and
+   get access to parts of the reply. This function has been ported from
+   the utils module and now use the same libcurl functions.
+
+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:
+     * None.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * libcurl.
+
+3. Parameters
+
+   3.1. httpredirect (int)
+   3.2. useragent (string)
+   3.3. connection_timeout (int)
+   3.4. tlsclientcert (string)
+   3.5. tlsclientkey (string)
+   3.6. tlscacert (string)
+   3.7. tlsverifyserver (int)
+   3.8. curlcon (string)
+
+3.1. httpredirect (int)
+
+   If set to 1, enabled, CURL will follow HTTP 302 Redirects. If set to 0,
+   CURL will not follow redirects. Default is 1, enabled.
+
+   The latest redirect URL will be stored in the $curlredirect
+   pseudovariable.
+
+   Example 1.1. Set httpredirect parameter
+...
+modparam("curl", "httpredirect", 0)
+...
+
+3.2. useragent (string)
+
+   Useragent to use in the HTTP protocol for requests. Defaults to the
+   Kamailio SIP useragent string - including software version and
+   platform.
+
+   Example 1.2. Set useragent parameter
+...
+modparam("curl", "useragent", "Secret HTTP REST grabber 0.42")
+...
+
+3.3. connection_timeout (int)
+
+   Defines in seconds how long Kamailio waits for response from servers.
+
+   Default value is zero, i.e., the timeout function is disabled.
+
+   Example 1.3. Set connection_timeout parameter
+...
+modparam("curl", "connection_timeout", 2)
+...
+
+3.4. tlsclientcert (string)
+
+   File name for a TLS client certificate. The certificate needs to be
+   encoded in PEM format.
+
+   Default value is empty string, i.e. no client certificate used. Note
+   that if you specify a client cert, you also need to specify the
+   tlsclientkey.
+
+   Example 1.4. Set tlsclientcert parameter
+...
+modparam("curl", "tlsclientcert", "/var/certs/sollentuna.example.com.cert")
+...
+
+3.5. tlsclientkey (string)
+
+   File name for a TLS client key. The key needs to be encoded in PEM
+   format.
+
+   Default value is empty string, i.e. no client certificate or key is
+   used. Note that if you specify a client key, you also need to specify
+   the tlsclientcert.
+
+   Example 1.5. Set tlsclientkey parameter
+...
+modparam("curl", "tlsclientkey", "/var/certs/sollentuna.example.com.key")
+...
+
+3.6. tlscacert (string)
+
+   File name for the trusted TLS CA cert used to verify servers. The
+   certificates need to be encoded in PEM format.
+
+   Default value is empty string, i.e. no CA certificate is used to verify
+   the host. If tlsverifyhost is on, all TLS connections will fail without
+   any CA certificate to validate with.
+
+   Example 1.6. Set tlscacert parameter
+...
+modparam("curl", "tlscacert", "/var/certs/ca/edvina-sip-ca.pem")
+...
+
+3.7. tlsverifyserver (int)
+
+   If set to 0, TLS verification of the server certificate is disabled.
+   This means that the connection will get encrypted, but there's no
+   authentication. There's no proof that the transmission of data is to
+   the host that is meant to receive data.
+
+   IF set to 1, default setting, and one or more CA certificates is
+   configured, the server TLS certificate will be validated. If validation
+   fails, the connection fails.
+
+   Example 1.7. Set tlsverifyserver parameter
+...
+modparam("curl", "tlsverifyserver", 1)
+...
+
+3.8. curlcon (string)
+
+   Defines a connection and credentials for the connection for use in a
+   connection-oriented function call in this module.
+
+   Syntax:
+   <connection-name>=><schema>://[<username>:<password>@]<hostname/address
+   >[;param=value]
+
+   The address in the URL is the base for the URL in the curlcon_query()
+   call. The address given in the function call will be appended to the
+   base URL in the connection definition.
+
+   By default, no connections are defined.
+
+   Parameters
+     * httpredirect Set to 1 for following HTTP 302 redirect. 0 to
+       disable. Default is the setting for the httpredirect modparam.
+     * timeout Timeout used for this connection. Overrides the default
+       timeout for the module.
+     * useragent Useragent used for HTTP requests. Overrides useragent
+       modparam.
+
+   Example 1.8. Set curlcon parameter
+...
+modparam("curl", "curlcon", "apione=>http://atlanta.example.com")
+modparam("curl", "curlcon", "apitwo=>http://atlanta.example.com/api/12")
+modparam("curl", "curlcon", "apithree=>http://annabella:[email protected]
+.com/api/12")
+modparam("curl", "curlcon", "apifour=>http://stockholm.example.com/api/getstuff;
+timeout=12")
+...
+
+4. Functions
+
+   4.1. curl_http_query(url, [post-data], result)
+
+4.1. curl_http_query(url, [post-data], result)
+
+   Sends HTTP GET or POST request according to URL given in "url"
+   parameter, which is a string that may contain pseudo variables.
+
+   If you want to make a POST-Request, you have to define the "post"-data,
+   that should be submitted in that request as the second parameter.
+
+   If HTTP server returns a class 2xx, 3xx or 4xx reply, the first line of
+   the reply's body (if any) is stored in "result" parameter, which must
+   be a writable pseudo variable.
+
+   Function returns reply code of HTTP reply or -1 if something went
+   wrong.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, and BRANCH_ROUTE.
+
+   Note that this function is based on the http_query function in the
+   utils module. It is changed to use the same base library and settings
+   as the rest of the functions in this module.
+
+   Example 1.9. curl_http_query() usage
+...
+# GET-Request
+curl_http_query("http://tutpro.com/index.php?r_uri=$(ru{s.escape.param})&f_uri=$
+(fu{s.escape.param})",
+           "$var(result)")
+switch ($retcode) {
+       ...
+}
+...
+...
+# POST-Request
+curl_http_query("http://tutpro.com/index.php", "r_uri=$(ru{s.escape.param})&f_ur
+i=$(fu{s.escape.param})",
+           "$var(result)")
+switch ($retcode) {
+       ...
+}
+...
+
+5. Pseudovariables
+
+   5.1. $curlerror(error)
+
+5.1. $curlerror(error)
+
+   cURL returns error codes from the protocol used. If an error happens, a
+   cURL specific error code below 100 is returned. The $curlerror pv
+   returns a text string representing the error. For more information on
+   cURL error codes, please visit
+   http://curl.haxx.se/libcurl/c/libcurl-errors.html
+
+6. Counters
+
+   6.1. curl.connections
+   6.2. curl.connok
+   6.3. curl.connfail
+
+6.1. curl.connections
+
+   The number of connection definitions that are in-memory.
+
+6.2. curl.connok
+
+   The number of successful connections since Kamailio start
+
+6.3. curl.connfail
+
+   The number of failed connections since Kamailio start

+ 49 - 0
modules/curl/TODO.txt

@@ -0,0 +1,49 @@
+The curl module - todo
+======================
+
+- connection parameters
+	timeout=x		- DONE
+	read=line/all
+	tlsverify=on/off
+	failover=curlcon	Other curlcon that can be used for failover in case of 5xx response or timeout
+	httpredirect		- DONE
+	useragent		- DONE
+
+- Make sure modparams work
+	httpproxy		- Not working with curl
+	tls cert, key, ca	- Not working with curl yet
+	httpredirect		- done
+	timeout			- done
+	
+- save fingerprint of cert for trust-on-first-use procedure
+
+- status for curlcon-object, needs to be object in process memory, so child_init
+	- if 5xx response or timeout set flag
+	- function in dialplan - curlcon_exist(object)
+	- pv $curlstatus(object) ??
+
+- pv for curl error codes (result < 100)
+	$curlerror(code)	Result string - DONE
+
+- rpc
+	curl.listcon	list all connection objects with properties
+			-- Done, but not documented
+	curl.modcon     modify connection object
+			url
+			username
+			password
+			scheme
+			timeout
+			read
+			tlsverify
+			failover
+	curl.addcon	add connection object
+	curl.rmcon	remove connection object
+
+
+- Async - event_route when done
+	Need background task process to run curl request
+	event_route[curl:connectioncomplete]
+	$curlcon == connection
+	$curl == URL
+	$curlres == result

+ 606 - 0
modules/curl/curl.c

@@ -0,0 +1,606 @@
+/*
+ * curl Module
+ * Copyright (C) 2015 Edvina AB, Olle E. Johansson
+ *
+ * Based on part of the utils module and part
+ * of the json-rpc-c module
+ *
+ * Copyright (C) 2008 Juha Heinanen
+ * Copyright (C) 2009 1&1 Internet AG
+ * Copyright (C) 2013 Carsten Bock, ng-voice GmbH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*! \file
+ * \brief  Kamailio curl :: The module interface file
+ * \ingroup curl
+ */
+
+/*! \defgroup curl Kamailio :: Module interface to Curl
+ *
+ * http://curl.haxx.se
+ * A generic library for many protocols
+ *
+ *  curl_connect(connection, url, $avp)
+ *  curl_connect(connection, url, content-type, data, $avp)
+ *
+ * 	$var(res) = curl_connect("anders", "/postlåda", "application/json", "{ ok, {200, ok}}", "$avp(gurka)");
+ *
+ */
+
+
+#include <curl/curl.h>
+
+#include "../../mod_fix.h"
+#include "../../sr_module.h"
+#include "../../ut.h"
+#include "../../resolve.h"
+#include "../../locking.h"
+#include "../../script_cb.h"
+#include "../../mem/shm_mem.h"
+#include "../../lib/srdb1/db.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+#include "../../config.h"
+
+#include "functions.h"
+#include "curlcon.h"
+#include "curlrpc.h"
+
+MODULE_VERSION
+
+#define CURL_USER_AGENT  NAME  " (" VERSION " (" ARCH "/" OS_QUOTED "))"
+#define CURL_USER_AGENT_LEN (sizeof(CURL_USER_AGENT)-1)
+
+/* Module parameter variables */
+unsigned int	default_connection_timeout = 4;
+char		*default_tls_cacert = NULL;		/*!< File name: Default CA cert to use for curl TLS connection */
+char		*default_tls_clientcert = NULL;		/*!< File name: Default client certificate to use for curl TLS connection */
+char		*default_tls_clientkey = NULL;		/*!< File name: Key in PEM format that belongs to client cert */
+unsigned int	default_tls_verifyserver = 1;		/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
+char 		*default_http_proxy = NULL;		/*!< Default HTTP proxy to use */
+unsigned int	default_http_proxy_port = 0;		/*!< Default HTTP proxy port to use */
+unsigned int	default_http_follow_redirect = 0;	/*!< Follow HTTP redirects CURLOPT_FOLLOWLOCATION */
+char 		*default_useragent = CURL_USER_AGENT;	/*!< Default CURL useragent. Default "Kamailio Curl " */
+
+static curl_version_info_data *curl_info;
+
+/* lock for configuration access */
+static gen_lock_t *conf_lock = NULL;
+
+
+/* Module management function prototypes */
+static int mod_init(void);
+static int child_init(int);
+static void destroy(void);
+
+/* Fixup functions to be defined later */
+static int fixup_http_query_get(void** param, int param_no);
+static int fixup_free_http_query_get(void** param, int param_no);
+static int fixup_http_query_post(void** param, int param_no);
+static int fixup_free_http_query_post(void** param, int param_no);
+
+static int fixup_curl_connect(void** param, int param_no);
+static int fixup_free_curl_connect(void** param, int param_no);
+static int fixup_curl_connect_post(void** param, int param_no);
+static int fixup_free_curl_connect_post(void** param, int param_no);
+
+/* Wrappers for http_query to be defined later */
+static int w_http_query(struct sip_msg* _m, char* _url, char* _result);
+static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, char* _result);
+static int w_curl_connect(struct sip_msg* _m, char* _con, char * _url, char* _result);
+static int w_curl_connect_post(struct sip_msg* _m, char* _con, char * _url, char* _result, char* _ctype, char* _data);
+
+/* forward function */
+static int curl_con_param(modparam_t type, void* val);
+static int pv_parse_curlerror(pv_spec_p sp, str *in);
+static int pv_get_curlerror(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
+static int pv_parse_curlredirect(pv_spec_p sp, str *in);
+static int pv_get_curlredirect(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
+
+/* Exported functions */
+static cmd_export_t cmds[] = {
+    {"curl_http_query", (cmd_function)w_http_query, 2, fixup_http_query_get,
+     fixup_free_http_query_get,
+     REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+    {"curl_http_query", (cmd_function)w_http_query_post, 3, fixup_http_query_post,
+     fixup_free_http_query_post,
+     REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+    {"curl_connect", (cmd_function)w_curl_connect, 3, fixup_curl_connect,
+     fixup_free_curl_connect,
+     REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+    {"curl_connect", (cmd_function)w_curl_connect_post, 5, fixup_curl_connect_post,
+     fixup_free_curl_connect_post,
+     REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE},
+};
+
+
+/* Exported parameters */
+static param_export_t params[] = {
+    	{"connection_timeout", INT_PARAM, &default_connection_timeout},
+	{"curlcon",  PARAM_STRING|USE_FUNC_PARAM, (void*)curl_con_param},
+	{"tlscacert", PARAM_STRING,  &default_tls_cacert },
+	{"tlsclientcert", PARAM_STRING, &default_tls_clientcert },
+	{"tlsclientkey", PARAM_STRING, &default_tls_clientkey },
+	{"tlsverifyserver", INT_PARAM, &default_tls_verifyserver },
+	{"httpproxyport", INT_PARAM, &default_http_proxy_port },
+	{"httpproxy", PARAM_STRING, &default_http_proxy},
+	{"httpredirect", INT_PARAM, &default_http_follow_redirect },
+	{"useragent", PARAM_STRING,  &default_useragent },
+    	{0, 0, 0}
+};
+
+//		str	default_tls_clientcert;			/*!< File name: Default client certificate to use for curl TLS connection */
+//		str	default_tls_clientkey;			/*!< File name: Key in PEM format that belongs to client cert */
+//		int	default_tls_verifyserver = 1;		/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
+//		str	default_http_proxy;			/*!< Default HTTP proxy to use */
+//		int	default_http_proxy_port;		/*!< Default HTTP proxy port to use */
+//		int	default_http_follow_redirect = 0;	/*!< Follow HTTP redirects CURLOPT_FOLLOWLOCATION */
+//		str	default_useragent;			/*!< Default CURL useragent. Default "Kamailio Curl " */
+
+/*!
+ * \brief Exported Pseudo variables
+ */
+static pv_export_t mod_pvs[] = {
+    {{"curlerror", (sizeof("curlerror")-1)}, /* Curl error codes */
+     PVT_OTHER, pv_get_curlerror, 0,
+	pv_parse_curlerror, 0, 0, 0},
+    {{"curlredirect", (sizeof("redirect")-1)}, /* Curl error codes */
+     PVT_OTHER, pv_get_curlredirect, 0,
+	pv_parse_curlredirect, 0, 0, 0},
+    {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
+};
+
+/* Module interface */
+struct module_exports exports = {
+    "curl",
+    DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds,      /* Exported functions */
+    params,    /* Exported parameters */
+    0,         /* exported statistics */
+    0,   	/* exported MI functions */
+    mod_pvs,         /* exported pseudo-variables */
+    0,         /* extra processes */
+    mod_init,  /* module initialization function */
+    0,         /* response function*/
+    destroy,   /* destroy function */
+    child_init /* per-child init function */
+};
+
+counter_handle_t connections;	/* Number of connection definitions */
+counter_handle_t connok;	/* Successful Connection attempts */
+counter_handle_t connfail;	/* Failed Connection attempts */
+
+
+
+static int init_shmlock(void)
+{
+	return 0;
+}
+
+
+static void destroy_shmlock(void)
+{
+	;
+}
+
+/* Init counters */
+static void curl_counter_init()
+{
+        counter_register(&connections, "curl", "connections", 0, 0, 0, "Counter of connection definitions (curlcon)", 0);
+        counter_register(&connok, "curl", "connok", 0, 0, 0, "Counter of successful connections (200 OK)", 0);
+        counter_register(&connfail, "curl", "connfail", 0, 0, 0, "Counter of failed connections (not 200 OK)", 0);
+}
+
+
+/* Module initialization function */
+static int mod_init(void)
+{
+	
+	LM_DBG("init curl module\n");
+
+	/* Initialize curl */
+	if (curl_global_init(CURL_GLOBAL_ALL)) {
+		LM_ERR("curl_global_init failed\n");
+		return -1;
+	}
+	curl_info = curl_version_info(CURLVERSION_NOW);
+
+	if(curl_init_rpc() < 0)
+        {
+                LM_ERR("failed to register RPC commands\n");
+                return -1;
+        }
+
+	if (init_shmlock() != 0) {
+		LM_CRIT("cannot initialize shmlock.\n");
+		return -1;
+	}
+
+	curl_counter_init();
+	counter_add(connections, curl_connection_count());
+
+	if (default_connection_timeout == 0) {
+		LM_ERR("CURL connection timeout set to zero. Using default 4 secs\n");
+		default_connection_timeout = 4;
+	}
+	if (default_http_proxy_port == 0) {
+		LM_INFO("HTTP proxy port set to 0. Disabling HTTP proxy\n");
+	}
+
+
+	LM_DBG("**** init curl module done. Curl version: %s SSL %s\n", curl_info->version, curl_info->ssl_version);
+	LM_DBG("**** init curl: Number of connection objects: %d \n", curl_connection_count());
+	LM_DBG("**** init curl: User Agent: %s \n", default_useragent);
+	LM_DBG("**** init curl: HTTPredirect: %d \n", default_http_follow_redirect);
+	LM_DBG("**** init curl: Client Cert: %s Key %s\n", default_tls_clientcert, default_tls_clientkey);
+	LM_DBG("**** init curl: CA Cert: %s \n", default_tls_cacert);
+	LM_DBG("**** init curl: HTTP Proxy: %s Port %d\n", default_http_proxy, default_http_proxy_port);
+
+	LM_DBG("Extra: Curl supports %s %s %s \n",
+			(curl_info->features & CURL_VERSION_SSL ? "SSL" : ""),
+			(curl_info->features & CURL_VERSION_IPV6 ? "IPv6" : ""),
+			(curl_info->features & CURL_VERSION_IDN ? "IDN" : "")
+		 );
+	return 0;
+}
+
+/*! Returns TRUE if curl supports TLS */
+int curl_support_tls()
+{
+	return (curl_info->features & CURL_VERSION_SSL);
+}
+
+/*! Returns TRUE if curl supports IPv6 */
+int curl_support_ipv6()
+{
+	return (curl_info->features & CURL_VERSION_IPV6);
+}
+
+
+/* Child initialization function */
+static int child_init(int rank)
+{	
+	if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN) {
+		return 0; /* do nothing for the main process */
+	}
+
+    	return 0;
+}
+
+
+static void destroy(void)
+{
+	/* Cleanup curl */
+	curl_global_cleanup();
+	destroy_shmlock();
+}
+
+
+
+/**
+ * parse curlcon module parameter
+ */
+int curl_con_param(modparam_t type, void *val)
+{
+	if(val == NULL) {
+		goto error;
+	}
+
+	LM_DBG("**** CURL got modparam curlcon \n");
+	return curl_parse_param((char*)val);
+error:
+	return -1;
+
+}
+
+/* Fixup functions */
+
+/*
+ * Fix http_query params: url (string that may contain pvars) and
+ * result (writable pvar).
+ */
+static int fixup_http_query_get(void** param, int param_no)
+{
+    if (param_no == 1) {
+	return fixup_spve_null(param, 1);
+    }
+
+    if (param_no == 2) {
+	if (fixup_pvar_null(param, 1) != 0) {
+	    LM_ERR("failed to fixup result pvar\n");
+	    return -1;
+	}
+	if (((pv_spec_t *)(*param))->setf == NULL) {
+	    LM_ERR("result pvar is not writeble\n");
+	    return -1;
+	}
+	return 0;
+    }
+
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+/*
+ * Free http_query params.
+ */
+static int fixup_free_http_query_get(void** param, int param_no)
+{
+    if (param_no == 1) {
+	LM_WARN("free function has not been defined for spve\n");
+	return 0;
+    }
+
+    if (param_no == 2) {
+	return fixup_free_pvar_null(param, 1);
+    }
+    
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+
+/*
+ * Fix curl_connect params: connection(string/pvar) url (string that may contain pvars) and
+ * result (writable pvar).
+ */
+static int fixup_curl_connect(void** param, int param_no)
+{
+
+    if ((param_no == 1) || (param_no == 2)) {
+	/* We want char * strings */
+	return 0;
+	}
+    if (param_no == 3) {
+	if (fixup_pvar_null(param, 1) != 0) {
+	    LM_ERR("failed to fixup result pvar\n");
+	    return -1;
+	}
+	if (((pv_spec_t *)(*param))->setf == NULL) {
+	    LM_ERR("result pvar is not writeble\n");
+	    return -1;
+	}
+	return 0;
+    }
+
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+/*
+ * Fix curl_connect params when posting (5 parameters): 
+ *	connection, url, content-type, data, pvar
+ */
+static int fixup_curl_connect_post(void** param, int param_no)
+{
+
+    if (param_no == 1 || param_no == 2 || param_no == 3 || param_no == 4) {
+	/* We want char * strings */
+	/* At some point we need to allow pvars in the string. */
+	return 0;
+	}
+    if (param_no == 5) {
+	if (fixup_pvar_null(param, 1) != 0) {
+	    LM_ERR("failed to fixup result pvar\n");
+	    return -1;
+	}
+	if (((pv_spec_t *)(*param))->setf == NULL) {
+	    LM_ERR("result pvar is not writeble\n");
+	    return -1;
+	}
+	return 0;
+    }
+
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+
+/*
+ * Free curl_connect params.
+ */
+static int fixup_free_curl_connect_post(void** param, int param_no)
+{
+    if (param_no == 1 || param_no == 2 || param_no == 3 || param_no == 4) {
+	LM_WARN("free function has not been defined for spve\n");
+	return 0;
+    }
+
+    if (param_no == 5) {
+	return fixup_free_pvar_null(param, 5);
+    }
+    
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+/*
+ * Free curl_connect params.
+ */
+static int fixup_free_curl_connect(void** param, int param_no)
+{
+    if ((param_no == 1) || (param_no == 2)) {
+	LM_WARN("free function has not been defined for spve\n");
+	return 0;
+    }
+
+    if (param_no == 5) {
+	return fixup_free_pvar_null(param, 5);
+    }
+    
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+/*
+ * Wrapper for Curl_connect (GET)
+ */
+static int w_curl_connect(struct sip_msg* _m, char* _con, char * _url, char* _result) {
+//	curl_con_query_url(struct sip_msg* _m, char *connection, char* _url, char* _result, const char *contenttype, char* _post)
+	LM_DBG("**** Curl Connection %s URL %s Result var %s\n", _con, _url, _result);
+
+	return curl_con_query_url(_m, _con, _url, _result, NULL, NULL);
+}
+
+/*
+ * Wrapper for Curl_connect (POST)
+ */
+static int w_curl_connect_post(struct sip_msg* _m, char* _con, char * _url, char* _ctype, char* _data, char *_result) {
+	return curl_con_query_url(_m, _con, _url, _result, _ctype, _data);
+}
+
+
+/*!
+ * Fix http_query params: url (string that may contain pvars) and
+ * result (writable pvar).
+ */
+static int fixup_http_query_post(void** param, int param_no)
+{
+    if ((param_no == 1) || (param_no == 2)) {
+	return fixup_spve_null(param, 1);
+    }
+
+    if (param_no == 3) {
+	if (fixup_pvar_null(param, 1) != 0) {
+	    LM_ERR("failed to fixup result pvar\n");
+	    return -1;
+	}
+	if (((pv_spec_t *)(*param))->setf == NULL) {
+	    LM_ERR("result pvar is not writeble\n");
+	    return -1;
+	}
+	return 0;
+    }
+
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+/*!
+ * Free http_query params.
+ */
+static int fixup_free_http_query_post(void** param, int param_no)
+{
+    if ((param_no == 1) || (param_no == 2)) {
+	LM_WARN("free function has not been defined for spve\n");
+	return 0;
+    }
+
+    if (param_no == 3) {
+	return fixup_free_pvar_null(param, 1);
+    }
+    
+    LM_ERR("invalid parameter number <%d>\n", param_no);
+    return -1;
+}
+
+/*!
+ * Wrapper for HTTP-Query (GET)
+ */
+static int w_http_query(struct sip_msg* _m, char* _url, char* _result) {
+	return http_query(_m, _url, _result, NULL);
+}
+
+
+/*!
+ * Wrapper for HTTP-Query (POST-Variant)
+ */
+static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, char* _result) {
+	return http_query(_m, _url, _result, _post);
+}
+
+/*!
+ * Parse arguments to  pv $curlerror
+ */
+static int pv_parse_curlerror(pv_spec_p sp, str *in)
+{
+	int cerr  = 0;
+	if(sp==NULL || in==NULL || in->len<=0)
+		return -1;
+
+	
+	cerr = atoi(in->s);
+	LM_DBG(" =====> CURL ERROR %d \n", cerr);
+	sp->pvp.pvn.u.isname.name.n = cerr;
+
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = 0;
+
+	return 0;
+}
+
+/*
+ * PV - return curl error explanation as string
+ */
+static int pv_get_curlerror(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
+{
+	str curlerr;
+	char *err = NULL;
+	CURLcode codeerr;
+
+	if(param==NULL) {
+		return -1;
+	}
+
+	/* cURL error codes does not collide with HTTP codes */
+	if (param->pvn.u.isname.name.n < 0 || param->pvn.u.isname.name.n > 999 ) {
+		err = "Bad CURL error code";
+	}
+	if (param->pvn.u.isname.name.n > 99) {
+		err = "HTTP result code";
+	}
+	if (err == NULL) {
+		err = (char *) curl_easy_strerror(param->pvn.u.isname.name.n);
+	}
+	curlerr.s = err;
+	curlerr.len = strlen(err);
+
+	return pv_get_strval(msg, param, res, &curlerr);
+}
+
+/*!
+ * Parse arguments to  pv $curlredirect
+ */
+static int pv_parse_curlredirect(pv_spec_p sp, str *in)
+{
+	int cerr  = 0;
+	if(sp==NULL || in==NULL || in->len<=0) {
+		return -1;
+	}
+
+	// DO SOMETHING HERE
+	return 0;
+}
+
+/*
+ * PV - return curl redirect URL for curlcon
+ *	$curlredirect("curlcon");
+ */
+static int pv_get_curlredirect(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
+{
+	str redirecturl;
+
+	if(param==NULL) {
+		return -1;
+	}
+
+	// DO SOMETHING HERE
+	return pv_get_strval(msg, param, res, &redirecturl);
+}

+ 89 - 0
modules/curl/curl.h

@@ -0,0 +1,89 @@
+/*
+ * header file of curl.c
+ *
+ * Copyright (C) 2008 Juha Heinanen
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*!
+ * \file
+ * \brief Kamailio curl :: Core include file
+ * \ingroup curl
+ * Module: \ref curl
+ */
+
+#ifndef CURL_H
+#define CURL_H
+
+#include "../../str.h"
+#include "../../counters.h"
+#include "../../lib/srdb1/db.h"
+
+extern unsigned int	default_connection_timeout;
+extern char	*default_tls_cacert;			/*!< File name: Default CA cert to use for curl TLS connection */
+extern char	*default_tls_clientcert;		/*!< File name: Default client certificate to use for curl TLS connection */
+extern char	*default_tls_clientkey;			/*!< File name: Key in PEM format that belongs to client cert */
+extern unsigned int	default_tls_verifyserver;		/*!< 0 = Do not verify TLS server cert. 1 = Verify TLS cert (default) */
+extern char 	*default_http_proxy;			/*!< Default HTTP proxy to use */
+extern unsigned int	default_http_proxy_port;		/*!< Default HTTP proxy port to use */
+extern unsigned int	default_http_follow_redirect;	/*!< Follow HTTP redirects CURLOPT_FOLLOWLOCATION */
+extern char 	*default_useragent;			/*!< Default CURL useragent. Default "Kamailio Curl " */
+
+extern counter_handle_t connections;	/* Number of connection definitions */
+extern counter_handle_t connok;	/* Successful Connection attempts */
+extern counter_handle_t connfail;	/* Failed Connection attempts */
+
+/* Curl  stream object  */
+typedef struct {
+	char		*buf;
+	size_t		curr_size;
+	size_t		pos;
+} http_res_stream_t;
+
+
+/*! Predefined connection objects */
+typedef struct _curl_con
+{
+	str name;			/*!< Connection name */
+	unsigned int conid;		/*!< Connection ID */
+	str url;			/*!< The URL without schema (host + base URL)*/
+	str schema;			/*!< The URL schema */
+	str username;			/*!< The username to use for auth */
+	str password;			/*!< The password to use for auth */
+	str failover;			/*!< Another connection to use if this one fails */
+	str cacert;			/*!< File name of CA cert to use */
+	str clientcert;			/*!< File name of CA client cert */
+	str useragent;			/*!< Useragent to use for this connection */
+	int tls_verifyserver;		/*!< TRUE if server cert needs to be verified */
+	int http_follow_redirect;	/*!< TRUE if we should follow HTTP 302 redirects */
+	unsigned int port;		/*!< The port to connect to */
+	int timeout;			/*!< Timeout for this connection */
+	http_res_stream_t *stream;	/*!< Curl stream */
+	struct _curl_con *next;		/*!< next connection */
+	char redirecturl[512];		/*!< Last redirect URL - to use for $curlredirect(curlcon) pv */
+} curl_con_t;
+
+
+/*! Returns true if CURL supports TLS */
+extern int curl_support_tls();
+
+/*! Returns TRUE if curl supports IPv6 */
+extern int curl_support_ipv6();
+
+#endif /* CURL_H */

+ 361 - 0
modules/curl/curlcon.c

@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2015 Olle E. Johansson, Edvina AB
+ *
+ * Based on code from sqlops and htable by Elena-Ramona:
+ * Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*! \file
+ * \brief  Kamailio curl :: Connectoin handling
+ * \ingroup curl
+ */
+
+#include "../../hashes.h"
+#include "../../dprint.h"
+#include "../../parser/parse_param.h"
+#include "../../usr_avp.h"
+#include "curl.h"
+#include "curlcon.h"
+
+#define KEYVALUE_TYPE_NONE	0
+#define KEYVALUE_TYPE_PARAMS	1
+
+
+curl_con_t *_curl_con_root = NULL;
+
+/* Forward declaration */
+curl_con_t *curl_init_con(str *name);
+
+/*! Count the number of connections 
+ */
+unsigned int curl_connection_count()
+{
+	unsigned int i = 0;
+	curl_con_t *cc;
+	cc = _curl_con_root;
+	while(cc)
+	{
+		i++;
+		cc = cc->next;
+	}
+	return i;
+}
+
+
+/*! Find CURL connection by name
+ */
+curl_con_t* curl_get_connection(str *name)
+{
+	curl_con_t *cc;
+	unsigned int conid;
+
+	conid = core_case_hash(name, 0, 0);
+
+	cc = _curl_con_root;
+	while(cc)
+	{
+		if(conid==cc->conid && cc->name.len==name->len
+				&& strncmp(cc->name.s, name->s, name->len)==0)
+			return cc;
+		cc = cc->next;
+	}
+	return NULL;
+}
+
+
+/*! Parse the curlcon module parameter
+ *
+ *	Syntax:
+ *		name => proto://user:password@server/url/url
+ *		name => proto://server/url/url
+ *		name => proto://server/url/url;param=value;param=value
+ *
+ *		the url is very much like CURLs syntax
+ *		the url is a base url where you can add local address
+ */
+int curl_parse_param(char *val)
+{
+	str name	= STR_NULL;;
+	str schema	= STR_NULL;
+	str url		= STR_NULL;
+	str username	= STR_NULL;
+	str password	= STR_NULL;
+	str params	= STR_NULL;
+	str failover	= STR_NULL;
+	unsigned int timeout	= default_connection_timeout;
+	str useragent   = { default_useragent, strlen(default_useragent) };
+	unsigned int http_follow_redirect = default_http_follow_redirect;
+
+	str in;
+	char *p;
+	char *u;
+	param_t *conparams = NULL;
+	curl_con_t *cc;
+
+	username.len = 0;
+	password.len = 0;
+	LM_INFO("curl modparam parsing starting\n");
+	LM_DBG("modparam curlcon: %s\n", val);
+
+	/* parse: name=>http_url*/
+	in.s = val;
+	in.len = strlen(in.s);
+	p = in.s;
+
+	/* Skip white space */
+	while(p < in.s+in.len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) {
+		p++;
+	}
+	if(p > in.s+in.len || *p=='\0') {
+		goto error;
+	}
+
+	/* This is the connection name */
+	name.s = p;
+	/* Skip to whitespace */
+	while(p < in.s + in.len)
+	{
+		if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r') {
+			break;
+		}
+		p++;
+	}
+	if(p > in.s+in.len || *p=='\0') {
+		goto error;
+	}
+	name.len = p - name.s;
+	if(*p != '=')
+	{
+		/* Skip whitespace */
+		while(p<in.s+in.len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) {
+			p++;
+		}
+		if(p>in.s+in.len || *p=='\0' || *p!='=') {
+			goto error;
+		}
+	}
+	p++;
+	if(*p != '>') {
+		goto error;
+	}
+	p++;
+	/* Skip white space again */
+	while(p < in.s+in.len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r')) {
+		p++;
+	}
+	schema.s = p;
+	/* Skip to colon ':' */
+	while(p < in.s + in.len)
+	{
+		if(*p == ':') {
+			break;
+		}
+		p++;
+	}
+	if(*p != ':') {
+		goto error;
+	}
+	schema.len = p - schema.s;
+	p++;	/* Skip the colon */
+	/* Skip two slashes */
+	if(*p != '/') {
+		goto error;
+	}
+	p++;
+	if(*p != '/') {
+		goto error;
+	}
+	p++;
+	/* We are now at the first character after :// */
+	url.s = p;
+	url.len = in.len + (int)(in.s - p);
+	u = p;
+
+	/* Now check if there is a @ character. If so, we need to parse the username
+	   and password */
+	/* Skip to at-sign '@' */
+	while(p < in.s + in.len)
+	{
+		if(*p == '@') {
+			break;
+		}
+		p++;
+	}
+	if (*p == '@') {
+		/* We have a username and possibly password - parse them out */
+		username.s = u;
+		while (u < p) {
+			if (*u == ':') {
+				break;
+			}
+			u++;
+		}
+		username.len = u - username.s;
+
+		/* We either have a : or a @ */
+		if (*u == ':') {
+			u++;
+			/* Go look for password */
+			password.s = u;
+			while (u < p) {
+				u++;
+			}
+			password.len = u - password.s;
+		}
+		p++;	/* Skip the at sign */
+		url.s = p;
+		url.len = in.len + (int)(in.s - p);
+	}
+	/* Reset P to beginning of URL and look for parameters - starting with ; */
+	p = url.s;
+	/* Skip to ';' or end of string */
+	while(p < url.s + url.len)
+	{
+		if(*p == ';') {
+			/* Cut off URL at the ; */
+			url.len = (int)(p - url.s);
+			break;
+		}
+		p++;
+	}
+	if (*p == ';') {
+		/* We have parameters */
+		str tok;
+		int_str ival;
+		int itype;
+		param_t *pit = NULL;
+
+		/* Adjust the URL length */
+
+		p++;		/* Skip the ; */
+		params.s = p;
+		params.len = in.len + (int) (in.s - p);
+		param_hooks_t phooks;
+
+		if (parse_params(&params, CLASS_ANY, &phooks, &conparams) < 0)
+                {
+                        LM_ERR("CURL failed parsing curlcon parameters value\n");
+                        goto error;
+                }
+
+		/* Have parameters */
+		for (pit = conparams; pit; pit=pit->next)
+		{
+			tok = pit->body;
+			if(pit->name.len==12 && strncmp(pit->name.s, "httpredirect", 12)==0) {
+				if(str2int(&tok, &http_follow_redirect) != 0) {
+					/* Bad value */
+					LM_DBG("curl connection [%.*s]: httpredirect bad value. Using default\n", name.len, name.s);
+					http_follow_redirect = default_http_follow_redirect;
+				}
+				if (http_follow_redirect != 0 && http_follow_redirect != 1) {
+					LM_DBG("curl connection [%.*s]: httpredirect bad value. Using default\n", name.len, name.s);
+					http_follow_redirect = default_http_follow_redirect;
+				}
+				LM_DBG("curl [%.*s] - httpredirect [%d]\n", pit->name.len, pit->name.s, http_follow_redirect);
+			} else if(pit->name.len==7 && strncmp(pit->name.s, "timeout", 7)==0) {
+				if(str2int(&tok, &timeout)!=0) {
+					/* Bad timeout */
+					LM_DBG("curl connection [%.*s]: timeout bad value. Using default\n", name.len, name.s);
+					timeout = default_connection_timeout;
+				}
+				LM_DBG("curl [%.*s] - timeout [%d]\n", pit->name.len, pit->name.s, timeout);
+			} else if(pit->name.len==9 && strncmp(pit->name.s, "useragent", 9)==0) {
+				useragent = tok;
+				LM_DBG("curl [%.*s] - useragent [%.*s]\n", pit->name.len, pit->name.s,
+						useragent.len, useragent.s);
+			} else if(pit->name.len==8 && strncmp(pit->name.s, "failover", 8)==0) {
+				failover = tok;
+				LM_DBG("curl [%.*s] - failover [%.*s]\n", pit->name.len, pit->name.s,
+						failover.len, failover.s);
+			} else {
+				LM_ERR("curl Unknown parameter [%.*s] \n", pit->name.len, pit->name.s);
+			}
+		}
+	}
+
+	/* The URL ends either with nothing or parameters. Parameters start with ; */
+	
+
+	LM_DBG("cname: [%.*s] url: [%.*s] username [%.*s] password [%.*s] failover [%.*s] timeout [%d] useragent [%.*s]\n", 
+			name.len, name.s, url.len, url.s, username.len, username.s,
+			password.len, password.s, failover.len, failover.s, timeout, useragent.len, useragent.s);
+
+	if(conparams != NULL) {
+		free_params(conparams);
+	}
+
+	cc =  curl_init_con(&name);
+	if (cc == NULL) {
+		return -1;
+	}
+	cc->username = username;
+	cc->password = password;
+	cc->schema = schema;
+	cc->failover = failover;
+	cc->useragent = useragent;
+	cc->url = url;
+	cc->timeout = timeout;
+	cc->http_follow_redirect = http_follow_redirect;
+	return 0;
+
+error:
+	LM_ERR("invalid curl parameter [%.*s] at [%d]\n", in.len, in.s, (int)(p-in.s));
+
+	if(conparams != NULL) {
+		free_params(conparams);
+	}
+	return -1;
+}
+
+/*! Init connection structure and place it in structure
+ */
+curl_con_t *curl_init_con(str *name)
+{
+	curl_con_t *cc;
+	unsigned int conid;
+
+	conid = core_case_hash(name, 0, 0);
+
+	cc = _curl_con_root;
+	while(cc)
+	{
+		if(conid==cc->conid && cc->name.len == name->len
+				&& strncmp(cc->name.s, name->s, name->len)==0)
+		{
+			LM_ERR("duplicate Curl connection name\n");
+			return NULL;
+		}
+		cc = cc->next;
+	}
+
+	cc = (curl_con_t*) pkg_malloc(sizeof(curl_con_t));
+	if(cc == NULL)
+	{
+		LM_ERR("no pkg memory\n");
+		return NULL;
+	}
+	memset(cc, 0, sizeof(curl_con_t));
+	cc->next = _curl_con_root;
+	_curl_con_root = cc;
+	cc->name = *name;
+
+	LM_INFO("CURL: Added connection [%.*s]\n", name->len, name->s);
+	return cc;
+}

+ 47 - 0
modules/curl/curlcon.h

@@ -0,0 +1,47 @@
+/*
+ * header file of curlcon.c
+ *
+ * Copyright (C) 2015 Olle E. Johansson, Edvina AB
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*!
+ * \file
+ * \brief Kamailio curl :: Connection handler include file
+ * \ingroup curl
+ * Module: \ref curl
+ */
+
+#include "curl.h"
+
+#ifndef CURLCON_H
+#define CURLCON_H
+
+extern curl_con_t *_curl_con_root;
+
+/*! Count the number of connections 
+ */
+unsigned int curl_connection_count();
+
+
+int curl_parse_param(char *val);
+
+curl_con_t* curl_get_connection(str *name);
+
+#endif

+ 113 - 0
modules/curl/curlrpc.c

@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 Olle E. Johansson, Edvina AB
+ * RPC functions
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+/*! \file
+ * \brief  Kamailio curl :: RPC functions
+ * \ingroup curl
+ */
+
+#include "../../dprint.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+#include "curl.h"
+#include "curlcon.h"
+
+
+static const char* curl_rpc_listcon_doc[2] = {
+	"List all CURL connection definitions",
+	0
+};
+
+
+/*
+ * RPC command to print curl destination sets
+ */
+static void curl_rpc_listcon(rpc_t* rpc, void* ctx)
+{
+	void* th;
+	void* ih;
+	void* rh;
+	void* sh;
+	int j;
+	char c[3];
+	str data = {"", 0};
+	curl_con_t *cc;
+
+	cc = _curl_con_root;
+	if(cc==NULL)
+	{
+		LM_ERR("no connection definitions\n");
+		rpc->fault(ctx, 500, "No Connection Definitions");
+		return;
+	}
+
+	/* add entry node */
+	if (rpc->add(ctx, "{", &th) < 0)
+	{
+		rpc->fault(ctx, 500, "Internal error root reply");
+		return;
+	}
+
+        while(cc)
+        {
+		int timeout = (int) cc->timeout;
+		if (rpc->struct_add(th, "{", "CONNECTION", &rh) < 0)
+		{
+			rpc->fault(ctx, 500, "Internal error set structure");
+			return;
+		}
+
+		if(rpc->struct_add(rh, "SSSSSSd", 
+				"NAME", &cc->name,
+				"SCHEMA", &cc->schema,
+				"URI", &cc->url,
+				"USERNAME", &cc->username,
+				"PASSWORD", &cc->password,
+				"FAILOVER", &cc->failover,
+				"TIMEOUT", timeout
+				) < 0) {
+			rpc->fault(ctx, 500, "Internal error set structure");
+			return;
+		}
+                cc = cc->next;
+        }
+	return;
+}
+
+rpc_export_t curl_rpc_cmds[] = {
+	{"curl.listcon",   curl_rpc_listcon,
+		curl_rpc_listcon_doc,   0},
+	{0, 0, 0, 0}
+};
+
+/**
+ * register RPC commands
+ */
+int curl_init_rpc(void)
+{
+	if (rpc_register_array(curl_rpc_cmds)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	return 0;
+}

+ 26 - 0
modules/curl/curlrpc.h

@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 Olle E. Johansson, Edvina AB
+ * RPC functions
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "curl.h"
+#include "curlcon.h"
+#include "../../dprint.h"
+#include "../../rpc.h"
+
+int curl_init_rpc(void);

+ 4 - 0
modules/curl/doc/Makefile

@@ -0,0 +1,4 @@
+docs = curl.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 59 - 0
modules/curl/doc/curl.xml

@@ -0,0 +1,59 @@
+<?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 "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+	<bookinfo>
+	<title>curl</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+		<author>
+		<firstname>Olle E.</firstname>
+		<surname>Johansson</surname>
+		<affiliation><orgname>Edvina AB</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+		<author>
+		<firstname>Juha</firstname>
+		<surname>Heinanen</surname>
+		<affiliation><orgname>TutPro Inc.</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+		<author>
+		<firstname>Carsten</firstname>
+		<surname>Bock</surname>
+		<affiliation><orgname>ng-voice GmbH</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+	</authorgroup>
+	<copyright>
+		<year>2008-2009</year>
+		<holder>Juha Heinanen</holder>
+	</copyright>
+	<copyright>
+		<year>2013</year>
+		<holder>Carsten Bock, ng-voice GmbH</holder>
+	</copyright>
+	<copyright>
+		<year>2015</year>
+		<holder>Olle E. Johansson, Edvina AB</holder>
+	</copyright>
+	</bookinfo>
+	<toc></toc>
+	
+	<xi:include href="curl_admin.xml"/>
+	
+	
+</book>

+ 367 - 0
modules/curl/doc/curl_admin.xml

@@ -0,0 +1,367 @@
+<?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 "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+	This module implements protocol functions that use the libcurl library
+	to fetch data from external HTTP servers or post data to HTTP servers.
+	The module is using a concept of "connections" to define properties
+	of sessions in a simple way.
+	</para>
+	<para>
+	Function http_query allows &kamailio; to issue an HTTP GET
+	request and get access to parts of the reply. This function has
+	been ported from the utils module and now use the same libcurl
+	functions.
+	</para>
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+			None.
+			</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>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	
+	<section>
+		<title>Parameters</title>
+		<section id="utils.p.httpredirect">
+			<title><varname>httpredirect</varname> (int)</title>
+			<para>
+			If set to 1, enabled, CURL will follow HTTP 302 Redirects.
+			If set to 0, CURL will not follow redirects. Default is 1, enabled.
+			</para>
+			<para>
+			The latest redirect URL will be stored in the <emphasis>$curlredirect</emphasis>
+			pseudovariable.
+			</para>
+			<example>
+			<title>Set <varname>httpredirect</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "httpredirect", 0)
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.useragent">
+			<title><varname>useragent</varname> (string)</title>
+			<para>
+			Useragent to use in the HTTP protocol for requests. Defaults to
+			the &kamailio; SIP useragent string - including software version and platform.
+			</para>
+			<example>
+			<title>Set <varname>useragent</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "useragent", "Secret HTTP REST grabber 0.42")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.connection_timeout">
+			<title><varname>connection_timeout</varname> (int)</title>
+			<para>
+			Defines in seconds how long &kamailio; waits for response
+			from servers.
+			</para>
+			<para>
+			<emphasis>
+				Default value is zero, i.e.,
+				the timeout function is disabled.
+			</emphasis>
+			</para>
+			<example>
+			<title>Set <varname>connection_timeout</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "connection_timeout", 2)
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.tlsclientcert">
+			<title><varname>tlsclientcert</varname> (string)</title>
+			<para>
+			File name for a TLS client certificate. The certificate needs to be encoded
+			in PEM format.
+			</para>
+			<para>
+			<emphasis>
+				Default value is empty string, i.e.
+				no client certificate used. Note that if
+				you specify a client cert, you also need to specify
+				the <varname>tlsclientkey</varname>.
+			</emphasis>
+			</para>
+			<example>
+			<title>Set <varname>tlsclientcert</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "tlsclientcert", "/var/certs/sollentuna.example.com.cert")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.tlsclientkey">
+			<title><varname>tlsclientkey</varname> (string)</title>
+			<para>
+			File name for a TLS client key. The key needs to be encoded
+			in PEM format.
+			</para>
+			<para>
+			<emphasis>
+				Default value is empty string, i.e.
+				no client certificate or key is used. Note that if
+				you specify a client key, you also need to specify
+				the <varname>tlsclientcert</varname>.
+			</emphasis>
+			</para>
+			<example>
+			<title>Set <varname>tlsclientkey</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "tlsclientkey", "/var/certs/sollentuna.example.com.key")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.tlscacert">
+			<title><varname>tlscacert</varname> (string)</title>
+			<para>
+			File name for the trusted TLS CA cert used to verify servers.
+			The certificates need to be encoded in PEM format.
+			</para>
+			<para>
+			<emphasis>
+				Default value is empty string, i.e.
+				no CA certificate is used to verify the host. If
+				<varname>tlsverifyhost</varname> is on, all TLS
+				connections will fail without any CA certificate
+				to validate with.
+			</emphasis>
+			</para>
+			<example>
+			<title>Set <varname>tlscacert</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "tlscacert", "/var/certs/ca/edvina-sip-ca.pem")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.tlsverifyserver">
+			<title><varname>tlsverifyserver</varname> (int)</title>
+			<para>
+			If set to 0, TLS verification of the server certificate
+			is disabled. This means that the connection will get 
+			encrypted, but there's no authentication. There's no
+			proof that the transmission of data is to the host
+			that is meant to receive data.
+			</para>
+			<para>
+			IF set to 1, default setting, and one or more CA certificates
+			is configured, the server TLS certificate will be validated.
+			If validation fails, the connection fails.
+			</para>
+			<para>
+			</para>
+			<example>
+			<title>Set <varname>tlsverifyserver</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "tlsverifyserver", 1)
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="utils.p.curlcon">
+			<title><varname>curlcon</varname> (string)</title>
+			<para>
+			Defines a connection and credentials for the connection for use
+			in a connection-oriented function call in this module.
+			</para>
+			<para>
+			<emphasis>Syntax:</emphasis>
+			&lt;connection-name&gt;=&gt;&lt;schema&gt;://[&lt;username&gt;:&lt;password&gt;@]&lt;hostname/address&gt;[;param=value]
+			</para>
+			<para>
+			The address in the URL is the base for the URL in the <function>curlcon_query()</function> call. The
+			address given in the function call will be appended to the base URL in the connection definition.
+			</para>
+			<para>
+			<emphasis>
+				By default, no connections are defined.
+			</emphasis>
+			</para>
+			<para>
+			Parameters
+			<itemizedlist>
+				<listitem><para>
+				<emphasis>httpredirect</emphasis> Set to 1 for following HTTP 302
+				redirect. 0 to disable. Default is the setting for the httpredirect modparam.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>timeout</emphasis> Timeout used for this connection. Overrides the 
+				default timeout for the module.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>useragent</emphasis> Useragent used for HTTP requests. Overrides
+				useragent modparam.
+				</para></listitem>
+			</itemizedlist>
+			</para>
+			<example>
+			<title>Set <varname>curlcon</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("curl", "curlcon", "apione=>http://atlanta.example.com")
+modparam("curl", "curlcon", "apitwo=>http://atlanta.example.com/api/12")
+modparam("curl", "curlcon", "apithree=>http://annabella:[email protected]/api/12")
+modparam("curl", "curlcon", "apifour=>http://stockholm.example.com/api/getstuff;timeout=12")
+...
+				</programlisting>
+			</example>
+		</section>
+	</section>
+
+	<section>
+	<title>Functions</title>
+		<section id="curl.f.curl_http_query">
+			<title>
+				<function moreinfo="none">curl_http_query(url, [post-data], result)</function>
+			</title>
+			<para>
+			Sends HTTP GET or POST request according to URL given in
+			<quote>url</quote> parameter, which is a string that may
+			contain pseudo variables.
+	    	        </para>
+			<para>
+			If you want to make a POST-Request, you have to define
+			the <quote>post</quote>-data, that should be submitted
+			in that request as the second parameter.
+	    	        </para>
+		        <para>
+			If HTTP server returns a class 2xx, 3xx or 4xx reply,
+			the first line of the reply's body (if any) is
+			stored in <quote>result</quote> parameter,
+			which must be a	writable pseudo	variable.
+			</para>
+			<para>
+			Function returns reply code of HTTP reply or -1
+			if something went wrong.
+	   	     	</para>
+			<para>
+			This function can be used from REQUEST_ROUTE,
+			ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE.
+			</para>
+			<para>
+			Note that this function is based on the http_query function in the utils module.
+			It is changed to use the same base library and settings as the rest of the functions in this module.
+			</para>
+			<example>
+				<title><function>curl_http_query()</function> usage</title>
+				<programlisting format="linespecific">
+...
+# GET-Request
+curl_http_query("http://tutpro.com/index.php?r_uri=$(ru{s.escape.param})&amp;f_uri=$(fu{s.escape.param})",
+           "$var(result)")
+switch ($retcode) {
+       ...
+}
+...
+				</programlisting>
+				<programlisting format="linespecific">
+...
+# POST-Request
+curl_http_query("http://tutpro.com/index.php", "r_uri=$(ru{s.escape.param})&amp;f_uri=$(fu{s.escape.param})",
+           "$var(result)")
+switch ($retcode) {
+       ...
+}
+...
+				</programlisting>
+			</example>
+		</section>
+	</section>
+	<section>
+	<title>Pseudovariables</title>
+		<section >
+			<title>
+				<function moreinfo="none">$curlerror(error)</function>
+			</title>
+			<para>
+			cURL returns error codes from the protocol used. If an error
+			happens, a cURL specific error code below 100 is returned.
+			The $curlerror pv returns a text string representing the error.
+			For more information on cURL error codes, please visit
+			http://curl.haxx.se/libcurl/c/libcurl-errors.html
+			</para>
+		</section>
+	</section>
+	<section>
+	<title>Counters</title>
+		<section >
+			<title>
+				<function moreinfo="none">curl.connections</function>
+			</title>
+			<para>
+				The number of connection definitions that are in-memory.
+			</para>
+		</section>
+		<section >
+			<title>
+				<function moreinfo="none">curl.connok</function>
+			</title>
+			<para>
+				The number of successful connections since &kamailio; start
+			</para>
+		</section>
+		<section >
+			<title>
+				<function moreinfo="none">curl.connfail</function>
+			</title>
+			<para>
+				The number of failed connections since &kamailio; start
+			</para>
+		</section>
+	</section>
+	
+</chapter>

+ 294 - 0
modules/curl/functions.c

@@ -0,0 +1,294 @@
+/*
+ * script functions of curl module
+ *
+ * Copyright (C) 2015 Olle E. Johansson, Edvina AB
+ *
+ * Based on functions from siputil
+ * 	Copyright (C) 2008 Juha Heinanen
+ * 	Copyright (C) 2013 Carsten Bock, ng-voice GmbH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*!
+ * \file
+ * \brief Kamailio curl :: script functions
+ * \ingroup curl
+ * Module: \ref curl
+ */
+
+
+#include <curl/curl.h>
+
+#include "../../mod_fix.h"
+#include "../../pvar.h"
+#include "../../route_struct.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../parser/msg_parser.h"
+#include "../../lvalue.h"
+
+#include "curl.h"
+#include "curlcon.h"
+
+/* Forward declaration */
+static int curL_query_url(struct sip_msg* _m, char* _url, char* _dst, const char *username, 
+		const char *secret, const char *contenttype, char* _post, const unsigned int timeout,
+		unsigned int http_follow_redirect);
+
+/* 
+ * curl write function that saves received data as zero terminated
+ * to stream. Returns the amount of data taken care of.
+ *
+ * This function may be called multiple times for larger responses, 
+ * so it reallocs + concatenates the buffer as needed.
+ */
+size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream_ptr)
+{
+    http_res_stream_t *stream = (http_res_stream_t *) stream_ptr;
+
+    stream->buf = (char *) pkg_realloc(stream->buf, stream->curr_size + 
+				(size * nmemb) + 1);
+
+    if (stream->buf == NULL) {
+	LM_ERR("cannot allocate memory for stream\n");
+	return CURLE_WRITE_ERROR;
+    }
+
+    memcpy(&stream->buf[stream->pos], (char *) ptr, (size * nmemb));
+
+    stream->curr_size += ((size * nmemb) + 1);
+    stream->pos += (size * nmemb);
+
+    stream->buf[stream->pos + 1] = '\0';
+
+    return size * nmemb;
+ }
+
+
+/*! Send query to server, optionally post data.
+ */
+static int curL_query_url(struct sip_msg* _m, char* _url, char* _dst, const char *_username, const char *_secret, const char *contenttype, char* _post, unsigned int timeout, unsigned int http_follow_redirect)
+{
+    CURL *curl;
+    CURLcode res;  
+    str value, post_value;
+    char *url, *at, *post;
+    http_res_stream_t stream;
+    long stat;
+    pv_spec_t *dst;
+    pv_value_t val;
+    double download_size;
+    double total_time;
+
+    memset(&stream, 0, sizeof(http_res_stream_t));
+
+#ifdef SKREP
+    if (fixup_get_svalue(_m, (gparam_p)_url, &value) != 0) {
+	LM_ERR("cannot get page value\n");
+	return -1;
+    }
+#endif
+	value.s = _url;
+	value.len = strlen(_url);
+
+    curl = curl_easy_init();
+    if (curl == NULL) {
+	LM_ERR("failed to initialize curl\n");
+	return -1;
+    }
+
+    url = pkg_malloc(value.len + 1);
+    if (url == NULL) {
+	curl_easy_cleanup(curl);
+	LM_ERR("cannot allocate pkg memory for url %d\n", (value.len + 1));
+	return -1;
+    }
+    memcpy(url, value.s, value.len);
+    *(url + value.len) = (char)0;
+    LM_DBG("****** ##### CURL URL %s _url [%.*s]\n", url, value.len, value.s);
+    res = curl_easy_setopt(curl, CURLOPT_URL, url);
+
+    if (_post) {
+        /* Now specify we want to POST data */ 
+	res |= curl_easy_setopt(curl, CURLOPT_POST, 1L);
+	/* Set the content-type of the DATA */
+
+#ifdef SKREP
+    	if (fixup_get_svalue(_m, (gparam_p)_post, &post_value) != 0) {
+		LM_ERR("cannot get post value\n");
+		pkg_free(url);
+		return -1;
+    	}
+        post = pkg_malloc(post_value.len + 1);
+        if (post == NULL) {
+		curl_easy_cleanup(curl);
+		pkg_free(url);
+        	LM_ERR("cannot allocate pkg memory for post\n");
+        	return -1;
+	}
+	memcpy(post, post_value.s, post_value.len);
+	*(post + post_value.len) = (char)0;
+#endif
+ 	res |= curl_easy_setopt(curl, CURLOPT_POSTFIELDS, _post);
+	
+    }
+
+    if (_username) {
+ 	res |= curl_easy_setopt(curl, CURLOPT_USERNAME, _username);
+	res |= curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (CURLAUTH_DIGEST|CURLAUTH_BASIC));
+    }
+    if (_secret) {
+ 	res |= curl_easy_setopt(curl, CURLOPT_PASSWORD, _secret);
+    }
+
+       
+
+    res |= curl_easy_setopt(curl, CURLOPT_NOSIGNAL, (long) 1);
+    res |= curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long) timeout);
+    res |= curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) http_follow_redirect);
+
+
+    res |= curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function);
+    res |= curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
+    	if (res != CURLE_OK) {
+		/* PANIC */
+		LM_ERR("Could not set CURL options. Library error \n");
+	} else {
+    		res = curl_easy_perform(curl);  
+	}
+    pkg_free(url);
+#ifdef SKREP
+    if (_post) {
+	pkg_free(post);
+    }
+#endif
+
+    if (res != CURLE_OK) {
+	/* http://curl.haxx.se/libcurl/c/libcurl-errors.html */
+	if (res == CURLE_COULDNT_CONNECT) {
+		LM_WARN("failed to connect() to host\n");
+	} else if ( res == CURLE_COULDNT_RESOLVE_HOST ) {
+		LM_WARN("couldn't resolve host\n");
+	} else {
+		LM_ERR("failed to perform curl (%d)\n", res);
+	}
+
+	curl_easy_cleanup(curl);
+	if(stream.buf) {
+		pkg_free(stream.buf);
+	}
+	counter_inc(connfail);
+	return -1;
+    }
+
+	/* HTTP_CODE CHANGED TO CURLINFO_RESPONSE_CODE in curl > 7.10.7 */
+    curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &stat);
+/* CURLINFO_CONTENT_TYPE, char *    */
+
+    if ((stat >= 200) && (stat < 500)) {
+	curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &download_size);
+    	curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
+	LM_DBG("http_query download size: %u Time : %ld \n", (unsigned int)download_size, (long) total_time);
+
+	/* search for line feed */
+	at = memchr(stream.buf, (char)10, download_size);
+	if (at == NULL) {
+	    /* not found: use whole stream */
+	    at = stream.buf + (unsigned int)download_size;
+	}
+	val.rs.s = stream.buf;
+	val.rs.len = at - stream.buf;
+	LM_DBG("http_query result: %.*s\n", val.rs.len, val.rs.s);
+	val.flags = PV_VAL_STR;
+	dst = (pv_spec_t *)_dst;
+	dst->setf(_m, &dst->pvp, (int)EQ_T, &val);
+    }
+    	if (stat == 200) {
+		counter_inc(connok);
+	} else {
+		counter_inc(connfail);
+	}
+	
+	/* CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... ); */
+    curl_easy_cleanup(curl);
+    pkg_free(stream.buf);
+    return stat;
+}
+
+
+/*! Run a query based on a connection definition */
+int curl_con_query_url(struct sip_msg* _m, char *connection, char* _url, char* _result, const char *contenttype, char* _post)
+{
+	curl_con_t *conn = NULL;
+	str connstr;
+	char usernamebuf[BUFSIZ/2];
+	char passwordbuf[BUFSIZ/2];
+	char connurlbuf[BUFSIZ/2];
+	char urlbuf[512];
+	unsigned int len = 0;
+
+	memset(usernamebuf,0,sizeof(usernamebuf));
+	memset(passwordbuf,0,sizeof(passwordbuf));
+	memset(connurlbuf,0,sizeof(connurlbuf));
+	memset(urlbuf,0,sizeof(urlbuf));
+
+	/* Find connection if it exists */
+	if (!connection) {
+		LM_ERR("No cURL connection specified\n");
+		return -1;
+	}
+	LM_DBG("******** CURL Connection %s\n", connection);
+	connstr.s = connection;
+	connstr.len = strlen(connection);
+	conn = curl_get_connection(&connstr);
+	if (conn == NULL) {
+		LM_ERR("No cURL connection found: %s\n", connection);
+		return -1;
+	}
+	strncpy(usernamebuf, conn->username.s, conn->username.len);
+	strncpy(passwordbuf, conn->password.s, conn->password.len);
+	strncpy(connurlbuf, conn->url.s, conn->url.len);
+
+	strncpy(urlbuf,conn->schema.s, conn->schema.len);
+	snprintf(&urlbuf[conn->schema.len],(sizeof(urlbuf) - conn->schema.len), "://%s%s%s", connurlbuf, 
+		(_url[0] && _url[0] == '/')?"":(_url[0] != '\0' ? "/": ""), _url);
+	LM_DBG("***** #### ***** CURL URL: %s \n", urlbuf);
+	if (_post && *_post) {
+		LM_DBG("***** #### ***** CURL POST data: %s \n", _post);
+	}
+
+	/* TODO: Concatenate URL in connection with URL given in function */
+	return curL_query_url(_m, urlbuf, _result, usernamebuf, passwordbuf, (contenttype ? contenttype : "text/plain"), _post,
+		conn->timeout, conn->http_follow_redirect );
+}
+
+
+/*!
+ * Performs http_query and saves possible result (first body line of reply)
+ * to pvar.
+ * This is the same http_query as used to be in the utils module.
+ */
+int http_query(struct sip_msg* _m, char* _url, char* _dst, char* _post)
+{
+	int res;
+
+	res =  curL_query_url(_m, _url, _dst, NULL, NULL, "text/plain", _post, default_connection_timeout, default_http_follow_redirect);
+
+	return res;
+}

+ 51 - 0
modules/curl/functions.h

@@ -0,0 +1,51 @@
+/*
+ * headers of script functions of curl module
+ *
+ * Copyright (C) 2008 Juha Heinanen
+ * Copyright (C) 2013 Carsten Bock, ng-voice GmbH
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*!
+ * \file
+ * \brief Kamailio curl :: script functions include file
+ * \ingroup curl
+ * Module: \ref curl
+ */
+
+
+#ifndef UTILS_FUNCTIONS_H
+#define UTILS_FUNCTIONS_H
+
+#include "../../parser/msg_parser.h"
+
+/*! Use predefined connection to run HTTP get or post
+ */
+int curl_con_query_url(struct sip_msg* _m, char *connection, char* _url, char* _result, const char *contenttype, char* _post);
+
+
+
+/* 
+ * Performs http_query and saves possible result (first body line of reply)
+ * to pvar.
+ */
+int http_query(struct sip_msg* _m, char* _url, char* _dst, char* _post);
+
+
+#endif /* UTILS_FUNCTIONS_H */