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 " ] = " " ;
2014-04-21 13:36:06 +00:00
defaults [ " message_uri_post " ] = " no " ;
2013-04-29 20:03:45 +00:00
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 " ] = " " ;
2015-07-05 19:39:30 +00:00
defaults [ " network_blacklist " ] = " " ;
2011-01-26 18:09:55 +00:00
defaults [ " replied " ] = " yes " ;
2015-03-26 03:57:12 +00:00
defaults [ " context " ] = " * " ;
2011-01-14 03:27:26 +00:00
2014-08-20 00:40:59 +00:00
// Proxy, for libcurl
defaults [ " proxy " ] = " " ;
defaults [ " proxy_ssl_verify " ] = " yes " ;
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 " ] ;
2014-05-30 20:50:18 +00:00
replace [ " {network} " ] = GetNetwork ( ) - > GetName ( ) ;
2015-02-21 22:14:44 +00:00
replace [ " {target} " ] = options [ " target " ] ;
2013-01-09 21:27:53 +00:00
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
{
2014-05-16 18:44:37 +00:00
if ( options [ " secret " ] = = " " )
2013-05-03 01:35:36 +00:00
{
2014-05-16 18:44:37 +00:00
PutModule ( " Error: 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 " ;
2014-05-16 18:44:37 +00:00
service_url = " /v2/pushes " ;
2013-05-03 01:35:36 +00:00
// BASIC auth, base64-encoded APIKey:
service_auth = options [ " secret " ] + CString ( " : " ) ;
2014-02-19 19:12:36 +00:00
2014-05-16 18:44:37 +00:00
if ( options [ " target " ] ! = " " )
{
params [ " device_iden " ] = options [ " target " ] ;
}
2014-08-27 08:28:14 +00:00
if ( message_uri = = " " )
{
params [ " type " ] = " note " ;
} else {
params [ " type " ] = " link " ;
params [ " url " ] = message_uri ;
}
2013-05-03 01:35:36 +00:00
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
}
2014-05-16 18:44:30 +00:00
else if ( service = = " boxcar2 " )
{
if ( options [ " secret " ] = = " " )
{
PutModule ( " Error: secret not set to apikey " ) ;
return ;
}
service_host = " new.boxcar.io " ;
service_url = " /api/notifications " ;
params [ " user_credentials " ] = options [ " secret " ] ;
params [ " notification[title] " ] = message_title ;
params [ " notification[long_message] " ] = message_content ;
if ( options [ " message_sound " ] ! = " " )
{
params [ " notification[sound] " ] = options [ " message_sound " ] ;
}
}
2011-10-06 18:54:17 +00:00
else if ( service = = " nma " )
{
if ( options [ " secret " ] = = " " )
{
PutModule ( " Error: secret not set " ) ;
return ;
}
2014-02-17 18:38:01 +00:00
if ( options [ " message_priority " ] ! = " " )
{
params [ " priority " ] = options [ " message_priority " ] ;
}
2011-10-06 18:54:17 +00:00
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
}
2015-07-14 07:25:52 +00:00
else if ( service = = " pushalot " )
{
if ( options [ " secret " ] = = " " )
{
PutModule ( " Error: secret (authorization token) not set " ) ;
return ;
}
service_host = " pushalot.com " ;
service_url = " /api/sendmessage " ;
params [ " AuthorizationToken " ] = options [ " secret " ] ;
params [ " Title " ] = message_title ;
params [ " Body " ] = message_content ;
if ( message_uri ! = " " )
{
params [ " Link " ] = message_uri ;
}
if ( options [ " message_uri_title " ] ! = " " )
{
params [ " LinkTitle " ] = options [ " message_uri_title " ] ;
}
}
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 ;
2014-02-11 01:49:08 +00:00
params [ " image " ] = " https://raw2.github.com/jreese/znc-push/master/logo.png " ;
2012-01-02 09:51:37 +00:00
params [ " sender " ] = " ZNC Push " ;
}
2014-02-19 19:12:36 +00:00
else if ( service = = " faast " )
2014-02-11 01:49:08 +00:00
{
if ( options [ " secret " ] = = " " )
{
PutModule ( " Error: secret not set to apikey " ) ;
return ;
}
service_host = " www.appnotifications.com " ;
service_url = " /account/notifications.json " ;
params [ " user_credentials " ] = options [ " secret " ] ;
2014-02-19 19:12:36 +00:00
params [ " notification[title] " ] = message_title ;
2014-02-11 01:49:08 +00:00
params [ " notification[subtitle] " ] = context ;
params [ " notification[message] " ] = message_content ;
params [ " notification[long_message] " ] = message_content ;
params [ " notification[icon_url] " ] = " https://raw2.github.com/jreese/znc-push/master/logo.png " ;
if ( options [ " message_sound " ] ! = " " )
{
params [ " notification[sound] " ] = options [ " message_sound " ] ;
}
2014-02-19 19:12:36 +00:00
if ( options [ " message_uri " ] ! = " " )
{
params [ " notification[run_command] " ] = options [ " message_uri " ] ;
}
}
2014-10-16 01:14:29 +00:00
else if ( service = = " nexmo " )
{
if ( options [ " username " ] = = " " )
{
2014-10-16 01:33:15 +00:00
PutModule ( " Error: username (api key) not set " ) ;
2014-10-16 01:14:29 +00:00
return ;
}
if ( options [ " secret " ] = = " " )
{
2014-10-16 01:33:15 +00:00
PutModule ( " Error: secret (api secret) not set " ) ;
return ;
}
if ( options [ " target " ] = = " " )
{
PutModule ( " Error: destination mobile number (in international format) not set " ) ;
2014-10-16 01:14:29 +00:00
return ;
}
service_host = " rest.nexmo.com " ;
service_url = " /sms/json " ;
params [ " api_secret " ] = options [ " secret " ] ;
params [ " api_key " ] = options [ " username " ] ;
params [ " from " ] = message_title ;
2014-10-16 01:33:15 +00:00
params [ " to " ] = options [ " target " ] ;
2014-10-16 01:14:29 +00:00
params [ " text " ] = message_content ;
2014-10-16 01:33:15 +00:00
2014-10-16 01:14:29 +00:00
}
2014-02-19 19:12:36 +00:00
else if ( service = = " url " )
2013-01-09 22:26:54 +00:00
{
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 ;
}
2014-04-21 13:36:06 +00:00
if ( options [ " message_uri_post " ] ! = " yes " )
{
use_post = false ;
}
2013-01-09 22:26:54 +00:00
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 ;
}
2014-04-21 13:36:06 +00:00
// HTTP basic auth
2014-04-23 18:33:12 +00:00
if ( options [ " username " ] ! = " " | | options [ " secret " ] ! = " " )
2014-04-21 13:36:06 +00:00
{
service_auth = options [ " username " ] + CString ( " : " ) + options [ " secret " ] ;
}
2013-01-09 22:26:54 +00:00
// 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 " ;
2014-08-20 16:31:04 +00:00
if ( options [ " username " ] ! = " " & & options [ " secret " ] ! = " " )
{
service_url = " /1/send " ;
service_auth = options [ " username " ] + CString ( " : " ) + options [ " secret " ] ;
}
else
{
service_url = " /1/send_as_guest " ;
}
2014-01-22 18:47:33 +00:00
params [ " email " ] = options [ " target " ] ;
params [ " msg " ] = message_content ;
}
2015-02-21 22:14:44 +00:00
else if ( service = = " slack " )
{
if ( options [ " secret " ] = = " " )
{
PutModule ( " Error: secret (from webhook, e.g. T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX) not set " ) ;
return ;
}
if ( options [ " target " ] = = " " )
{
PutModule ( " Error: target (channel or username) not set " ) ;
return ;
}
service_host = " hooks.slack.com " ;
service_url = " /services/ " + options [ " secret " ] ;
if ( options [ " username " ] ! = " " )
{
params [ " username " ] = options [ " username " ] ;
}
2015-02-26 10:21:01 +00:00
params [ " payload " ] = expand ( " { \" channel \" : \" {target} \" , \" text \" : \" *{title}*: {message} \" } " , replace ) ;
2015-02-21 22:14:44 +00:00
PutDebug ( " payload: " + params [ " payload " ] ) ;
}
2011-09-29 19:42:33 +00:00
else
{
PutModule ( " Error: service type not selected " ) ;
return ;
}
2011-01-11 03:33:13 +00:00
2014-01-28 22:35:30 +00:00
PutDebug ( " service: " + service ) ;
PutDebug ( " service_host: " + service_host ) ;
PutDebug ( " service_url: " + service_url ) ;
PutDebug ( " service_auth: " + service_auth ) ;
PutDebug ( " use_port: " + CString ( use_port ) ) ;
PutDebug ( " use_ssl: " + CString ( use_ssl ? 1 : 0 ) ) ;
PutDebug ( " use_post: " + CString ( use_post ? 1 : 0 ) ) ;
2013-10-28 23:55:18 +00:00
# ifdef USE_CURL
2014-01-28 22:35:30 +00:00
PutDebug ( " using libcurl " ) ;
2014-08-20 00:40:59 +00:00
params [ " proxy " ] = options [ " proxy " ] ;
params [ " proxy_ssl_verify " ] = options [ " proxy_ssl_verify " ] ;
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
2014-01-28 22:35:30 +00:00
PutDebug ( " NOT using libcurl " ) ;
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 ) ;
2014-09-02 19:08:06 +00:00
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 ) )
2015-07-05 19:39:30 +00:00
expr ( " network_blacklist " , network_blacklist ( ) )
2011-02-14 19:27:18 +00:00
expr ( " replied " , replied ( context ) )
2015-03-26 03:57:12 +00:00
expr ( " context " , context_filter ( context ) )
2011-02-14 19:27:18 +00:00
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 ;
}
2015-03-26 03:57:12 +00:00
/**
* Determine if the given context matches any context rules .
*
* @ param context The context of a message
* @ return True if context matches the filter
*/
bool context_filter ( const CString & context )
{
if ( context = = " all " | | context = = " * " )
return true ;
VCString values ;
options [ " context " ] . Split ( " " , values , false ) ;
for ( VCString : : iterator i = values . begin ( ) ; i ! = values . end ( ) ; i + + )
{
CString value = i - > AsLower ( ) ;
char prefix = value [ 0 ] ;
bool push = true ;
if ( prefix = = ' - ' )
{
push = false ;
value . LeftChomp ( 1 ) ;
}
if ( value ! = " * " )
{
value = " * " + value . AsLower ( ) + " * " ;
}
if ( context . WildCmp ( value ) )
{
return push ;
}
}
return false ;
}
2011-01-14 01:58:13 +00:00
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 ;
}
2015-07-05 19:39:30 +00:00
/**
* Check if the network_blacklist condition is met .
*
* @ param network Network that the message was received on
* @ return True if network is not in the blacklist
*/
bool network_blacklist ( )
{
VCString blacklist ;
options [ " network_blacklist " ] . Split ( " " , blacklist , false ) ;
CString name = ( * m_pNetwork ) . GetName ( ) . AsLower ( ) ;
for ( VCString : : iterator i = blacklist . begin ( ) ; i ! = blacklist . end ( ) ; i + + )
{
if ( name . WildCmp ( ( * i ) . AsLower ( ) ) )
{
return false ;
}
}
return true ;
}
2011-01-18 16:12:35 +00:00
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 )
2015-07-05 19:39:30 +00:00
& & network_blacklist ( )
2011-01-26 18:09:55 +00:00
& & replied ( context )
2015-03-26 03:57:12 +00:00
& & context_filter ( 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 )
2015-07-05 19:39:30 +00:00
& & network_blacklist ( )
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
2014-06-17 05:03:19 +00:00
send_message ( msg , title , channel . GetName ( ) , nick ) ;
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 ;
2014-06-17 05:03:19 +00:00
send_message ( msg , title , channel . GetName ( ) , nick ) ;
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 ;
2014-06-17 05:03:19 +00:00
send_message ( msg , title , nick . GetNick ( ) , nick ) ;
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 ;
2014-06-17 05:03:19 +00:00
send_message ( msg , title , nick . GetNick ( ) , nick ) ;
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 " ) ;
}
2014-04-23 13:49:48 +00:00
else if ( value = = " boxcar2 " )
{
PutModule ( " Note: Boxcar 2 requires setting the 'secret' 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
}
2015-07-14 07:25:52 +00:00
else if ( value = = " pushalot " )
{
PutModule ( " Note: Pushalot requires setting the 'secret' (to user key) (to authorization token) option " ) ;
}
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-02-19 19:12:36 +00:00
}
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
}
2014-02-19 19:12:36 +00:00
else if ( value = = " faast " )
{
PutModule ( " Note: Faast requires setting the secret to your apikey " ) ;
}
2014-10-16 01:23:18 +00:00
else if ( value = = " nexmo " )
{
PutModule ( " Note: Nexmo requires setting the 'username' (to api key), 'secret' (to api secret), 'message_title' (to sender number in international format), and 'target' (to destination number in international format) options " ) ;
}
2015-02-21 22:14:44 +00:00
else if ( value = = " slack " )
{
PutModule ( " Note: Slack requires setting 'secret' (from webhook) and 'target' (channel or username), optional 'username' (bot name) " ) ;
}
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 " ) ;
2015-07-05 19:39:30 +00:00
table . AddRow ( ) ;
table . SetCell ( " Condition " , " network_blacklist " ) ;
// network_blacklist() is True if the network is not in a blacklist
table . SetCell ( " Status " , network_blacklist ( ) ? " no " : " yes " ) ;
2011-01-18 20:24:50 +00:00
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 " ] ;
}
2014-08-20 16:31:04 +00:00
else if ( service = = " airgram " )
{
if ( options [ " username " ] = = " " | | options [ " secret " ] = = " " | | options [ " target " ] = = " " )
{
PutModule ( " Error: target, username, and secret must be set " ) ;
return ;
}
service_host = " api.airgramapp.com " ;
service_url = " /1/subscribe " ;
service_auth = options [ " username " ] + CString ( " : " ) + options [ " secret " ] ;
params [ " email " ] = options [ " target " ] ;
}
2011-10-25 19:40:45 +00:00
else
{
PutModule ( " Error: service does not support subscribe command " ) ;
return ;
}
2014-08-20 00:40:59 +00:00
params [ " proxy " ] = options [ " proxy " ] ;
params [ " proxy_ssl_verify " ] = options [ " proxy_ssl_verify " ] ;
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 ) ;
2014-09-02 19:08:06 +00:00
sock - > Request ( use_post , service_host , service_url , params , service_auth ) ;
2011-10-25 19:40:45 +00:00
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 ( ) ) ;
}
2014-08-20 00:40:59 +00:00
if ( params [ " proxy " ] ! = " " ) {
curl_easy_setopt ( curl , CURLOPT_PROXY , params [ " proxy " ] . c_str ( ) ) ;
if ( params [ " proxy_ssl_verify " ] = = " no " ) {
curl_easy_setopt ( curl , CURLOPT_SSL_VERIFYPEER , 0L ) ;
}
}
2013-10-28 23:55:18 +00:00
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
// 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 ;
2014-04-13 18:47:54 +00:00
parent - > PutDebug ( " User-Agent: " + user_agent ) ;
2011-12-15 17:49:46 +00:00
2011-09-29 20:36:28 +00:00
if ( auth ! = " " )
{
2014-04-13 18:44:10 +00:00
CString auth_b64 = auth . Base64Encode_n ( ) ;
request + = " Authorization: Basic " + auth_b64 + crlf ;
parent - > PutDebug ( " Authorization: Basic " + auth_b64 ) ;
2011-09-29 20:36:28 +00:00
}
request + = crlf ;
if ( post )
{
2011-12-15 17:49:46 +00:00
request + = query ;
2011-09-29 20:36:28 +00:00
}
2014-04-13 18:47:54 +00:00
parent - > PutDebug ( " Query string: " + 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 ) ;
2014-04-13 18:47:54 +00:00
parent - > PutDebug ( " Status: " + status ) ;
parent - > PutDebug ( " Message: " + message ) ;
2011-09-29 19:20:53 +00:00
first = false ;
}
else
{
2014-04-13 18:47:54 +00:00
parent - > PutDebug ( " Data: " + data ) ;
2011-09-29 19:20:53 +00:00
}
}
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
2014-04-11 20:45:33 +00:00
template < > void TModInfo < CPushMod > ( CModInfo & Info ) {
Info . AddType ( CModInfo : : UserModule ) ;
Info . SetWikiPage ( " push " ) ;
}
NETWORKMODULEDEFS ( CPushMod , " Send highlights and personal messages to a push notification service " )