Add support for using curl instead of CSocket

When compiling with USE_CURL defined, znc push will use libcurl for
making http requests instead of ZNC's builtin CSocket library.  This
enables some advanced features, such as supporting HTTP proxies for
outbound requests.

Currently, building with curl support requires running the following:

    make curl=yes clean install

libcurl must already be installed and accessible by default.  Otherwise,
it is up to the user to populate CXXFLAGS with the appropriate values so
that znc-buildmod can appropriately find and link libcurl to the module.
This commit is contained in:
John Reese 2013-10-28 16:55:18 -07:00
parent 103933f492
commit 77ce3e8c1a
3 changed files with 132 additions and 23 deletions

View File

@ -1,8 +1,15 @@
version := $(shell git describe --dirty) version := $(shell git describe --dirty)
curl=no
ifneq ($(curl),no)
flags=-DUSE_CURL -lcurl
else
flags=
endif
push.so: push.cpp push.so: push.cpp
sed -i -e "s|PUSHVERSION \".*\"|PUSHVERSION \"$(version)\"|" push.cpp sed -i -e "s|PUSHVERSION \".*\"|PUSHVERSION \"$(version)\"|" push.cpp
znc-buildmod push.cpp CXXFLAGS="$(CXXFLAGS) $(flags)" znc-buildmod push.cpp
sed -i -e "s|PUSHVERSION \".*\"|PUSHVERSION \"dev\"|" push.cpp sed -i -e "s|PUSHVERSION \".*\"|PUSHVERSION \"dev\"|" push.cpp
install: push.so install: push.so

View File

@ -50,6 +50,16 @@ Otherwise, run the full command:
$ znc-buildmod push.cpp $ znc-buildmod push.cpp
### Advanced
If you would like to compile ZNC Push using libcurl for http requests, you must use:
$ make curl=yes
If libcurl is not in the default system library paths, you will need to populate `$CXXFLAGS`
with the appropriate GCC flags so that it can find and link ZNC Push with libcurl.
Installation Installation
------------ ------------

136
push.cpp
View File

