Extract HTTP request handling to CPushSocket

When sending HTTP requests, the notification code doesn't need to know
how to build arbitrary requests.

Issue #226
This commit is contained in:
John Reese 2011-09-29 15:20:53 -04:00
parent 7817e98dba
commit 0af5679f1d
1 changed files with 155 additions and 40 deletions

195
push.cpp
View File

@ -25,15 +25,118 @@
#define PUSH_AWAY #define PUSH_AWAY
#endif #endif
// Debug output // Forward declaration
#define PUSH_DEBUG 0 class CPushMod;
#if PUSH_DEBUG /**
#define PutDebug(s) PutModule(s) * Socket class for generating HTTP requests.
#else */
#define PutDebug(s) //s class CPushSocket : public CSocket
#endif {
public:
CPushSocket(CModule *p) : CSocket(p)
{
EnableReadLine();
parent = (CPushMod*) p;
first = true;
crlf = "\r\n";
user_agent = "ZNC Push";
}
/**
* 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 Request(bool post, const CString& host, const CString& url, MCString& parameters, const CString& auth="")
{
// query string for the request
bool more = false;
CString query;
CString key;
CString value;
for (MCString::iterator param = parameters.begin(); param != parameters.end(); param++)
{
key = urlencode(param->first);
value = urlencode(param->second);
if (more)
{
query += "&" + key + "=" + value;
}
else
{
query += key + "=" + value;
more = true;
}
}
// Request headers and POST body
CString request;
if (post)
{
request += "POST " + url + " HTTP/1.1" + crlf;
request += "Host: " + host + crlf;
request += "Content-Type: application/x-www-form-urlencoded" + crlf;
request += "Content-Length: " + CString(query.length()) + crlf;
request += "User-Agent: " + user_agent + crlf;
}
else
{
request += "GET " + url + "?" + query + " HTTP/1.1" + crlf;
request += "Host: " + host + crlf;
request += "User-Agent: " + user_agent + crlf;
}
if (auth != "")
{
request += "Authorization: Basic " + auth + crlf;
}
request += crlf;
if (post)
{
request += query + crlf;
}
Write(request);
}
// Implemented after CPushMod
virtual void ReadLine(const CString& data);
virtual void Disconnected();
private:
CPushMod *parent;
bool first;
// Too lazy to add CString("\r\n\") everywhere
CString crlf;
// User agent to use
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);
}
};
/**
* Push notification module.
*/
class CPushMod : public CModule class CPushMod : public CModule
{ {
protected: protected:
@ -41,9 +144,6 @@ class CPushMod : public CModule
// Application name // Application name
CString app; CString app;
// Too lazy to add CString("\r\n\") everywhere
CString crlf;
// BASIC auth string, needs to be encoded each time username/secret is changed // BASIC auth string, needs to be encoded each time username/secret is changed
CString notifo_auth; CString notifo_auth;
@ -51,9 +151,6 @@ class CPushMod : public CModule
CString notifo_host; CString notifo_host;
CString notifo_url; CString notifo_url;
// User agent to use
CString user_agent;
// Time last notification was sent for a given context // Time last notification was sent for a given context
map <CString, unsigned int> last_notification_time; map <CString, unsigned int> last_notification_time;
@ -77,13 +174,11 @@ class CPushMod : public CModule
MODCONSTRUCTOR(CPushMod) { MODCONSTRUCTOR(CPushMod) {
app = "ZNC"; app = "ZNC";
crlf = "\r\n";
idle_time = time(NULL); idle_time = time(NULL);
notifo_auth = ""; notifo_auth = "";
notifo_host = "api.notifo.com"; notifo_host = "api.notifo.com";
notifo_url = "/v1/send_notification"; notifo_url = "/v1/send_notification";
user_agent = "ZNC Push";
// Current user // Current user
user = GetUser(); user = GetUser();
@ -112,22 +207,28 @@ class CPushMod : public CModule
// Notification settings // Notification settings
defaults["message_length"] = "100"; defaults["message_length"] = "100";
defaults["message_uri"] = ""; defaults["message_uri"] = "";
defaults["debug"] = "off";
} }
virtual ~CPushMod() {} virtual ~CPushMod() {}
protected: public:
/** /**
* Shorthand for encoding a string for a URL. * Debugging messages. Prints to *push when the debug option is enabled.
* *
* @param str String to be encoded * @param data Debug message
* @return Encoded string
*/ */
CString urlencode(const CString& str) void PutDebug(const CString& data)
{ {
return str.Escape_n(CString::EASCII, CString::EURL); if (options["debug"] == "on")
{
PutModule(data);
}
} }
protected:
/** /**
* Re-encode the authentication credentials. * Re-encode the authentication credentials.
*/ */
@ -197,28 +298,17 @@ class CPushMod : public CModule
replace["{unixtime}"] = CString(time(NULL)); replace["{unixtime}"] = CString(time(NULL));
CString uri = expand(options["message_uri"], replace); CString uri = expand(options["message_uri"], replace);
// POST body parameters for the request MCString params;
CString post = "to=" + urlencode(options["username"]); params["to"] = options["username"];
post += "&msg=" + urlencode(short_message); params["msg"] = short_message;
post += "&label=" + urlencode(app); params["label"] = app;
post += "&title=" + urlencode(title); params["title"] = title;
post += "&uri=" + urlencode(uri); params["uri"] = uri;
// Request headers and POST body
CString request = "POST " + notifo_url + " HTTP/1.1" + crlf;
request += "Host: " + notifo_host + crlf;
request += "Content-Type: application/x-www-form-urlencoded" + crlf;
request += "Content-Length: " + CString(post.length()) + crlf;
request += "User-Agent: " + user_agent + crlf;
request += "Authorization: Basic " + notifo_auth + crlf;
request += crlf;
request += post + crlf;
// 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
CSocket *sock = new CSocket(this); CPushSocket *sock = new CPushSocket(this);
sock->Connect(notifo_host, 443, true); sock->Connect(notifo_host, 443, true);
sock->Write(request); sock->Request(true, notifo_host, notifo_url, params, notifo_auth);
sock->Close(Csock::CLT_AFTERWRITE);
AddSocket(sock); AddSocket(sock);
} }
@ -1112,4 +1202,29 @@ class CPushMod : public CModule
} }
}; };
/**
* Read each line of data returned from the HTTP request.
*/
void CPushSocket::ReadLine(const CString& data)
{
if (first)
{
CString status = data.Token(1);
CString message = data.Token(2, true);
parent->PutDebug(status);
parent->PutDebug(message);
first = false;
}
else
{
parent->PutDebug(data);
}
}
void CPushSocket::Disconnected()
{
Close();
}
MODULEDEFS(CPushMod, "Send highlights and personal messages to a push notification service") MODULEDEFS(CPushMod, "Send highlights and personal messages to a push notification service")