Proof of concept completed

User can set and retrieve config values for their Notifo account name
and API secret.  The SEND command allows the user to send an arbitrary
notification to their account.

Fixed issue with encoding credentials in request headers.

Tested against official API and notification succeeded.
This commit is contained in:
John Reese 2011-01-13 16:26:31 -05:00
parent 53e0e30aaf
commit 0baa7d866d
1 changed files with 161 additions and 24 deletions

View File

@ -19,54 +19,191 @@
#error This module needs ZNC 0.072 or newer.
#endif
#define DEBUG_HOST 0
#define DEBUG_LOGGING 0
class CNotifoMod : public CModule
{
protected:
// Too lazy to add CString("\r\n\") everywhere
CString crlf;
// Host and URL to send messages to
CString notifo_host;
CString notifo_url;
// User agent to use
CString user_agent;
// Recipient account's username and API secret
CString notifo_username;
CString notifo_secret;
public:
MODCONSTRUCTOR(CNotifoMod) {}
MODCONSTRUCTOR(CNotifoMod) {
crlf = "\r\n";
#if DEBUG_HOST
notifo_host = "notifo.leetcode.net";
notifo_url = "/index.php";
#else
notifo_host = "api.notifo.com";
notifo_url = "/v1/send_notification";
#endif
user_agent = "ZNC To Notifo";
notifo_username = "";
notifo_secret = "";
}
virtual ~CNotifoMod() {}
virtual void OnModCommand(const CString& sCommand)
{
PutModule("Sending message...");
sendmessage("foo");
}
protected:
/**
* 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);
}
void sendmessage(const CString& message)
/**
* Send a message to the currently-configured Notifo account.
* Requires (and assumes) that the user has already configured their
* username and API secret using the 'set' command.
*
* @param message Message to be sent to the user
*/
void send_message(const CString& message)
{
CString crlf = "\r\n";
// BASIC auth style
CString auth = notifo_username + CString(":") + notifo_secret;
CString auth = "foo:bar";
CString post = "to=";
// POST body parameters for the request
CString post = "to=" + urlencode(notifo_username);
post += "&msg=" + urlencode(message);
post += "&label=" + urlencode(CString("ZNC"));
post += "&title=" + urlencode(CString("New Message"));
post += "&uri=";
post += "&uri=" + urlencode(CString("http://notifo.leetcode.net/"));
CString headers = "POST /index.php HTTP/1.1" + crlf;
headers += "Host: notifo.leetcode.net" + crlf;
headers += "Content-Type: application/x-www-form-urlencoded" + crlf;
headers += "Content-Length: " + CString(post.length()) + crlf;
headers += "User-Agent: zncnotifo" + crlf;
headers += "Authorization: Basic " + auth.Base64Encode() + crlf;
headers += crlf;
headers += post + crlf;
// 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 " + auth.Base64Encode_n() + crlf;
request += crlf;
request += post + crlf;
// Create the socket connection, write to it, and add it to the queue
CSocket *sock = new CSocket(this);
sock->Connect("notifo.leetcode.net", 443, true);
sock->Write(headers);
sock->Connect(notifo_host, 443, true);
sock->Write(request);
sock->Close(Csock::CLT_AFTERWRITE);
AddSocket(sock);
#if DEBUG_LOGGING
// Log the HTTP request
FILE *fh = fopen("/tmp/notifo.log", "a");
fputs(headers.c_str(), fh);
fputs(request.c_str(), fh);
fclose(fh);
#endif
}
protected:
bool OnLoad(const CString& args, CString& message)
{
notifo_username = GetNV("notifo_username");
notifo_secret = GetNV("notifo_secret");
return true;
}
/**
* Handle direct commands to the *notifo virtual user.
*
* @param command Command string
*/
void OnModCommand(const CString& command)
{
VCString tokens;
int token_count = command.Split(" ", tokens, false);
CString action = tokens[0].AsLower();
// SET command
if (action == "set")
{
if (token_count != 3)
{
PutModule("Usage: set <option> <value>");
return;
}
CString option = tokens[1].AsLower();
CString value = tokens[2];
if (option == "username")
{
SetNV("notifo_username", value);
notifo_username = value;
}
else if (option == "secret")
{
SetNV("notifo_secret", value);
notifo_secret = value;
}
else
{
PutModule("Error: invalid option name");
return;
}
PutModule("Done");
}
// GET command
else if (action == "get")
{
if (token_count != 2)
{
PutModule("Usage: get <option>");
return;
}
CString option = tokens[1].AsLower();
if (option == "username")
{
PutModule(GetNV("notifo_username"));
}
else if (option == "secret")
{
PutModule(GetNV("notifo_secret"));
}
else
{
PutModule("Error: invalid option name");
return;
}
}
// SEND command
else if (action == "send")
{
CString message = command.Token(1, true, " ", true);
send_message(message);
}
else
{
PutModule("Error: invalid command");
}
}
};