2011-01-11 03:33:13 +00:00
|
|
|
/**
|
2011-09-29 13:30:06 +00:00
|
|
|
* ZNC Push Module
|
2011-01-11 03:33:13 +00:00
|
|
|
*
|
2011-09-29 13:30:06 +00:00
|
|
|
* Allows the user to enter a Push user and API token, and sends
|
|
|
|
* channel highlights and personal messages to Push.
|
2011-01-11 03:33:13 +00:00
|
|
|
*
|
|
|
|
* Copyright (c) 2011 John Reese
|
|
|
|
* Licensed under the MIT license
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define REQUIRESSL
|
2013-07-31 17:35:53 +00:00
|
|
|
#define PUSHVERSION "dev"
|
2011-01-11 03:33:13 +00:00
|
|
|
|
2012-07-17 00:43:51 +00:00
|
|
|
#include <znc/znc.h>
|
|
|
|
#include <znc/Chan.h>
|
|
|
|
#include <znc/User.h>
|
|
|
|
#include <znc/IRCNetwork.h>
|
|
|
|
#include <znc/Modules.h>
|
|
|
|
#include <znc/FileUtils.h>
|
2012-08-31 05:09:56 +00:00
|
|
|
#include <znc/Client.h>
|
2011-01-14 15:49:08 +00:00
|
|
|
#include "time.h"
|
2012-08-31 02:47:27 +00:00
|
|
|
#include <string.h>
|
2011-01-11 03:33:13 +00:00
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
#ifdef USE_CURL
|
|
|
|
#include <curl/curl.h>
|
|
|
|
#endif // USE_CURL
|
|
|
|
|
2011-09-29 19:20:53 +00:00
|
|
|
// Forward declaration
|
|
|
|
class CPushMod;
|
2011-02-15 15:18:13 +00:00
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2011-09-29 19:20:53 +00:00
|
|
|
/**
|
|
|
|
* Socket class for generating HTTP requests.
|
|
|
|
*/
|
|
|
|
class CPushSocket : public CSocket
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CPushSocket(CModule *p) : CSocket(p)
|
|
|
|
{
|
|
|
|
EnableReadLine();
|
|
|
|
parent = (CPushMod*) p;
|
|
|
|
first = true;
|
|
|
|
crlf = "\r\n";
|
2013-11-17 16:18:42 +00:00
|
|
|
user_agent = "ZNC Push/" + CString(PUSHVERSION);
|
2011-09-29 19:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implemented after CPushMod
|
2011-09-29 20:36:28 +00:00
|
|
|
void Request(bool post, const CString& host, const CString& url, MCString& parameters, const CString& auth="");
|
2011-09-29 19:20:53 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
};
|
2013-10-28 23:55:18 +00:00
|
|
|
#else
|
|
|
|
// forward declaration
|
|
|
|
CURLcode make_curl_request(const CString& service_host, const CString& service_url,
|
|
|
|
const CString& service_auth, MCString& params, int port,
|
2013-11-14 20:08:35 +00:00
|
|
|
bool use_ssl, bool use_post, bool debug);
|
2013-10-28 23:55:18 +00:00
|
|
|
#endif // USE_CURL
|
2011-02-15 15:18:13 +00:00
|
|
|
|
2011-09-29 19:20:53 +00:00
|
|
|
/**
|
|
|
|
* Push notification module.
|
|
|
|
*/
|
2011-09-29 13:30:06 +00:00
|
|
|
class CPushMod : public CModule
|
2011-01-11 03:33:13 +00:00
|
|
|
{
|
2011-01-13 21:26:31 +00:00
|
|
|
protected:
|
|
|
|
|
2011-01-13 22:09:17 +00:00
|
|
|
// Application name
|
|
|
|
CString app;
|
|
|
|
|
2011-01-18 20:24:50 +00:00
|
|
|
// Time last notification was sent for a given context
|
2013-10-26 09:00:33 +00:00
|
|
|
std::map <CString, time_t> last_notification_time;
|
2011-01-14 15:49:08 +00:00
|
|
|
|
2011-01-26 18:09:55 +00:00
|
|
|
// Time of last message by user to a given context
|
2013-10-26 09:00:33 +00:00
|
|
|
std::map <CString, time_t> last_reply_time;
|
2011-01-26 18:09:55 +00:00
|
|
|
|
2011-01-18 20:24:50 +00:00
|
|
|
// Time of last activity by user for a given context
|
2013-10-26 09:00:33 +00:00
|
|
|
std::map <CString, time_t> last_active_time;
|
2011-01-18 17:59:46 +00:00
|
|
|
|
2011-01-18 20:24:50 +00:00
|
|
|
// Time of last activity by user in any context
|
2013-10-26 09:00:33 +00:00
|
|
|
time_t idle_time;
|
2011-01-18 20:24:50 +00:00
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
// User object
|
|
|
|
CUser *user;
|
2011-01-13 22:16:43 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
// Configuration options
|
|
|
|
MCString options;
|
2011-01-14 03:52:50 +00:00
|
|
|
MCString defaults;
|
2011-01-14 00:50:29 +00:00
|
|
|
|
2011-01-11 03:33:13 +00:00
|
|
|
public:
|
|
|
|
|
2011-09-29 13:30:06 +00:00
|
|
|
MODCONSTRUCTOR(CPushMod) {
|
2013-10-28 23:55:18 +00:00
|
|
|
#ifdef USE_CURL
|
|
|
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
|
|
#endif
|
|
|
|
|
2011-01-13 22:09:17 +00:00
|
|
|
app = "ZNC";
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-18 20:24:50 +00:00
|
|
|
idle_time = time(NULL);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
// Current user
|
|
|
|
user = GetUser();
|
|
|
|
|
2011-09-29 14:21:15 +00:00
|
|
|
// Push service information
|
|
|
|
defaults["service"] = "";
|
2011-01-14 03:52:50 +00:00
|
|
|
defaults["username"] = "";
|
|
|
|
defaults["secret"] = "";
|
2012-07-25 23:22:10 +00:00
|
|
|
defaults["target"] = "";
|
2013-03-01 20:23:48 +00:00
|
|
|
|
|
|
|
// Notification settings
|
2013-04-29 20:03:45 +00:00
|
|
|
defaults["message_content"] = "{message}";
|
2013-03-01 20:23:48 +00:00
|
|
|
defaults["message_length"] = "100";
|
|
|
|
defaults["message_title"] = "{title}";
|
2013-04-29 20:03:45 +00:00
|
|
|
defaults["message_uri"] = "";
|
|
|
|
defaults["message_uri_title"] = "";
|
2013-04-29 19:57:58 +00:00
|
|
|
defaults["message_priority"] = "0";
|
2013-04-29 20:03:45 +00:00
|
|
|
defaults["message_sound"] = "";
|
2011-01-14 03:00:24 +00:00
|
|
|
|
|
|
|
// Notification conditions
|
2011-01-14 14:45:47 +00:00
|
|
|
defaults["away_only"] = "no";
|
2011-01-14 03:52:50 +00:00
|
|
|
defaults["client_count_less_than"] = "0";
|
2011-01-19 18:48:43 +00:00
|
|
|
defaults["highlight"] = "";
|
2011-01-18 20:24:50 +00:00
|
|
|
defaults["idle"] = "0";
|
2011-01-18 17:59:46 +00:00
|
|
|
defaults["last_active"] = "180";
|
2011-01-14 15:49:08 +00:00
|
|
|
defaults["last_notification"] = "300";
|
2011-01-18 17:59:46 +00:00
|
|
|
defaults["nick_blacklist"] = "";
|
2011-01-26 18:09:55 +00:00
|
|
|
defaults["replied"] = "yes";
|
2011-01-14 03:27:26 +00:00
|
|
|
|
2013-05-03 01:29:39 +00:00
|
|
|
// Advanced
|
2013-03-01 20:30:28 +00:00
|
|
|
defaults["channel_conditions"] = "all";
|
|
|
|
defaults["query_conditions"] = "all";
|
2011-09-29 19:20:53 +00:00
|
|
|
defaults["debug"] = "off";
|
2011-01-11 03:33:13 +00:00
|
|
|
}
|
2013-10-28 23:55:18 +00:00
|
|
|
|
|
|
|
virtual ~CPushMod() {
|
|
|
|
#ifdef USE_CURL
|
|
|
|
curl_global_cleanup();
|
|
|
|
#endif
|
|
|
|
}
|
2011-01-11 03:33:13 +00:00
|
|
|
|
2011-09-29 19:20:53 +00:00
|
|
|
public:
|
2011-01-13 21:26:31 +00:00
|
|
|
|
|
|
|
/**
|
2011-09-29 19:20:53 +00:00
|
|
|
* Debugging messages. Prints to *push when the debug option is enabled.
|
2011-01-13 21:26:31 +00:00
|
|
|
*
|
2011-09-29 19:20:53 +00:00
|
|
|
* @param data Debug message
|
2011-01-13 21:26:31 +00:00
|
|
|
*/
|
2011-09-29 19:20:53 +00:00
|
|
|
void PutDebug(const CString& data)
|
2011-01-11 03:33:13 +00:00
|
|
|
{
|
2011-09-29 19:20:53 +00:00
|
|
|
if (options["debug"] == "on")
|
|
|
|
{
|
|
|
|
PutModule(data);
|
|
|
|
}
|
2011-01-11 03:33:13 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 19:20:53 +00:00
|
|
|
protected:
|
|
|
|
|
2011-03-09 15:41:55 +00:00
|
|
|
/**
|
|
|
|
* Performs string expansion on a set of keywords.
|
|
|
|
* Given an initial string and a dictionary of string replacments,
|
|
|
|
* iterate over the dictionary, expanding keywords one-by-one.
|
|
|
|
*
|
|
|
|
* @param content String contents
|
|
|
|
* @param replace Dictionary of string replacements
|
|
|
|
* @return Result of string replacements
|
|
|
|
*/
|
|
|
|
CString expand(const CString& content, MCString& replace)
|
|
|
|
{
|
|
|
|
CString result = content.c_str();
|
|
|
|
|
|
|
|
for(MCString::iterator i = replace.begin(); i != replace.end(); i++)
|
|
|
|
{
|
|
|
|
result.Replace(i->first, i->second);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-01-22 08:35:44 +00:00
|
|
|
/**
|
|
|
|
* Verifies whether a given string contains only numbers.
|
|
|
|
*
|
|
|
|
* @param content String to verify
|
|
|
|
*/
|
|
|
|
bool is_number(const CString& content)
|
|
|
|
{
|
|
|
|
CString::const_iterator it = content.begin();
|
|
|
|
while(it != content.end() && std::isdigit(*it)) ++it;
|
|
|
|
return !content.empty() && it == content.end();
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
/**
|
2013-08-27 23:46:40 +00:00
|
|
|
* Send a message to the currently-configured push service.
|
2011-01-13 21:26:31 +00:00
|
|
|
* 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
|
2011-01-13 22:09:17 +00:00
|
|
|
* @param title Message title to use
|
2011-01-14 15:49:08 +00:00
|
|
|
* @param context Channel or nick context
|
2011-01-13 21:26:31 +00:00
|
|
|
*/
|
2011-09-29 13:30:06 +00:00
|
|
|
void send_message(const CString& message, const CString& title="New Message", const CString& context="*push", const CNick& nick=CString("*push"))
|
2011-01-11 03:33:13 +00:00
|
|
|
{
|
2011-01-14 15:49:08 +00:00
|
|
|
// Set the last notification time
|
|
|
|
last_notification_time[context] = time(NULL);
|
|
|
|
|
2011-01-14 03:46:02 +00:00
|
|
|
// Shorten message if needed
|
|
|
|
unsigned int message_length = options["message_length"].ToUInt();
|
|
|
|
CString short_message = message;
|
|
|
|
if (message_length > 0)
|
|
|
|
{
|
|
|
|
short_message = message.Ellipsize(message_length);
|
|
|
|
}
|
|
|
|
|
2011-03-09 15:59:51 +00:00
|
|
|
// Generate an ISO8601 date string
|
|
|
|
time_t rawtime;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
time(&rawtime);
|
|
|
|
timeinfo = localtime(&rawtime);
|
|
|
|
char iso8601 [20];
|
|
|
|
strftime(iso8601, 20, "%Y-%m-%d %H:%M:%S", timeinfo);
|
|
|
|
|
2013-01-09 21:27:53 +00:00
|
|
|
// Message string replacements
|
2011-03-09 15:59:51 +00:00
|
|
|
MCString replace;
|
|
|
|
replace["{context}"] = context;
|
|
|
|
replace["{nick}"] = nick.GetNick();
|
|
|
|
replace["{datetime}"] = CString(iso8601);
|
|
|
|
replace["{unixtime}"] = CString(time(NULL));
|
2013-01-09 21:27:53 +00:00
|
|
|
replace["{message}"] = short_message;
|
|
|
|
replace["{title}"] = title;
|
|
|
|
replace["{username}"] = options["username"];
|
|
|
|
replace["{secret}"] = options["secret"];
|
|
|
|
|
|
|
|
CString message_uri = expand(options["message_uri"], replace);
|
|
|
|
CString message_title = expand(options["message_title"], replace);
|
|
|
|
CString message_content = expand(options["message_content"], replace);
|
2011-03-09 15:59:51 +00:00
|
|
|
|
2011-09-29 19:42:33 +00:00
|
|
|
// Set up the connection profile
|
|
|
|
CString service = options["service"];
|
|
|
|
bool use_post = true;
|
|
|
|
int use_port = 443;
|
|
|
|
bool use_ssl = true;
|
|
|
|
CString service_host;
|
|
|
|
CString service_url;
|
|
|
|
CString service_auth;
|
2011-09-29 19:20:53 +00:00
|
|
|
MCString params;
|
2011-09-29 19:42:33 +00:00
|
|
|
|
2011-09-29 19:44:58 +00:00
|
|
|
// Service-specific profiles
|
2013-08-27 23:46:40 +00:00
|
|
|
if (service == "pushbullet")
|
2013-05-03 01:35:36 +00:00
|
|
|
{
|
2013-05-10 02:35:54 +00:00
|
|
|
if (options["target"] == "" || options["secret"] == "")
|
2013-05-03 01:35:36 +00:00
|
|
|
{
|
2013-05-10 02:35:54 +00:00
|
|
|
PutModule("Error: target (device id) or secret (api key) not set");
|
2013-05-03 01:35:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-15 14:44:48 +00:00
|
|
|
service_host = "api.pushbullet.com";
|
2013-05-03 01:35:36 +00:00
|
|
|
service_url = "/api/pushes";
|
|
|
|
|
|
|
|
// BASIC auth, base64-encoded APIKey:
|
|
|
|
service_auth = options["secret"] + CString(":");
|
|
|
|
service_auth.Base64Encode();
|
|
|
|
|
2014-01-22 08:50:07 +00:00
|
|
|
// Pushbullet uses numeric device_id but they
|
2014-01-22 08:35:44 +00:00
|
|
|
// are transitioning to an alphanumeric device_iden
|
|
|
|
if (is_number(options["target"]))
|
|
|
|
{
|
|
|
|
params["device_id"] = options["target"];
|
|
|
|
} else {
|
|
|
|
params["device_iden"] = options["target"];
|
|
|
|
}
|
2013-05-03 01:35:36 +00:00
|
|
|
params["type"] = "note";
|
|
|
|
params["title"] = message_title;
|
|
|
|
params["body"] = message_content;
|
|
|
|
}
|
2011-10-05 19:25:59 +00:00
|
|
|
else if (service == "boxcar")
|
|
|
|
{
|
|
|
|
if (options["username"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: username not set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString boxcar_api_key = "puSd2qp2gCDZO7nWkvb9";
|
|
|
|
CString boxcar_api_secret = "wLQQKSyGybIOkggbiKipefeYGLni9B3FPZabopHp";
|
|
|
|
|
|
|
|
service_host = "boxcar.io";
|
|
|
|
service_url = "/devices/providers/" + boxcar_api_key + "/notifications";
|
|
|
|
|
|
|
|
params["email"] = options["username"];
|
|
|
|
params["notification[from_screen_name]"] = context;
|
2013-01-09 21:27:53 +00:00
|
|
|
params["notification[message]"] = message_content;
|
|
|
|
params["notification[source_url]"] = message_uri;
|
2011-10-05 19:25:59 +00:00
|
|
|
}
|
2011-10-06 18:54:17 +00:00
|
|
|
else if (service == "nma")
|
|
|
|
{
|
|
|
|
if (options["secret"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: secret not set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_host = "www.notifymyandroid.com";
|
|
|
|
service_url = "/publicapi/notify";
|
|
|
|
|
|
|
|
params["apikey"] = options["secret"];
|
|
|
|
params["application"] = app;
|
2013-01-09 21:27:53 +00:00
|
|
|
params["event"] = message_title;
|
|
|
|
params["description"] = message_content;
|
|
|
|
params["url"] = message_uri;
|
2011-10-06 18:54:17 +00:00
|
|
|
}
|
2012-07-24 05:07:28 +00:00
|
|
|
else if (service == "pushover")
|
|
|
|
{
|
2013-07-30 17:25:46 +00:00
|
|
|
if (options["username"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: username (user key) not set");
|
|
|
|
return;
|
|
|
|
}
|
2012-07-24 05:07:28 +00:00
|
|
|
if (options["secret"] == "")
|
|
|
|
{
|
2013-07-30 17:25:46 +00:00
|
|
|
PutModule("Error: secret (application token/key) not set");
|
2012-07-24 05:07:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_host = "api.pushover.net";
|
|
|
|
service_url = "/1/messages.json";
|
|
|
|
|
2013-07-30 17:25:46 +00:00
|
|
|
params["token"] = options["secret"];
|
|
|
|
params["user"] = options["username"];
|
2013-01-09 21:27:53 +00:00
|
|
|
params["title"] = message_title;
|
|
|
|
params["message"] = message_content;
|
2012-07-25 23:22:10 +00:00
|
|
|
|
2013-01-09 21:27:53 +00:00
|
|
|
if (message_uri != "")
|
2012-10-24 22:59:17 +00:00
|
|
|
{
|
2013-01-09 21:27:53 +00:00
|
|
|
params["url"] = message_uri;
|
2012-10-24 22:59:17 +00:00
|
|
|
}
|
|
|
|
|
2013-05-03 01:29:39 +00:00
|
|
|
if ( options["message_uri_title"] != "" )
|
|
|
|
{
|
|
|
|
params["url_title"] = options["message_uri_title"];
|
|
|
|
}
|
2013-03-01 21:58:03 +00:00
|
|
|
|
2012-07-25 23:22:10 +00:00
|
|
|
if (options["target"] != "")
|
|
|
|
{
|
|
|
|
params["device"] = options["target"];
|
|
|
|
}
|
2013-03-01 18:47:32 +00:00
|
|
|
|
2013-05-03 01:29:39 +00:00
|
|
|
if ( options["message_sound"] != "" )
|
|
|
|
{
|
|
|
|
params["sound"] = options["message_sound"];
|
|
|
|
}
|
2013-04-29 19:57:58 +00:00
|
|
|
|
|
|
|
if (options["message_priority"] != "")
|
|
|
|
{
|
|
|
|
params["priority"] = options["message_priority"];
|
|
|
|
}
|
2012-07-24 05:07:28 +00:00
|
|
|
}
|
2011-10-06 18:43:54 +00:00
|
|
|
else if (service == "prowl")
|
|
|
|
{
|
|
|
|
if (options["secret"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: secret not set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_host = "api.prowlapp.com";
|
|
|
|
service_url = "/publicapi/add";
|
|
|
|
|
|
|
|
params["apikey"] = options["secret"];
|
|
|
|
params["application"] = app;
|
2013-01-09 21:27:53 +00:00
|
|
|
params["event"] = message_title;
|
|
|
|
params["description"] = message_content;
|
|
|
|
params["url"] = message_uri;
|
2011-10-06 18:43:54 +00:00
|
|
|
}
|
2012-01-02 09:51:37 +00:00
|
|
|
else if (service == "supertoasty")
|
|
|
|
{
|
|
|
|
if (options["secret"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: secret (device id) not set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
use_post = false;
|
|
|
|
use_port = 80;
|
|
|
|
use_ssl = false;
|
|
|
|
|
|
|
|
service_host = "api.supertoasty.com";
|
|
|
|
service_url = "/notify/"+options["secret"];
|
|
|
|
|
2013-01-09 21:27:53 +00:00
|
|
|
params["title"] = message_title;
|
|
|
|
params["text"] = message_content;
|
2012-01-02 09:51:37 +00:00
|
|
|
params["image"] = "https://github.com/jreese/znc-push/raw/supertoasty/logo.png";
|
|
|
|
params["sender"] = "ZNC Push";
|
|
|
|
}
|
2013-01-09 22:26:54 +00:00
|
|
|
else if (service == "url")
|
|
|
|
{
|
2013-01-09 23:01:51 +00:00
|
|
|
if (options["message_uri"] == "")
|
2013-01-09 22:26:54 +00:00
|
|
|
{
|
2013-01-09 23:01:51 +00:00
|
|
|
PutModule("Error: message_uri not set");
|
2013-01-09 22:26:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-26 09:00:33 +00:00
|
|
|
CString::size_type count;
|
2013-01-09 22:26:54 +00:00
|
|
|
VCString parts;
|
2013-01-09 23:01:51 +00:00
|
|
|
CString url = options["message_uri"];
|
2013-01-09 22:26:54 +00:00
|
|
|
|
|
|
|
// Verify that the URL begins with either http:// or https://
|
|
|
|
count = url.Split("://", parts, false);
|
|
|
|
|
|
|
|
if (count != 2)
|
|
|
|
{
|
|
|
|
PutModule("Error: invalid url format");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
use_post = false;
|
|
|
|
|
|
|
|
if (parts[0] == "https")
|
|
|
|
{
|
|
|
|
use_ssl = true;
|
|
|
|
use_port = 443;
|
|
|
|
}
|
|
|
|
else if (parts[0] == "http")
|
|
|
|
{
|
|
|
|
use_ssl = false;
|
|
|
|
use_port = 80;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PutModule("Error: invalid url schema");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process the remaining portion of the URL
|
|
|
|
url = parts[1];
|
|
|
|
|
|
|
|
// Split out the host and optional port number; this breaks with raw IPv6 addresses
|
|
|
|
CString host = url.Token(0, false, "/");
|
|
|
|
count = host.Split(":", parts, false);
|
|
|
|
|
|
|
|
if (count > 1)
|
|
|
|
{
|
|
|
|
use_port = parts[1].ToInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
service_host = parts[0];
|
|
|
|
|
|
|
|
// Split remaining URL into path and query components
|
|
|
|
url = "/" + url.Token(1, true, "/");
|
|
|
|
service_url = expand(url.Token(0, false, "?"), replace);
|
|
|
|
|
|
|
|
// Parse and expand query parameter values
|
|
|
|
url = url.Token(1, true, "?");
|
|
|
|
url.URLSplit(params);
|
|
|
|
|
|
|
|
for (MCString::iterator i = params.begin(); i != params.end(); i++) {
|
|
|
|
i->second = expand(i->second, replace);
|
|
|
|
}
|
|
|
|
}
|
2014-01-22 18:47:33 +00:00
|
|
|
else if (service == "airgram")
|
|
|
|
{
|
|
|
|
if (options["target"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: target (email) not set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_host = "api.airgramapp.com";
|
|
|
|
service_url = "/1/send_as_guest";
|
|
|
|
|
|
|
|
params["email"] = options["target"];
|
|
|
|
params["msg"] = message_content;
|
|
|
|
}
|
2011-09-29 19:42:33 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
PutModule("Error: service type not selected");
|
|
|
|
return;
|
|
|
|
}
|
2011-01-11 03:33:13 +00:00
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
#ifdef USE_CURL
|
2013-11-14 20:08:35 +00:00
|
|
|
make_curl_request(service_host, service_url, service_auth, params, use_port, use_ssl, use_post, options["debug"] == "on");
|
2013-10-28 23:55:18 +00:00
|
|
|
#else
|
2011-01-13 21:26:31 +00:00
|
|
|
// Create the socket connection, write to it, and add it to the queue
|
2011-09-29 19:20:53 +00:00
|
|
|
CPushSocket *sock = new CPushSocket(this);
|
2011-09-29 19:42:33 +00:00
|
|
|
sock->Connect(service_host, use_port, use_ssl);
|
|
|
|
sock->Request(use_post, service_host, service_url, params, service_auth);
|
2011-01-11 03:33:13 +00:00
|
|
|
AddSocket(sock);
|
2013-10-28 23:55:18 +00:00
|
|
|
#endif
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
|
2011-02-14 19:27:18 +00:00
|
|
|
/**
|
|
|
|
* Evaluate a boolean expression using condition values.
|
|
|
|
* All tokens must be separated by spaces, using "and" and "or" for
|
|
|
|
* boolean operators, "(" and ")" to enclose sub-expressions, and
|
|
|
|
* condition option names to evaluate each condition.
|
|
|
|
*
|
|
|
|
* @param expression Boolean expression string
|
|
|
|
* @param context Notification context
|
|
|
|
* @param nick Sender nick
|
|
|
|
* @param message Message contents
|
|
|
|
* @return Result of boolean evaluation
|
|
|
|
*/
|
|
|
|
bool eval(const CString& expression, const CString& context=CString(""), const CNick& nick=CNick(""), const CString& message=" ")
|
|
|
|
{
|
|
|
|
CString padded = expression.Replace_n("(", " ( ");
|
|
|
|
padded.Replace(")", " ) ");
|
|
|
|
|
|
|
|
VCString tokens;
|
|
|
|
padded.Split(" ", tokens, false);
|
|
|
|
|
2011-02-15 15:18:13 +00:00
|
|
|
PutDebug("Evaluating message: <" + nick.GetNick() + "> " + message);
|
|
|
|
bool result = eval_tokens(tokens.begin(), tokens.end(), context, nick, message);
|
|
|
|
|
|
|
|
return result;
|
2011-02-14 19:27:18 +00:00
|
|
|
}
|
|
|
|
|
2011-02-15 15:18:13 +00:00
|
|
|
#define expr(x, y) else if (token == x) { \
|
|
|
|
bool result = y; \
|
|
|
|
dbg += CString(x) + "/" + CString(result ? "true" : "false") + " "; \
|
|
|
|
value = oper ? value && result : value || result; \
|
|
|
|
}
|
2011-02-14 19:27:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Evaluate a tokenized boolean expression, or sub-expression.
|
|
|
|
*
|
|
|
|
* @param pos Token vector iterator current position
|
|
|
|
* @param end Token vector iterator end position
|
|
|
|
* @param context Notification context
|
|
|
|
* @param nick Sender nick
|
|
|
|
* @param message Message contents
|
|
|
|
* @return Result of boolean expression
|
|
|
|
*/
|
|
|
|
bool eval_tokens(VCString::iterator pos, VCString::iterator end, const CString& context, const CNick& nick, const CString& message)
|
|
|
|
{
|
|
|
|
bool oper = true;
|
|
|
|
bool value = true;
|
|
|
|
|
2011-02-15 15:18:13 +00:00
|
|
|
CString dbg = "";
|
|
|
|
|
2011-02-14 19:27:18 +00:00
|
|
|
for(; pos != end; pos++)
|
|
|
|
{
|
|
|
|
CString token = pos->AsLower();
|
|
|
|
|
|
|
|
if (token == "(")
|
|
|
|
{
|
2011-02-15 15:18:13 +00:00
|
|
|
// recursively evaluate sub-expressions
|
2011-02-14 19:27:18 +00:00
|
|
|
bool inner = eval_tokens(++pos, end, context, nick, message);
|
2011-02-15 15:18:13 +00:00
|
|
|
dbg += "( inner/" + CString(inner ? "true" : "false") + " ) ";
|
2011-02-14 19:27:18 +00:00
|
|
|
value = oper ? value && inner : value || inner;
|
2011-02-15 15:18:13 +00:00
|
|
|
|
|
|
|
// search ahead to the matching parenthesis token
|
|
|
|
unsigned int parens = 1;
|
|
|
|
while(pos != end)
|
|
|
|
{
|
|
|
|
if (*pos == "(")
|
|
|
|
{
|
|
|
|
parens++;
|
|
|
|
}
|
|
|
|
else if (*pos == ")")
|
|
|
|
{
|
|
|
|
parens--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parens == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
}
|
2011-02-14 19:27:18 +00:00
|
|
|
}
|
|
|
|
else if (token == ")")
|
|
|
|
{
|
|
|
|
pos++;
|
2011-02-15 15:18:13 +00:00
|
|
|
PutDebug(dbg);
|
2011-02-14 19:27:18 +00:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
else if (token == "and")
|
|
|
|
{
|
2011-02-15 15:18:13 +00:00
|
|
|
dbg += "and ";
|
2011-02-14 19:27:18 +00:00
|
|
|
oper = true;
|
|
|
|
}
|
|
|
|
else if (token == "or")
|
|
|
|
{
|
2011-02-15 15:18:13 +00:00
|
|
|
dbg += "or ";
|
2011-02-14 19:27:18 +00:00
|
|
|
oper = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
expr("true", true)
|
|
|
|
expr("false", false)
|
|
|
|
expr("away_only", away_only())
|
|
|
|
expr("client_count_less_than", client_count_less_than())
|
|
|
|
expr("highlight", highlight(message))
|
|
|
|
expr("idle", idle())
|
|
|
|
expr("last_active", last_active(context))
|
|
|
|
expr("last_notification", last_notification(context))
|
|
|
|
expr("nick_blacklist", nick_blacklist(nick))
|
|
|
|
expr("replied", replied(context))
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PutModule("Error: Unexpected token \"" + token + "\"");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-15 15:18:13 +00:00
|
|
|
PutDebug(dbg);
|
2011-02-14 19:27:18 +00:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef expr
|
|
|
|
|
2011-01-14 14:45:47 +00:00
|
|
|
protected:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the away status condition is met.
|
|
|
|
*
|
|
|
|
* @return True if away_only is not "yes" or away status is set
|
|
|
|
*/
|
|
|
|
bool away_only()
|
|
|
|
{
|
|
|
|
CString value = options["away_only"].AsLower();
|
2012-07-17 00:43:51 +00:00
|
|
|
return value != "yes" || GetNetwork()->IsIRCAway();
|
2011-01-14 14:45:47 +00:00
|
|
|
}
|
|
|
|
|
2011-01-14 03:00:24 +00:00
|
|
|
/**
|
|
|
|
* Check how many clients are connected to ZNC.
|
|
|
|
*
|
|
|
|
* @return Number of connected clients
|
|
|
|
*/
|
2013-10-26 09:00:33 +00:00
|
|
|
size_t client_count()
|
2011-01-14 03:00:24 +00:00
|
|
|
{
|
2012-08-31 05:09:56 +00:00
|
|
|
return GetNetwork()->GetClients().size();
|
2011-01-14 03:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the client_count condition is met.
|
|
|
|
*
|
|
|
|
* @return True if client_count is less than client_count_less_than or if client_count_less_than is zero
|
|
|
|
*/
|
|
|
|
bool client_count_less_than()
|
|
|
|
{
|
|
|
|
unsigned int value = options["client_count_less_than"].ToUInt();
|
|
|
|
return value == 0 || client_count() < value;
|
|
|
|
}
|
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
/**
|
|
|
|
* Determine if the given message matches any highlight rules.
|
|
|
|
*
|
|
|
|
* @param message Message contents
|
|
|
|
* @return True if message matches a highlight
|
|
|
|
*/
|
|
|
|
bool highlight(const CString& message)
|
|
|
|
{
|
2011-01-19 18:48:43 +00:00
|
|
|
CString msg = " " + message.AsLower() + " ";
|
|
|
|
|
|
|
|
VCString values;
|
|
|
|
options["highlight"].Split(" ", values, false);
|
2013-12-17 16:37:54 +00:00
|
|
|
values.push_back("%nick%");
|
2011-01-19 18:48:43 +00:00
|
|
|
|
|
|
|
for (VCString::iterator i = values.begin(); i != values.end(); i++)
|
|
|
|
{
|
|
|
|
CString value = i->AsLower();
|
|
|
|
char prefix = value[0];
|
2011-09-29 13:30:06 +00:00
|
|
|
bool push = true;
|
2011-01-19 18:48:43 +00:00
|
|
|
|
|
|
|
if (prefix == '-')
|
|
|
|
{
|
2011-09-29 13:30:06 +00:00
|
|
|
push = false;
|
2011-01-19 18:48:43 +00:00
|
|
|
value.LeftChomp(1);
|
|
|
|
}
|
|
|
|
else if (prefix == '_')
|
|
|
|
{
|
|
|
|
value = " " + value.LeftChomp_n(1) + " ";
|
|
|
|
}
|
|
|
|
|
2013-01-02 23:02:00 +00:00
|
|
|
// Expand substrings like %nick%
|
|
|
|
if (m_pNetwork)
|
|
|
|
{
|
|
|
|
value = m_pNetwork->ExpandString(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value = GetUser()->ExpandString(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
value = "*" + value.AsLower() + "*";
|
2011-01-19 18:48:43 +00:00
|
|
|
|
2013-01-02 18:18:37 +00:00
|
|
|
if (msg.WildCmp(value))
|
2011-01-19 18:48:43 +00:00
|
|
|
{
|
2011-09-29 13:30:06 +00:00
|
|
|
return push;
|
2011-01-19 18:48:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-18 20:24:50 +00:00
|
|
|
/**
|
|
|
|
* Check if the idle condition is met.
|
|
|
|
*
|
2013-12-12 04:10:03 +00:00
|
|
|
* @return True if idle is less than or equal to zero or elapsed time is greater than idle
|
2011-01-18 20:24:50 +00:00
|
|
|
*/
|
|
|
|
bool idle()
|
|
|
|
{
|
2013-12-13 04:15:26 +00:00
|
|
|
unsigned int value = options["idle"].ToUInt();
|
2013-10-26 09:00:33 +00:00
|
|
|
time_t now = time(NULL);
|
2013-12-13 04:15:26 +00:00
|
|
|
return value == 0 || difftime(now, idle_time) >= value;
|
2011-01-18 20:24:50 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 17:59:46 +00:00
|
|
|
/**
|
|
|
|
* Check if the last_active condition is met.
|
|
|
|
*
|
|
|
|
* @param context Channel or nick context
|
2013-12-12 04:10:03 +00:00
|
|
|
* @return True if last_active is less than or equal to zero or elapsed time is greater than last_active
|
2011-01-18 17:59:46 +00:00
|
|
|
*/
|
|
|
|
bool last_active(const CString& context)
|
|
|
|
{
|
2013-12-13 04:15:26 +00:00
|
|
|
unsigned int value = options["last_active"].ToUInt();
|
2013-10-26 09:00:33 +00:00
|
|
|
time_t now = time(NULL);
|
2013-12-13 04:15:26 +00:00
|
|
|
return value == 0
|
2011-01-18 17:59:46 +00:00
|
|
|
|| last_active_time.count(context) < 1
|
2013-12-13 04:15:26 +00:00
|
|
|
|| difftime(now, last_active_time[context]) >= value;
|
2011-01-18 17:59:46 +00:00
|
|
|
}
|
|
|
|
|
2011-01-14 15:49:08 +00:00
|
|
|
/**
|
|
|
|
* Check if the last_notification condition is met.
|
|
|
|
*
|
|
|
|
* @param context Channel or nick context
|
2013-12-12 04:10:03 +00:00
|
|
|
* @return True if last_notification is less than or equal to zero or elapsed time is greater than last_nofication
|
2011-01-14 15:49:08 +00:00
|
|
|
*/
|
|
|
|
bool last_notification(const CString& context)
|
|
|
|
{
|
2013-12-13 04:15:26 +00:00
|
|
|
unsigned int value = options["last_notification"].ToUInt();
|
2013-10-26 09:00:33 +00:00
|
|
|
time_t now = time(NULL);
|
2013-12-13 04:15:26 +00:00
|
|
|
return value == 0
|
2011-01-14 15:49:08 +00:00
|
|
|
|| last_notification_time.count(context) < 1
|
2013-12-13 04:15:26 +00:00
|
|
|
|| difftime(now, last_notification_time[context]) >= value;
|
2011-01-14 15:49:08 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 16:12:35 +00:00
|
|
|
/**
|
|
|
|
* Check if the nick_blacklist condition is met.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
|
|
|
* @return True if nick is not in the blacklist
|
|
|
|
*/
|
|
|
|
bool nick_blacklist(const CNick& nick)
|
|
|
|
{
|
|
|
|
VCString blacklist;
|
|
|
|
options["nick_blacklist"].Split(" ", blacklist, false);
|
|
|
|
|
|
|
|
CString name = nick.GetNick().AsLower();
|
|
|
|
|
|
|
|
for (VCString::iterator i = blacklist.begin(); i != blacklist.end(); i++)
|
|
|
|
{
|
2013-01-02 23:02:00 +00:00
|
|
|
CString value;
|
|
|
|
|
|
|
|
// Expand substrings like %nick%
|
|
|
|
if (m_pNetwork)
|
|
|
|
{
|
|
|
|
value = m_pNetwork->ExpandString(*i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value = GetUser()->ExpandString(*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name.WildCmp(value.AsLower()))
|
2011-01-18 16:12:35 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-26 18:09:55 +00:00
|
|
|
/**
|
|
|
|
* Check if the replied condition is met.
|
|
|
|
*
|
|
|
|
* @param context Channel or nick context
|
|
|
|
* @return True if last_reply_time > last_notification_time or if replied is not "yes"
|
|
|
|
*/
|
|
|
|
bool replied(const CString& context)
|
|
|
|
{
|
|
|
|
CString value = options["replied"].AsLower();
|
2011-01-27 02:09:34 +00:00
|
|
|
return value != "yes"
|
|
|
|
|| last_notification_time[context] == 0
|
|
|
|
|| last_notification_time[context] < last_reply_time[context];
|
2011-01-26 18:09:55 +00:00
|
|
|
}
|
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
/**
|
|
|
|
* Determine when to notify the user of a channel message.
|
|
|
|
*
|
2011-01-14 01:39:08 +00:00
|
|
|
* @param nick Nick that sent the message
|
2011-01-14 00:50:29 +00:00
|
|
|
* @param channel Channel the message was sent to
|
2011-01-14 01:39:08 +00:00
|
|
|
* @param message Message contents
|
2011-01-14 00:50:29 +00:00
|
|
|
* @return Notification should be sent
|
|
|
|
*/
|
2011-01-14 01:39:08 +00:00
|
|
|
bool notify_channel(const CNick& nick, const CChan& channel, const CString& message)
|
2011-01-14 00:50:29 +00:00
|
|
|
{
|
2011-01-18 17:59:46 +00:00
|
|
|
CString context = channel.GetName();
|
2011-02-14 19:27:18 +00:00
|
|
|
|
|
|
|
CString expression = options["channel_conditions"].AsLower();
|
|
|
|
if (expression != "all")
|
|
|
|
{
|
|
|
|
return eval(expression, context, nick, message);
|
|
|
|
}
|
|
|
|
|
2011-01-14 14:48:56 +00:00
|
|
|
return away_only()
|
|
|
|
&& client_count_less_than()
|
2011-01-14 15:49:08 +00:00
|
|
|
&& highlight(message)
|
2011-01-18 20:24:50 +00:00
|
|
|
&& idle()
|
2011-01-18 17:59:46 +00:00
|
|
|
&& last_active(context)
|
|
|
|
&& last_notification(context)
|
2011-01-18 16:12:35 +00:00
|
|
|
&& nick_blacklist(nick)
|
2011-01-26 18:09:55 +00:00
|
|
|
&& replied(context)
|
2011-01-18 16:12:35 +00:00
|
|
|
&& true;
|
2011-01-14 00:50:29 +00:00
|
|
|
}
|
|
|
|
|
2011-01-13 22:40:41 +00:00
|
|
|
/**
|
2011-01-14 00:15:17 +00:00
|
|
|
* Determine when to notify the user of a private message.
|
2011-01-13 22:40:41 +00:00
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
2011-01-14 00:15:17 +00:00
|
|
|
* @return Notification should be sent
|
2011-01-13 22:40:41 +00:00
|
|
|
*/
|
2011-02-14 19:27:18 +00:00
|
|
|
bool notify_pm(const CNick& nick, const CString& message)
|
2011-01-13 22:40:41 +00:00
|
|
|
{
|
2011-01-18 17:59:46 +00:00
|
|
|
CString context = nick.GetNick();
|
2011-02-14 19:27:18 +00:00
|
|
|
|
|
|
|
CString expression = options["query_conditions"].AsLower();
|
|
|
|
if (expression != "all")
|
|
|
|
{
|
|
|
|
return eval(expression, context, nick, message);
|
|
|
|
}
|
|
|
|
|
2011-01-14 15:49:08 +00:00
|
|
|
return away_only()
|
2011-02-14 15:20:56 +00:00
|
|
|
&& client_count_less_than()
|
2011-01-18 20:24:50 +00:00
|
|
|
&& idle()
|
2011-01-18 17:59:46 +00:00
|
|
|
&& last_active(context)
|
|
|
|
&& last_notification(context)
|
2011-01-18 16:12:35 +00:00
|
|
|
&& nick_blacklist(nick)
|
2011-01-26 18:09:55 +00:00
|
|
|
&& replied(context)
|
2011-01-18 16:12:35 +00:00
|
|
|
&& true;
|
2011-01-13 22:40:41 +00:00
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
protected:
|
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
/**
|
|
|
|
* Handle the plugin being loaded. Retrieve plugin config values.
|
|
|
|
*
|
|
|
|
* @param args Plugin arguments
|
|
|
|
* @param message Message to show the user after loading
|
|
|
|
*/
|
2011-01-13 21:26:31 +00:00
|
|
|
bool OnLoad(const CString& args, CString& message)
|
|
|
|
{
|
2011-01-14 03:52:50 +00:00
|
|
|
for (MCString::iterator i = defaults.begin(); i != defaults.end(); i++)
|
2011-01-14 00:50:29 +00:00
|
|
|
{
|
2011-01-14 02:59:44 +00:00
|
|
|
CString value = GetNV(i->first);
|
|
|
|
if (value != "")
|
|
|
|
{
|
|
|
|
options[i->first] = value;
|
|
|
|
}
|
2011-01-14 03:52:50 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
options[i->first] = defaults[i->first];
|
|
|
|
}
|
2011-01-14 00:50:29 +00:00
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-14 01:39:08 +00:00
|
|
|
/**
|
|
|
|
* Handle channel messages.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
|
|
|
* @param channel Channel the message was sent to
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnChanMsg(CNick& nick, CChan& channel, CString& message)
|
|
|
|
{
|
|
|
|
if (notify_channel(nick, channel, message))
|
|
|
|
{
|
|
|
|
CString title = "Highlight";
|
|
|
|
CString msg = channel.GetName();
|
2013-02-13 01:55:38 +00:00
|
|
|
msg += ": [" + nick.GetNick();
|
|
|
|
msg += "] " + message;
|
2011-01-14 01:39:08 +00:00
|
|
|
|
2011-01-14 15:49:08 +00:00
|
|
|
send_message(msg, title, channel.GetName());
|
2011-01-14 01:39:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle channel actions.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the action
|
|
|
|
* @param channel Channel the message was sent to
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnChanAction(CNick& nick, CChan& channel, CString& message)
|
|
|
|
{
|
|
|
|
if (notify_channel(nick, channel, message))
|
|
|
|
{
|
|
|
|
CString title = "Highlight";
|
|
|
|
CString msg = channel.GetName();
|
|
|
|
msg += ": " + nick.GetNick();
|
|
|
|
msg += " " + message;
|
|
|
|
|
2011-01-14 15:49:08 +00:00
|
|
|
send_message(msg, title, channel.GetName());
|
2011-01-14 01:39:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
2011-01-13 22:40:41 +00:00
|
|
|
/**
|
2011-01-14 00:15:17 +00:00
|
|
|
* Handle a private message.
|
2011-01-13 22:40:41 +00:00
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnPrivMsg(CNick& nick, CString& message)
|
|
|
|
{
|
2011-02-14 19:27:18 +00:00
|
|
|
if (notify_pm(nick, message))
|
2011-01-14 00:15:17 +00:00
|
|
|
{
|
|
|
|
CString title = "Private Message";
|
|
|
|
CString msg = "From " + nick.GetNick();
|
|
|
|
msg += ": " + message;
|
|
|
|
|
2011-01-14 15:49:08 +00:00
|
|
|
send_message(msg, title, nick.GetNick());
|
2011-01-14 00:15:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a private action.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the action
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnPrivAction(CNick& nick, CString& message)
|
|
|
|
{
|
2011-02-14 19:27:18 +00:00
|
|
|
if (notify_pm(nick, message))
|
2011-01-14 00:15:17 +00:00
|
|
|
{
|
|
|
|
CString title = "Private Message";
|
|
|
|
CString msg = "* " + nick.GetNick();
|
|
|
|
msg += " " + message;
|
|
|
|
|
2011-01-14 15:49:08 +00:00
|
|
|
send_message(msg, title, nick.GetNick());
|
2011-01-14 00:15:17 +00:00
|
|
|
}
|
|
|
|
|
2011-01-13 22:40:41 +00:00
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
2011-01-18 17:59:46 +00:00
|
|
|
/**
|
|
|
|
* Handle a message sent by the user.
|
|
|
|
*
|
|
|
|
* @param target Target channel or nick
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnUserMsg(CString& target, CString& message)
|
|
|
|
{
|
2011-01-26 18:09:55 +00:00
|
|
|
last_reply_time[target] = last_active_time[target] = idle_time = time(NULL);
|
2011-01-18 17:59:46 +00:00
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle an action sent by the user.
|
|
|
|
*
|
|
|
|
* @param target Target channel or nick
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnUserAction(CString& target, CString& message)
|
|
|
|
{
|
2011-01-26 18:09:55 +00:00
|
|
|
last_reply_time[target] = last_active_time[target] = idle_time = time(NULL);
|
2011-01-18 20:24:50 +00:00
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the user joining a channel.
|
|
|
|
*
|
|
|
|
* @param channel Channel name
|
|
|
|
* @param key Channel key
|
|
|
|
*/
|
|
|
|
EModRet OnUserJoin(CString& channel, CString& key)
|
|
|
|
{
|
|
|
|
idle_time = time(NULL);
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the user parting a channel.
|
|
|
|
*
|
|
|
|
* @param channel Channel name
|
|
|
|
* @param message Part message
|
|
|
|
*/
|
|
|
|
EModRet OnUserPart(CString& channel, CString& message)
|
|
|
|
{
|
|
|
|
idle_time = time(NULL);
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the user setting the channel topic.
|
|
|
|
*
|
|
|
|
* @param channel Channel name
|
|
|
|
* @param topic Topic message
|
|
|
|
*/
|
|
|
|
EModRet OnUserTopic(CString& channel, CString& topic)
|
|
|
|
{
|
|
|
|
idle_time = time(NULL);
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the user requesting the channel topic.
|
|
|
|
*
|
|
|
|
* @param channel Channel name
|
|
|
|
*/
|
|
|
|
EModRet OnUserTopicRequest(CString& channel)
|
|
|
|
{
|
|
|
|
idle_time = time(NULL);
|
2011-01-18 17:59:46 +00:00
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
/**
|
2011-09-29 13:30:06 +00:00
|
|
|
* Handle direct commands to the *push virtual user.
|
2011-01-13 21:26:31 +00:00
|
|
|
*
|
|
|
|
* @param command Command string
|
|
|
|
*/
|
|
|
|
void OnModCommand(const CString& command)
|
|
|
|
{
|
|
|
|
VCString tokens;
|
2013-10-26 09:00:33 +00:00
|
|
|
CString::size_type token_count = command.Split(" ", tokens, false);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 03:00:08 +00:00
|
|
|
if (token_count < 1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
CString action = tokens[0].AsLower();
|
|
|
|
|
|
|
|
// SET command
|
|
|
|
if (action == "set")
|
|
|
|
{
|
2011-01-18 15:44:23 +00:00
|
|
|
if (token_count < 3)
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
|
|
|
PutModule("Usage: set <option> <value>");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
2011-01-18 15:44:23 +00:00
|
|
|
CString value = command.Token(2, true, " ");
|
2011-01-14 00:50:29 +00:00
|
|
|
MCString::iterator pos = options.find(option);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
if (pos == options.end())
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
2011-01-14 00:50:29 +00:00
|
|
|
PutModule("Error: invalid option name");
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-09-29 14:21:15 +00:00
|
|
|
value.Trim();
|
|
|
|
|
2011-02-14 19:27:18 +00:00
|
|
|
if (option == "channel_conditions" || option == "query_conditions")
|
|
|
|
{
|
|
|
|
if (value != "all")
|
|
|
|
{
|
|
|
|
eval(value);
|
|
|
|
}
|
|
|
|
}
|
2011-09-29 14:21:15 +00:00
|
|
|
else if (option == "service")
|
|
|
|
{
|
|
|
|
value.MakeLower();
|
|
|
|
|
2013-08-27 23:46:40 +00:00
|
|
|
if (value == "pushbullet")
|
2013-05-03 01:35:36 +00:00
|
|
|
{
|
2013-05-10 02:35:54 +00:00
|
|
|
PutModule("Note: Pushbullet requires setting both 'target' (to device id) and 'secret' (to api key) options");
|
2013-05-03 01:35:36 +00:00
|
|
|
}
|
2011-09-29 14:21:15 +00:00
|
|
|
else if (value == "boxcar")
|
|
|
|
{
|
|
|
|
PutModule("Note: Boxcar requires setting the 'username' option");
|
|
|
|
}
|
2011-10-06 18:54:17 +00:00
|
|
|
else if (value == "nma")
|
|
|
|
{
|
|
|
|
PutModule("Note: NMA requires setting the 'secret' option");
|
|
|
|
}
|
2012-07-24 05:07:28 +00:00
|
|
|
else if (value == "pushover")
|
|
|
|
{
|
2013-07-31 16:33:52 +00:00
|
|
|
PutModule("Note: Pushover requires setting both the 'username' (to user key) and the 'secret' (to application api key) option");
|
2012-07-24 05:07:28 +00:00
|
|
|
}
|
2011-10-06 18:43:54 +00:00
|
|
|
else if (value == "prowl")
|
|
|
|
{
|
|
|
|
PutModule("Note: Prowl requires setting the 'secret' option");
|
|
|
|
}
|
2012-01-02 09:51:37 +00:00
|
|
|
else if (value == "supertoasty")
|
|
|
|
{
|
|
|
|
PutModule("Note: Supertoasty requires setting the 'secret' option with device id");
|
|
|
|
}
|
2013-01-09 22:26:54 +00:00
|
|
|
else if (value == "url")
|
|
|
|
{
|
2013-01-09 23:01:51 +00:00
|
|
|
PutModule("Note: URL requires setting the 'message_uri' option with the full URL");
|
2014-01-22 18:47:33 +00:00
|
|
|
}
|
|
|
|
else if (value == "airgram")
|
|
|
|
{
|
|
|
|
PutModule("Note: Airgram requires setting the 'target' with the email address of the recipient");
|
2013-01-09 22:26:54 +00:00
|
|
|
}
|
2011-09-29 14:21:15 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
PutModule("Error: unknown service name");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-02-14 19:27:18 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
options[option] = value;
|
2011-01-19 17:54:05 +00:00
|
|
|
SetNV(option, options[option]);
|
2012-10-26 18:26:38 +00:00
|
|
|
|
|
|
|
PutModule("Ok");
|
2011-01-19 16:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// APPEND command
|
|
|
|
else if (action == "append")
|
|
|
|
{
|
|
|
|
if (token_count < 3)
|
|
|
|
{
|
|
|
|
PutModule("Usage: append <option> <value>");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
|
|
|
CString value = command.Token(2, true, " ");
|
|
|
|
MCString::iterator pos = options.find(option);
|
|
|
|
|
|
|
|
if (pos == options.end())
|
|
|
|
{
|
|
|
|
PutModule("Error: invalid option name");
|
|
|
|
}
|
2011-09-29 14:21:15 +00:00
|
|
|
else if (option == "service")
|
|
|
|
{
|
|
|
|
PutModule("Error: cannot append to this option");
|
|
|
|
}
|
2011-01-19 16:36:07 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
options[option] += " " + value;
|
|
|
|
options[option].Trim();
|
2011-01-19 17:54:05 +00:00
|
|
|
SetNV(option, options[option]);
|
2012-10-26 18:26:38 +00:00
|
|
|
|
|
|
|
PutModule("Ok");
|
2011-01-19 17:54:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// PREPEND command
|
|
|
|
else if (action == "prepend")
|
|
|
|
{
|
|
|
|
if (token_count < 3)
|
|
|
|
{
|
|
|
|
PutModule("Usage: prepend <option> <value>");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
|
|
|
CString value = command.Token(2, true, " ");
|
|
|
|
MCString::iterator pos = options.find(option);
|
|
|
|
|
|
|
|
if (pos == options.end())
|
|
|
|
{
|
|
|
|
PutModule("Error: invalid option name");
|
|
|
|
}
|
2011-09-29 14:21:15 +00:00
|
|
|
else if (option == "service")
|
|
|
|
{
|
|
|
|
PutModule("Error: cannot prepend to this option");
|
|
|
|
}
|
2011-01-19 17:54:05 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
options[option] = value + " " + options[option];
|
|
|
|
options[option].Trim();
|
|
|
|
SetNV(option, options[option]);
|
2012-10-26 18:26:38 +00:00
|
|
|
|
|
|
|
PutModule("Ok");
|
2011-01-14 00:50:29 +00:00
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
2011-01-14 03:52:50 +00:00
|
|
|
// UNSET command
|
|
|
|
else if (action == "unset")
|
|
|
|
{
|
|
|
|
if (token_count != 2)
|
|
|
|
{
|
|
|
|
PutModule("Usage: unset <option>");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
|
|
|
MCString::iterator pos = options.find(option);
|
|
|
|
|
|
|
|
if (pos == options.end())
|
|
|
|
{
|
|
|
|
PutModule("Error: invalid option name");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
options[option] = defaults[option];
|
|
|
|
DelNV(option);
|
2012-10-26 18:26:38 +00:00
|
|
|
|
|
|
|
PutModule("Ok");
|
2011-01-14 03:52:50 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
// GET command
|
|
|
|
else if (action == "get")
|
|
|
|
{
|
2011-01-14 03:03:29 +00:00
|
|
|
if (token_count > 2)
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
2011-01-14 03:03:29 +00:00
|
|
|
PutModule("Usage: get [<option>]");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token_count < 2)
|
|
|
|
{
|
|
|
|
CTable table;
|
|
|
|
|
|
|
|
table.AddColumn("Option");
|
|
|
|
table.AddColumn("Value");
|
|
|
|
|
|
|
|
for (MCString::iterator i = options.begin(); i != options.end(); i++)
|
|
|
|
{
|
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Option", i->first);
|
|
|
|
table.SetCell("Value", i->second);
|
|
|
|
}
|
|
|
|
|
|
|
|
PutModule(table);
|
2011-01-13 21:26:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
2011-01-14 00:50:29 +00:00
|
|
|
MCString::iterator pos = options.find(option);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
if (pos == options.end())
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
2011-01-14 00:50:29 +00:00
|
|
|
PutModule("Error: invalid option name");
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-07-03 17:59:48 +00:00
|
|
|
PutModule(option + CString(": ") + options[option]);
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-28 20:23:00 +00:00
|
|
|
// SAVE command
|
|
|
|
else if (action == "save")
|
|
|
|
{
|
|
|
|
if (token_count < 2)
|
|
|
|
{
|
|
|
|
PutModule("Usage: save <filepath>");
|
|
|
|
}
|
|
|
|
|
|
|
|
CString file_path = command.Token(1, true, " ");
|
2012-03-04 21:03:07 +00:00
|
|
|
int status = options.WriteToDisk(file_path);
|
2011-09-28 20:23:00 +00:00
|
|
|
|
|
|
|
if (status == MCString::MCS_SUCCESS)
|
|
|
|
{
|
|
|
|
PutModule("Options saved to " + file_path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (status)
|
|
|
|
{
|
|
|
|
case MCString::MCS_EOPEN:
|
|
|
|
case MCString::MCS_EWRITE:
|
|
|
|
case MCString::MCS_EWRITEFIL:
|
|
|
|
PutModule("Failed to save options to " + file_path);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PutModule("Failure");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// LOAD command
|
|
|
|
else if (action == "load")
|
|
|
|
{
|
|
|
|
if (token_count < 2)
|
|
|
|
{
|
|
|
|
PutModule("Usage: load <filename>");
|
|
|
|
}
|
|
|
|
|
|
|
|
CString file_path = command.Token(1, true, " ");
|
2011-09-29 13:30:06 +00:00
|
|
|
|
2011-09-28 20:23:00 +00:00
|
|
|
if (!CFile::Exists(file_path))
|
|
|
|
{
|
|
|
|
PutModule("File does not exist: " + file_path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-04 21:03:07 +00:00
|
|
|
int status = options.ReadFromDisk(file_path);
|
2011-09-28 20:23:00 +00:00
|
|
|
|
|
|
|
if (status == MCString::MCS_SUCCESS)
|
|
|
|
{
|
|
|
|
PutModule("Options loaded from " + file_path);
|
2011-09-29 13:54:44 +00:00
|
|
|
|
|
|
|
// Restore any defaults that aren't in the loaded dictionary,
|
|
|
|
// and save loaded options to ZNC's data store
|
|
|
|
for (MCString::iterator i = defaults.begin(); i != defaults.end(); i++)
|
|
|
|
{
|
|
|
|
CString option = i->first;
|
|
|
|
MCString::iterator pos = options.find(option);
|
|
|
|
|
|
|
|
if (pos == options.end())
|
|
|
|
{
|
|
|
|
options[option] = defaults[option];
|
|
|
|
DelNV(option);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetNV(option, options[option]);
|
|
|
|
}
|
|
|
|
}
|
2011-09-28 20:23:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (status)
|
|
|
|
{
|
|
|
|
case MCString::MCS_EOPEN:
|
|
|
|
case MCString::MCS_EREADFIL:
|
|
|
|
PutModule("Failed to read options from " + file_path);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PutModule("Failure");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-14 03:03:49 +00:00
|
|
|
// STATUS command
|
|
|
|
else if (action == "status")
|
|
|
|
{
|
|
|
|
CTable table;
|
|
|
|
|
|
|
|
table.AddColumn("Condition");
|
|
|
|
table.AddColumn("Status");
|
|
|
|
|
2011-01-14 14:45:47 +00:00
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "away");
|
2012-07-17 00:43:51 +00:00
|
|
|
table.SetCell("Status", GetNetwork()->IsIRCAway() ? "yes" : "no");
|
2011-01-14 14:45:47 +00:00
|
|
|
|
2011-01-14 03:03:49 +00:00
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "client_count");
|
|
|
|
table.SetCell("Status", CString(client_count()));
|
|
|
|
|
2013-10-26 09:00:33 +00:00
|
|
|
time_t now = time(NULL);
|
|
|
|
time_t ago = now - idle_time;
|
2011-01-18 20:24:50 +00:00
|
|
|
|
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "idle");
|
|
|
|
table.SetCell("Status", CString(ago) + " seconds");
|
|
|
|
|
2011-01-18 21:14:16 +00:00
|
|
|
if (token_count > 1)
|
|
|
|
{
|
|
|
|
CString context = tokens[1];
|
|
|
|
|
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "last_active");
|
|
|
|
|
|
|
|
if (last_active_time.count(context) < 1)
|
|
|
|
{
|
|
|
|
table.SetCell("Status", "n/a");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ago = now - last_active_time[context];
|
|
|
|
table.SetCell("Status", CString(ago) + " seconds");
|
|
|
|
}
|
|
|
|
|
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "last_notification");
|
|
|
|
|
|
|
|
if (last_notification_time.count(context) < 1)
|
|
|
|
{
|
|
|
|
table.SetCell("Status", "n/a");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ago = now - last_notification_time[context];
|
|
|
|
table.SetCell("Status", CString(ago) + " seconds");
|
|
|
|
}
|
2011-01-26 18:09:55 +00:00
|
|
|
|
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "replied");
|
|
|
|
table.SetCell("Status", replied(context) ? "yes" : "no");
|
2011-01-18 21:14:16 +00:00
|
|
|
}
|
|
|
|
|
2011-01-14 03:03:49 +00:00
|
|
|
PutModule(table);
|
|
|
|
}
|
2011-10-25 19:40:45 +00:00
|
|
|
// SUBSCRIBE command
|
|
|
|
else if (action == "subscribe")
|
|
|
|
{
|
|
|
|
// Set up the connection profile
|
|
|
|
CString service = options["service"];
|
|
|
|
bool use_post = true;
|
|
|
|
int use_port = 443;
|
|
|
|
bool use_ssl = true;
|
|
|
|
CString service_host;
|
|
|
|
CString service_url;
|
|
|
|
CString service_auth;
|
|
|
|
MCString params;
|
|
|
|
|
|
|
|
if (service == "boxcar")
|
|
|
|
{
|
|
|
|
if (options["username"] == "")
|
|
|
|
{
|
|
|
|
PutModule("Error: username not set");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString boxcar_api_key = "puSd2qp2gCDZO7nWkvb9";
|
|
|
|
|
|
|
|
service_host = "boxcar.io";
|
|
|
|
service_url = "/devices/providers/" + boxcar_api_key + "/notifications/subscribe";
|
|
|
|
|
|
|
|
params["email"] = options["username"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PutModule("Error: service does not support subscribe command");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
#ifdef USE_CURL
|
2013-11-14 20:08:35 +00:00
|
|
|
make_curl_request(service_host, service_url, service_auth, params, use_port, use_ssl, use_post, options["debug"] == "on");
|
2013-10-28 23:55:18 +00:00
|
|
|
#else
|
2011-10-25 19:40:45 +00:00
|
|
|
// Create the socket connection, write to it, and add it to the queue
|
|
|
|
CPushSocket *sock = new CPushSocket(this);
|
|
|
|
sock->Connect(service_host, use_port, use_ssl);
|
|
|
|
sock->Request(use_post, service_host, service_url, params, service_auth);
|
|
|
|
AddSocket(sock);
|
2013-10-28 23:55:18 +00:00
|
|
|
#endif
|
2012-10-26 18:26:38 +00:00
|
|
|
|
|
|
|
PutModule("Ok");
|
2011-10-25 19:40:45 +00:00
|
|
|
}
|
2011-01-14 03:51:53 +00:00
|
|
|
// SEND command
|
|
|
|
else if (action == "send")
|
|
|
|
{
|
|
|
|
CString message = command.Token(1, true, " ", true);
|
|
|
|
send_message(message);
|
2012-10-26 18:26:38 +00:00
|
|
|
|
|
|
|
PutModule("Ok");
|
2011-01-14 03:51:53 +00:00
|
|
|
}
|
2011-01-14 03:58:49 +00:00
|
|
|
// HELP command
|
|
|
|
else if (action == "help")
|
|
|
|
{
|
2011-09-29 13:30:06 +00:00
|
|
|
PutModule("View the detailed documentation at https://github.com/jreese/znc-push/blob/master/README.md");
|
2011-01-14 03:58:49 +00:00
|
|
|
}
|
2013-07-31 17:35:53 +00:00
|
|
|
// VERSION command
|
|
|
|
else if (action == "version")
|
|
|
|
{
|
|
|
|
PutModule("znc-push " + CString(PUSHVERSION));
|
|
|
|
}
|
2011-02-14 19:27:18 +00:00
|
|
|
// EVAL command
|
|
|
|
else if (action == "eval")
|
|
|
|
{
|
|
|
|
CString value = command.Token(1, true, " ");
|
|
|
|
PutModule(eval(value) ? "true" : "false");
|
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
else
|
|
|
|
{
|
2011-01-14 03:58:49 +00:00
|
|
|
PutModule("Error: invalid command, try `help`");
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
2011-01-11 03:33:13 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-09-29 20:36:28 +00:00
|
|
|
/**
|
2013-10-28 23:55:18 +00:00
|
|
|
* Build a query string from a dictionary of request parameters.
|
2011-09-29 20:36:28 +00:00
|
|
|
*
|
2013-10-28 23:55:18 +00:00
|
|
|
* @param params Request parameters
|
|
|
|
* @return query string
|
2011-09-29 20:36:28 +00:00
|
|
|
*/
|
2013-10-28 23:55:18 +00:00
|
|
|
CString build_query_string(MCString& params)
|
2011-09-29 20:36:28 +00:00
|
|
|
{
|
|
|
|
bool more = false;
|
|
|
|
CString query;
|
|
|
|
CString key;
|
|
|
|
CString value;
|
2013-10-28 23:55:18 +00:00
|
|
|
for (MCString::iterator param = params.begin(); param != params.end(); param++)
|
2011-09-29 20:36:28 +00:00
|
|
|
{
|
|
|
|
key = urlencode(param->first);
|
|
|
|
value = urlencode(param->second);
|
|
|
|
|
|
|
|
if (more)
|
|
|
|
{
|
|
|
|
query += "&" + key + "=" + value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
query += key + "=" + value;
|
|
|
|
more = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
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,
|
2013-11-14 20:08:35 +00:00
|
|
|
bool use_ssl, bool use_post, bool debug)
|
2013-10-28 23:55:18 +00:00
|
|
|
{
|
|
|
|
CURL *curl;
|
|
|
|
CURLcode result;
|
|
|
|
|
|
|
|
curl = curl_easy_init();
|
|
|
|
|
2013-11-17 16:18:42 +00:00
|
|
|
CString user_agent = "ZNC Push/" + CString(PUSHVERSION);
|
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
CString url = CString(use_ssl ? "https" : "http") + "://" + service_host + service_url;
|
|
|
|
CString query = build_query_string(params);
|
|
|
|
|
2013-11-14 20:08:35 +00:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
|
|
|
}
|
|
|
|
|
2013-11-17 16:11:25 +00:00
|
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
|
|
|
|
2013-10-28 23:55:18 +00:00
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.data());
|
|
|
|
curl_easy_setopt(curl, CURLOPT_PORT, port);
|
2013-11-17 16:18:42 +00:00
|
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
|
2013-10-28 23:55:18 +00:00
|
|
|
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);
|
|
|
|
|
2011-09-29 20:36:28 +00:00
|
|
|
parent->PutDebug("Query string: " + query);
|
|
|
|
|
|
|
|
// Request headers and POST body
|
|
|
|
CString request;
|
|
|
|
|
|
|
|
if (post)
|
|
|
|
{
|
|
|
|
request += "POST " + url + " HTTP/1.1" + crlf;
|
|
|
|
request += "Content-Type: application/x-www-form-urlencoded" + crlf;
|
|
|
|
request += "Content-Length: " + CString(query.length()) + crlf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
request += "GET " + url + "?" + query + " HTTP/1.1" + crlf;
|
|
|
|
}
|
|
|
|
|
2011-12-15 17:49:46 +00:00
|
|
|
request += "Host: " + host + crlf;
|
|
|
|
request += "Connection: close" + crlf;
|
|
|
|
request += "User-Agent: " + user_agent + crlf;
|
|
|
|
|
2011-09-29 20:36:28 +00:00
|
|
|
if (auth != "")
|
|
|
|
{
|
|
|
|
request += "Authorization: Basic " + auth + crlf;
|
|
|
|
}
|
|
|
|
|
|
|
|
request += crlf;
|
|
|
|
|
|
|
|
if (post)
|
|
|
|
{
|
2011-12-15 17:49:46 +00:00
|
|
|
request += query;
|
2011-09-29 20:36:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Write(request);
|
|
|
|
parent->PutDebug("Request sending");
|
|
|
|
}
|
|
|
|
|
2011-09-29 19:20:53 +00:00
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2011-09-29 20:36:28 +00:00
|
|
|
parent->PutDebug("Disconnected.");
|
|
|
|
Close(CSocket::CLT_AFTERWRITE);
|
2011-09-29 19:20:53 +00:00
|
|
|
}
|
2013-10-28 23:55:18 +00:00
|
|
|
#endif // USE_CURL
|
2011-09-29 19:20:53 +00:00
|
|
|
|
2011-09-29 13:30:06 +00:00
|
|
|
MODULEDEFS(CPushMod, "Send highlights and personal messages to a push notification service")
|