@ -21,9 +21,25 @@
#include "time.h" #include "time.h"
#include <string.h> #include <string.h>
#ifdef USE_CURL
#include <curl/curl.h>
#endif // USE_CURL
// Forward declaration // Forward declaration
class CPushMod; class CPushMod;
/**
* Shorthand for encoding a string for a URL.
*
* @param str String to be encoded
* @return Encoded string
*/
CString urlencode(const CString& str)
{
return str.Escape_n(CString::EASCII, CString::EURL);
}
#ifndef USE_CURL
/** /**
* Socket class for generating HTTP requests. * Socket class for generating HTTP requests.
*/ */
@ -54,17 +70,13 @@ class CPushSocket : public CSocket
// User agent to use // User agent to use
CString user_agent; CString user_agent;
/**
* Shorthand for encoding a string for a URL.
*
* @param str String to be encoded
* @return Encoded string
*/
CString urlencode(const CString& str)
{
return str.Escape_n(CString::EASCII, CString::EURL);
}
}; };
#else
// forward declaration
CURLcode make_curl_request(const CString& service_host, const CString& service_url,
const CString& service_auth, MCString& params, int port,
bool use_ssl, bool use_post);
#endif // USE_CURL
/** /**
* Push notification module. * Push notification module.
@ -98,6 +110,10 @@ class CPushMod : public CModule
public: public:
MODCONSTRUCTOR(CPushMod) { MODCONSTRUCTOR(CPushMod) {
#ifdef USE_CURL
curl_global_init(CURL_GLOBAL_DEFAULT);
#endif
app = "ZNC"; app = "ZNC";
idle_time = time(NULL); idle_time = time(NULL);
@ -135,7 +151,12 @@ class CPushMod : public CModule
defaults["query_conditions"] = "all"; defaults["query_conditions"] = "all";
defaults["debug"] = "off"; defaults["debug"] = "off";
} }
virtual ~CPushMod() {}
virtual ~CPushMod() {
#ifdef USE_CURL
curl_global_cleanup();
#endif
}
public: public:
@ -441,11 +462,15 @@ class CPushMod : public CModule
return; return;
} }
#ifdef USE_CURL
make_curl_request(service_host, service_url, service_auth, params, use_port, use_ssl, use_post);
#else
// Create the socket connection, write to it, and add it to the queue // Create the socket connection, write to it, and add it to the queue
CPushSocket *sock = new CPushSocket(this); CPushSocket *sock = new CPushSocket(this);
sock->Connect(service_host, use_port, use_ssl); sock->Connect(service_host, use_port, use_ssl);
sock->Request(use_post, service_host, service_url, params, service_auth); sock->Request(use_post, service_host, service_url, params, service_auth);
AddSocket(sock); AddSocket(sock);
#endif
} }
/** /**
@ -1380,11 +1405,15 @@ class CPushMod : public CModule
return; return;
} }
#ifdef USE_CURL
make_curl_request(service_host, service_url, service_auth, params, use_port, use_ssl, use_post);
#else
// Create the socket connection, write to it, and add it to the queue // Create the socket connection, write to it, and add it to the queue
CPushSocket *sock = new CPushSocket(this); CPushSocket *sock = new CPushSocket(this);
sock->Connect(service_host, use_port, use_ssl); sock->Connect(service_host, use_port, use_ssl);
sock->Request(use_post, service_host, service_url, params, service_auth); sock->Request(use_post, service_host, service_url, params, service_auth);
AddSocket(sock); AddSocket(sock);
#endif
PutModule("Ok"); PutModule("Ok");
} }
@ -1420,24 +1449,18 @@ class CPushMod : public CModule
}; };
/** /**
* Send an HTTP request. * Build a query string from a dictionary of request parameters.
* *
* @param post POST command * @param params Request parameters
* @param host Host domain * @return query string
* @param url Resource path
* @param parameters Query parameters
* @param auth Basic authentication string
*/ */
void CPushSocket::Request(bool post, const CString& host, const CString& url, MCString& parameters, const CString& auth) CString build_query_string(MCString& params)
{ {
parent->PutDebug("Building notification to " + host + url + "...");
// query string for the request
bool more = false; bool more = false;
CString query; CString query;
CString key; CString key;
CString value; CString value;
for (MCString::iterator param = parameters.begin(); param != parameters.end(); param++) for (MCString::iterator param = params.begin(); param != params.end(); param++)
{ {
key = urlencode(param->first); key = urlencode(param->first);
value = urlencode(param->second); value = urlencode(param->second);
@ -1453,6 +1476,74 @@ void CPushSocket::Request(bool post, const CString& host, const CString& url, MC
} }
} }
return query;
}
#ifdef USE_CURL
/**
* Send an HTTP request using libcurl.
*
* @param service_host Host domain
* @param service_url Host path
* @param service_auth Basic auth string
* @param params Request parameters
* @param port Port number
* @param use_ssl Use SSL
* @param use_post Use POST method
*/
CURLcode make_curl_request(const CString& service_host, const CString& service_url,
const CString& service_auth, MCString& params, int port,
bool use_ssl, bool use_post)
{
CURL *curl;
CURLcode result;
curl = curl_easy_init();
CString url = CString(use_ssl ? "https" : "http") + "://" + service_host + service_url;
CString query = build_query_string(params);
curl_easy_setopt(curl, CURLOPT_URL, url.data());
curl_easy_setopt(curl, CURLOPT_PORT, port);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "ZNC Push");
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3); // three seconds ought to be good enough for anyone, eh?
if (service_auth != "")
{
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_USERPWD, service_auth.data());
}
if (use_post)
{
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query.data());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, query.length());
}
result = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return result;
}
#else
/**
* Send an HTTP request.
*
* @param post POST command
* @param host Host domain
* @param url Resource path
* @param parameters Query parameters
* @param auth Basic authentication string
*/
void CPushSocket::Request(bool post, const CString& host, const CString& url, MCString& parameters, const CString& auth)
{
parent->PutDebug("Building notification to " + host + url + "...");
CString query = build_query_string(parameters);
parent->PutDebug("Query string: " + query); parent->PutDebug("Query string: " + query);
// Request headers and POST body // Request headers and POST body
@ -1514,5 +1605,6 @@ void CPushSocket::Disconnected()
parent->PutDebug("Disconnected."); parent->PutDebug("Disconnected.");
Close(CSocket::CLT_AFTERWRITE); Close(CSocket::CLT_AFTERWRITE);
} }
#endif // USE_CURL
MODULEDEFS(CPushMod, "Send highlights and personal messages to a push notification service") MODULEDEFS(CPushMod, "Send highlights and personal messages to a push notification service")