2017-03-27 11:15:34 +03:00
/***********************************************************************************
* Author : Dmitry Isaenko *
2017-11-20 15:09:35 +03:00
* License : GNU GPL v .3 *
2017-12-03 02:28:34 +03:00
* Version : 1.4 *
2017-03-27 11:15:34 +03:00
* Site : https : //developersu.blogspot.com/ *
* 2017 , Russia *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "libircclient/libircclient.h"
# include "libircclient/libirc_rfcnumeric.h"
# include "stdio.h"
# include "string.h"
# include <sys/stat.h>
# include <sys/types.h>
# include <time.h>
2017-11-27 06:24:36 +03:00
# include <limits.h> // only to get PATH_MAX
2017-12-03 02:28:34 +03:00
# include <unistd.h> // for using setsid.
2017-11-27 06:24:36 +03:00
# include <syslog.h> // Use syslog
2017-11-27 06:41:17 +03:00
# include <signal.h> // Use signals
2017-12-03 02:28:34 +03:00
# include <sys/select.h> // Use pselect
2017-11-20 22:10:16 +03:00
2017-11-27 06:24:36 +03:00
# include "defined_values.h"
2017-12-03 02:28:34 +03:00
# include "daemon.c"
2017-03-27 11:15:34 +03:00
// TODO make it multi-channel
typedef struct conf_sruct {
int status ;
char server [ 2048 ] ;
char channel [ 2048 ] ;
int port ;
char nick [ 2048 ] ;
char username [ 2048 ] ;
char realname [ 2048 ] ;
char password [ 2048 ] ;
int maxNickLength ;
char logPath [ PATH_MAX ] ;
char link [ 2048 ] ; // should be enough to store link
2017-03-29 05:19:32 +03:00
int reJoin ; // 1 - yes, 0 - no
2017-11-27 06:24:36 +03:00
int floodTimeOut ;
2017-03-27 11:15:34 +03:00
} configuration ;
2017-11-20 15:09:35 +03:00
configuration load_config ( int run , char * nick_or_path ) {
2017-03-27 11:15:34 +03:00
/*
* if run = 0 we load config from the file
* if run = 1 we return configuration structure stored in this function
* if run = 2 we overrite ' nick ' at the configuration structure
*/
int i ;
FILE * conf ;
2017-03-29 05:19:32 +03:00
static char fileArray [ 15 ] [ 2048 ] ; // user setup stored here and avaliable by any call after initial (by passing 0 to function)
2017-03-27 11:15:34 +03:00
char * st ;
2017-03-29 05:19:32 +03:00
char sta [ 15 ] [ 2 ] [ 2048 ] ; // should be 3D
2017-03-27 11:15:34 +03:00
static configuration C ; //returning structure
char confFilePath [ PATH_MAX ] ;
int checksum = 0 ;
2017-11-27 06:24:36 +03:00
if ( run = = 0 ) { // the first time call
if ( ( conf = fopen ( " /etc/loperIRCLogBot/bot.conf " , " r " ) ) ! = NULL ) {
printf ( " Using configuration file stored at /etc/loperIRCLogBot/bot.conf \n "
" Prefere using the one, stored in this folder? Just rename or delete the one from /etc... \n " ) ;
2017-03-27 11:15:34 +03:00
for ( i = 0 ; ( ! feof ( conf ) ) ; i + + ) {
fgets ( fileArray [ i ] , sizeof ( fileArray [ i ] ) , conf ) ;
2017-11-27 06:24:36 +03:00
if ( i > = 14 ) { // 15 lines is max size for config file
C . status = - 2 ;
return C ;
}
2017-03-27 11:15:34 +03:00
}
2017-11-27 06:24:36 +03:00
}
else {
strcpy ( confFilePath , nick_or_path ) ;
strcat ( confFilePath , " bot.conf " ) ; // add bot.conf to path-to-executable
printf ( " Using configuration file stored at %s \n "
" Please note, configuration file also could be stored at '/etc/loperIRCLogBot/bot.conf' and have higher priority \n " , confFilePath ) ;
if ( ( conf = fopen ( confFilePath , " r " ) ) ! = NULL ) {
for ( i = 0 ; ( ! feof ( conf ) ) ; i + + ) {
fgets ( fileArray [ i ] , sizeof ( fileArray [ i ] ) , conf ) ;
if ( i > = 14 ) { // 15 lines is max size for config file
C . status = - 2 ;
return C ;
}
}
}
else {
C . status = - 2 ; //unable to open file or it's too big = -2
return C ;
}
}
2017-03-27 11:15:34 +03:00
fclose ( conf ) ;
# ifdef DEBUG_LOG
printf ( " ___Content of the file___ \n " ) ;
2017-03-29 05:19:32 +03:00
for ( i = 0 ; i < 15 ; i + + ) {
2017-03-27 11:15:34 +03:00
printf ( " %s " , fileArray [ i ] ) ;
}
printf ( " ______________________ \n " ) ;
# endif
// Parsing configuration file - IF STRING DOESN'T HAVE ":" - SKIP IT
2017-03-29 05:19:32 +03:00
for ( i = 0 ; i < 15 ; i + + ) {
2017-03-27 11:15:34 +03:00
if ( strstr ( fileArray [ i ] , " : " ) ! = NULL ) {
st = strtok ( fileArray [ i ] , " : \t \n " ) ; // if we can't get ANY parameter at the string, that have ":" inside, then skip this string
if ( st ! = NULL ) {
strcpy ( sta [ i ] [ 0 ] , st ) ;
2017-11-27 06:24:36 +03:00
st = strtok ( NULL , " \t \n " ) ; // if we see anything before 'space' \t or \n
2017-03-27 11:15:34 +03:00
if ( st = = NULL ) { // if we had a first parameter in string, (no matter was it set before ":" or after) and don't have the second one,
C . status = - 1 ; // then we ruin the chain and returning error. 'Status' of incorrect config-file = "-1"
return C ;
}
else {
strcpy ( sta [ i ] [ 1 ] , st ) ;
2017-11-27 06:24:36 +03:00
if ( strstr ( sta [ i ] [ 0 ] , " link " ) ! = NULL ) { // if it's 'link', then add here everything user wrote
st = strtok ( NULL , " \n " ) ;
while ( st ! = NULL ) {
strcat ( sta [ i ] [ 1 ] , st ) ;
st = strtok ( NULL , " \n " ) ;
}
2017-03-27 11:15:34 +03:00
}
}
}
}
}
2017-11-21 04:33:57 +03:00
for ( i = 0 ; i < 15 ; i + + ) {
2017-03-27 11:15:34 +03:00
if ( strstr ( sta [ i ] [ 0 ] , " server " ) ! = NULL )
checksum | = 1 < < 0 ;
else if ( strstr ( sta [ i ] [ 0 ] , " channel " ) ! = NULL )
checksum | = 1 < < 1 ;
else if ( strstr ( sta [ i ] [ 0 ] , " port " ) ! = NULL )
checksum | = 1 < < 2 ;
else if ( strstr ( sta [ i ] [ 0 ] , " nick " ) ! = NULL )
checksum | = 1 < < 3 ;
else if ( strstr ( sta [ i ] [ 0 ] , " username " ) ! = NULL )
checksum | = 1 < < 4 ;
else if ( strstr ( sta [ i ] [ 0 ] , " realname " ) ! = NULL )
checksum | = 1 < < 5 ;
else if ( strstr ( sta [ i ] [ 0 ] , " password " ) ! = NULL )
checksum | = 1 < < 6 ;
else if ( strstr ( sta [ i ] [ 0 ] , " maxNickLength " ) ! = NULL )
checksum | = 1 < < 7 ;
else if ( strstr ( sta [ i ] [ 0 ] , " logPath " ) ! = NULL )
checksum | = 1 < < 8 ;
else if ( strstr ( sta [ i ] [ 0 ] , " link " ) ! = NULL )
checksum | = 1 < < 9 ;
2017-03-29 05:19:32 +03:00
else if ( strstr ( sta [ i ] [ 0 ] , " reJoin " ) ! = NULL )
checksum | = 1 < < 10 ;
2017-11-27 06:24:36 +03:00
else if ( strstr ( sta [ i ] [ 0 ] , " floodTimeOut " ) ! = NULL )
checksum | = 1 < < 11 ;
2017-03-27 11:15:34 +03:00
}
2017-11-27 06:24:36 +03:00
if ( checksum ! = 0 b111111111111 ) {
2017-03-27 11:15:34 +03:00
C . status = checksum ; // incorrect number of settings defined
return C ;
}
else {
// Format array for return in case we're all good
C . status = 0 ; // OK = 0.
2017-03-29 05:19:32 +03:00
for ( i = 0 ; i < 15 ; i + + ) {
2017-03-27 11:15:34 +03:00
if ( ( strcmp ( sta [ i ] [ 0 ] , " server " ) ) = = 0 )
strcpy ( C . server , sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " port " ) ) = = 0 )
C . port = atoi ( sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " channel " ) ) = = 0 )
strcpy ( C . channel , sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " nick " ) ) = = 0 )
strcpy ( C . nick , sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " username " ) ) = = 0 )
strcpy ( C . username , sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " realname " ) ) = = 0 )
strcpy ( C . realname , sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " password " ) ) = = 0 )
strcpy ( C . password , sta [ i ] [ 1 ] ) ;
else if ( ( strcmp ( sta [ i ] [ 0 ] , " maxNickLength " ) ) = = 0 ) {
C . maxNickLength = atoi ( sta [ i ] [ 1 ] ) ;
if ( C . maxNickLength > 128 ) {
C . maxNickLength = 128 ; // now idiots could feel themselfs protected. Libircclient restriction IIRC set to 128 chars
}
}
else if ( ( strcmp ( sta [ i ] [ 0 ] , " logPath " ) ) = = 0 ) {
if ( strcmp ( sta [ i ] [ 1 ] , " 0 " ) = = 0 )
2017-11-20 15:09:35 +03:00
strcpy ( C . logPath , nick_or_path ) ;
2017-03-27 11:15:34 +03:00
else {
if ( sta [ i ] [ 1 ] [ 0 ] ! = ' / ' ) {
2017-11-20 15:09:35 +03:00
C . status = - 3 ;
2017-03-27 11:15:34 +03:00
return C ;
}
else {
strcpy ( C . logPath , sta [ i ] [ 1 ] ) ;
if ( C . logPath [ strlen ( C . logPath ) ] ! = ' / ' )
strcat ( C . logPath , " / " ) ;
}
}
}
else if ( ( strcmp ( sta [ i ] [ 0 ] , " link " ) ) = = 0 ) {
2017-11-27 06:24:36 +03:00
if ( strcmp ( sta [ i ] [ 1 ] , " 0 " ) = = 0 )
strcpy ( C . link , " ;) " ) ;
else
strcpy ( C . link , sta [ i ] [ 1 ] ) ;
2017-03-27 11:15:34 +03:00
}
2017-03-29 05:19:32 +03:00
else if ( ( strcmp ( sta [ i ] [ 0 ] , " reJoin " ) ) = = 0 ) {
if ( strcmp ( sta [ i ] [ 1 ] , " yes " ) = = 0 | | strcmp ( sta [ i ] [ 1 ] , " Yes " ) = = 0 )
C . reJoin = 1 ;
else
C . reJoin = 0 ;
}
2017-11-27 06:24:36 +03:00
else if ( ( strcmp ( sta [ i ] [ 0 ] , " floodTimeOut " ) ) = = 0 )
C . floodTimeOut = atoi ( sta [ i ] [ 1 ] ) ;
2017-03-27 11:15:34 +03:00
}
if ( strlen ( C . nick ) > C . maxNickLength )
C . nick [ C . maxNickLength ] = ' \0 ' ; // yeah, they will love it, before set nick name longer then 128 char =(
}
// ++++++++++++++++++++++++++++++++++++++++++++++
# ifdef DEBUG_LOG
printf ( " ___Recieved keys from the file___ \n " ) ;
2017-03-29 05:19:32 +03:00
for ( i = 0 ; i < 15 ; i + + ) {
2017-03-27 11:15:34 +03:00
printf ( " %s - %s \n " , sta [ i ] [ 0 ] , sta [ i ] [ 1 ] ) ;
}
printf ( " ______________________ \n " ) ;
# endif
return C ;
}
else if ( run = = 1 ) {
return C ; // just return already loaded structure by request
}
else if ( run = = 2 ) { // save nick recieved
2017-11-20 15:09:35 +03:00
strcpy ( C . nick , nick_or_path ) ;
2017-03-27 11:15:34 +03:00
return C ;
}
}
2017-11-27 06:24:36 +03:00
int createDir ( char * logdir , char * newDirName ) {
2017-03-27 11:15:34 +03:00
struct stat st = { 0 } ;
2017-11-27 06:24:36 +03:00
char dir [ PATH_MAX ] ;
strcpy ( dir , logdir ) ;
strcat ( dir , newDirName ) ; // add bot.conf to path-to-executable
2017-03-27 11:15:34 +03:00
2017-11-27 06:24:36 +03:00
if ( stat ( dir , & st ) = = - 1 ) {
if ( mkdir ( dir , 0777 ) ! = 0 )
2017-03-27 11:15:34 +03:00
return 1 ;
}
return 0 ;
}
2017-11-20 15:09:35 +03:00
// Function to print time stamps when writing to console
2017-11-27 06:24:36 +03:00
const char * printTimeStamp ( ) { // const function, static array.. one say this function should be replaced to something better. It's make me sad.
static char timeStamp [ 22 ] ;
2017-11-20 15:09:35 +03:00
time_t now = time ( NULL ) ;
2017-11-27 06:24:36 +03:00
strftime ( timeStamp , 22 , " %d %b %Y %X " , localtime ( & now ) ) ;
return timeStamp ;
2017-11-20 15:09:35 +03:00
}
// Mainly used to update is_alive
void event_ctcp_rep ( irc_session_t * session , const char * event , const char * origin , const char * * params , unsigned int count ) { // handle own ctcp requests - get recieved data and do something with it
configuration C = load_config ( 1 , " " ) ; //load config to get current nickname
# ifdef PING_DEBUG
printf ( " \n Reply from my CTCP, where USER = %s \n " , origin ) ;
# endif
if ( strncmp ( origin , C . nick , strlen ( C . nick ) ) = = 0 ) { // if bot generated response, then it's ping for internal use. (Ok, documentation is 80% clear on this, it will work but in future should be checked)
is_alive ( 1 , time ( NULL ) , session ) ;
}
}
/* Handle timeouts before re-connect (rewrite in January 2038)
state = 0 - get status
state = 1 - write status
returns 0 - OK
returns 1 - not OK
*/
int is_alive ( int state , time_t timeGot , irc_session_t * session ) { // in future, it should be stand-alone thread
static time_t timeSav ;
static int pingRequestSent = 0 ; // 0 - not sent, 1 - already sent
configuration C = load_config ( 1 , " " ) ; //load config to get current nickname
# ifdef PING_DEBUG
printf ( " is_alive:ping = %d \n " , pingRequestSent ) ;
# endif
if ( state = = 0 ) {
2017-11-27 06:24:36 +03:00
if ( ( timeGot - timeSav ) > TIME_TO_SEND_PING ) { // ok, it's time to send PING to yourself or probably we have problems
2017-11-20 15:09:35 +03:00
if ( pingRequestSent = = 0 ) { // if we didn't sent PING request before, let's do it and change pingRequestSent
# ifdef PING_DEBUG
printf ( " PANIC - Request sent \n " ) ;
printf ( " stored time = %ld \n " , timeSav ) ;
printf ( " recieved time = %ld \n " , timeGot ) ;
# endif
irc_cmd_ctcp_request ( session , C . nick , " PING " ) ;
pingRequestSent = 1 ;
return 0 ;
}
else { // we already sent PING
2017-11-27 06:24:36 +03:00
if ( ( timeGot - timeSav ) > ( TIME_TO_SEND_PING + TIME_TO_WAIT_PING ) ) { // we know, that the time is greater than stored one, so we add 3min(180) and if it's greater, then re-connect
2017-11-20 15:09:35 +03:00
# ifdef PING_DEBUG
printf ( " Now we have problems \n " ) ;
# endif
return 1 ;
}
else
return 0 ;
}
}
}
else if ( state = = 1 ) {
timeSav = timeGot ; // re-write stored time
pingRequestSent = 0 ; // reset pingRequestSent
# ifdef PING_DEBUG
printf ( " \n 1-WRITE event \n Time Saved (re-written) = %ld \n " , timeSav ) ; //debug
2017-11-27 06:24:36 +03:00
printf ( " TIME_TO_SEND_PING = %d \n " , TIME_TO_SEND_PING ) ; //debug
2017-11-20 15:09:35 +03:00
# endif
return 0 ;
}
return 0 ; // how it could be possible to come here? ok, whatever
}
2017-03-27 11:15:34 +03:00
void event_connect ( irc_session_t * session , const char * event , const char * origin , const char * * params , unsigned int count )
{
2017-11-20 15:09:35 +03:00
is_alive ( 1 , time ( NULL ) , session ) ; // this time really 'initial' timestamp written into the watchdog function
2017-03-27 11:15:34 +03:00
2017-11-27 06:24:36 +03:00
configuration C = load_config ( 1 , " " ) ; // dump_event (session, event, origin, params, count);
# ifdef X_MODE
2017-03-27 11:15:34 +03:00
irc_cmd_user_mode ( session , " +x " ) ; // TODO clarify this shit
2017-11-27 06:24:36 +03:00
# endif
2017-03-27 11:15:34 +03:00
irc_cmd_join ( session , C . channel , 0 ) ;
}
void dump_event ( irc_session_t * session , const char * event , const char * origin , const char * * params , unsigned int count )
2017-12-03 02:28:34 +03:00
{ // TODO: Re-write. Actually, this implementation is also pice of shit
# ifdef DEBUG // It cause too many system calls even we don't need them
2017-03-27 11:15:34 +03:00
char buf [ 512 ] ;
int cnt ;
2017-11-27 06:24:36 +03:00
buf [ 0 ] = ' \0 ' ;
for ( cnt = 0 ; cnt < count ; cnt + + ) {
if ( cnt )
strcat ( buf , " | " ) ;
strcat ( buf , params [ cnt ] ) ;
}
2017-03-29 05:19:32 +03:00
# endif
2017-11-27 06:24:36 +03:00
static time_t floodTrackTime = 0 ;
2017-03-27 11:15:34 +03:00
char nowTime [ 20 ] ;
char nickBuf [ 128 ] ;
char hostBuf [ 1024 ] ;
time_t now = time ( NULL ) ;
FILE * fp ; // set FD for the file for logs
configuration C = load_config ( 1 , " " ) ;
// Set name for the log file
irc_target_get_nick ( origin , nickBuf , 128 ) ;
irc_target_get_host ( origin , hostBuf , 1024 ) ;
strftime ( nowTime , 20 , " logs/%Y-%m-%d.txt " , localtime ( & now ) ) ;
strcat ( C . logPath , nowTime ) ; // now C.logPath have the correct name of the file
if ( ( fp = fopen ( C . logPath , " ab " ) ) ! = NULL )
{
strftime ( nowTime , 20 , " [%H:%M:%S] " , localtime ( & now ) ) ;
! ( strcmp ( event , " CHANNEL " ) ) ? fprintf ( fp , " %s <%s>: %s \n " , nowTime , nickBuf , params [ 1 ] ) :
! ( strcmp ( event , " ACTION " ) ) ? fprintf ( fp , " %s %s %s \n " , nowTime , nickBuf , params [ 1 ] ) :
2017-11-20 15:09:35 +03:00
! ( strcmp ( event , " JOIN " ) ) ? fprintf ( fp , " %s >> %s [%s] joined %s \n " , nowTime , nickBuf , hostBuf , params [ 0 ] ) :
2017-03-27 11:15:34 +03:00
! ( strcmp ( event , " INVITE " ) ) ? fprintf ( fp , " %s %s invites %s to %s \n " , nowTime , nickBuf , params [ 0 ] , params [ 1 ] ) :
! ( strcmp ( event , " KICK " ) ) ? fprintf ( fp , " %s !<< %s kicked by %s [%s] with reason: %s \n " , nowTime , params [ 1 ] , nickBuf , hostBuf , params [ 2 ] ) :
! ( strcmp ( event , " MODE " ) ) ? strcmp ( nickBuf , hostBuf ) = = 0 ? 0 : fprintf ( fp , " %s -!- %s [%s] set %s %s \n " , nowTime , nickBuf , hostBuf , params [ 1 ] , params [ 2 ] ? params [ 2 ] : " " ) :
2017-11-20 15:09:35 +03:00
! ( strcmp ( event , " PART " ) ) ? fprintf ( fp , " %s << %s [%s] parted: %s \n " , nowTime , nickBuf , hostBuf , params [ 1 ] ) :
2017-03-27 11:15:34 +03:00
! ( strcmp ( event , " TOPIC " ) ) ? fprintf ( fp , " %s -!- %s [%s] has changed topic to: %s \n " , nowTime , nickBuf , hostBuf , params [ 1 ] ) :
2017-12-03 02:28:34 +03:00
! ( strcmp ( event , " QUIT " ) ) ? fprintf ( fp , " %s << %s [%s] quit: %s \n " , nowTime , nickBuf , hostBuf , params [ 0 ] ) :
2017-03-27 11:15:34 +03:00
! ( strcmp ( event , " NICK " ) ) ? fprintf ( fp , " %s -!- %s [%s] changed nick to: %s \n " , nowTime , nickBuf , hostBuf , params [ 0 ] ) : 0 ;
if ( ! strcmp ( event , " KICK " ) ) {
// Reloading configuration in case our nick has been changed recently
2017-03-29 05:19:32 +03:00
if ( ! ( strcmp ( params [ 1 ] , C . nick ) ) & & C . reJoin = = 1 ) {
2017-03-27 11:15:34 +03:00
event_connect ( session , event , origin , params , count ) ;
}
}
2017-11-27 06:24:36 +03:00
else if ( ! strcmp ( event , " CHANNEL " ) ) // works like shit. Should be separated thread
2017-03-29 05:19:32 +03:00
if ( strncmp ( params [ 1 ] , C . nick , strlen ( C . nick ) ) = = 0 )
2017-11-27 06:24:36 +03:00
if ( ( now - floodTrackTime ) > = C . floodTimeOut ) {
irc_cmd_msg ( session , params [ 0 ] , C . link ) ;
floodTrackTime = now ;
fprintf ( fp , " %s <%s>: %s \n " , nowTime , C . nick , C . link ) ;
}
2017-03-27 11:15:34 +03:00
fclose ( fp ) ;
}
else {
printf ( " Unable to open/create log file: check folder permissions \n " ) ; // and die, but it doesn't die.. well. Ok for now.
2017-11-27 06:41:17 +03:00
exit ( EXIT_FAILURE ) ; // TODO: QA: integration testing needed
2017-03-27 11:15:34 +03:00
}
# if defined (DEBUG)
printf ( " Event \" %s \" , origin: \" %s \" , params: %d [%s] \n " , event , origin ? origin : " NULL " , cnt , buf ) ;
# endif
}
void event_ctcp_req ( irc_session_t * session , const char * event , const char * origin , const char * * params , unsigned int count )
{
dump_event ( session , event , origin , params , count ) ;
time_t now = time ( NULL ) ;
char curTime [ 138 ] ;
strftime ( curTime , 138 , " TIME %a %b %d %H:%M:%S %Z %Y " , localtime ( & now ) ) ;
if ( strcmp ( params [ 0 ] , " VERSION " ) = = 0 ) {
dump_event ( session , event , origin , params , count ) ;
2017-11-21 04:33:57 +03:00
irc_cmd_ctcp_reply ( session , origin , " VERSION loperIRCLogBot v. " __CUR_VER__ ) ;
2017-03-27 11:15:34 +03:00
}
else if ( strcmp ( params [ 0 ] , " SOURCE " ) = = 0 ) {
dump_event ( session , event , origin , params , count ) ;
2017-11-21 04:33:57 +03:00
irc_cmd_ctcp_reply ( session , origin , " SOURCE loperIRCLogBot v. " __CUR_VER__ ) ;
2017-03-27 11:15:34 +03:00
}
else if ( strcmp ( params [ 0 ] , " TIME " ) = = 0 ) {
dump_event ( session , event , origin , params , count ) ;
irc_cmd_ctcp_reply ( session , origin , curTime ) ;
}
else if ( strncmp ( params [ 0 ] , " PING " , 4 ) = = 0 ) {
dump_event ( session , event , origin , params , count ) ;
irc_cmd_ctcp_reply ( session , origin , params [ 0 ] ) ;
}
else if ( strcmp ( params [ 0 ] , " CLIENTINFO " ) = = 0 ) {
dump_event ( session , event , origin , params , count ) ;
2017-11-21 04:33:57 +03:00
irc_cmd_ctcp_reply ( session , origin , " CLIENTINFO loperIRCLogBot v. " __CUR_VER__ " - Supported tags: VERSION, SOURCE, TIME, PING, CLIENTINFO " ) ;
2017-03-27 11:15:34 +03:00
}
}
void event_join ( irc_session_t * session , const char * event , const char * origin , const char * * params , unsigned int count )
{
dump_event ( session , event , origin , params , count ) ;
irc_cmd_user_mode ( session , " +i " ) ;
}
void nick_change ( irc_session_t * session ) {
static char append [ ] = " |0 " ;
configuration C = load_config ( 1 , " " ) ;
if ( append [ 1 ] > ' 9 ' ) // Refactoring request
append [ 1 ] = ' 0 ' ; // if all your nicknames with |0 |2 ... |9 are already occupied, we're fucked up. Really, there is no good solution in this code.
// Let's say that we have string like 1234567|0, and maxNickLength = 10, then we see, that string length is greater (9) then maxNickLength-2, but also we've already appended something like "|0" into it, then
// we could check it and wipe. Otherwise we wipe 2 last chars.
if ( strlen ( C . nick ) > ( C . maxNickLength - 2 ) ) {
if ( ( C . nick [ C . maxNickLength - 3 ] = = ' | ' ) & & ( C . nick [ C . maxNickLength - 2 ] > = ' 0 ' ) & & ( C . nick [ C . maxNickLength - 2 ] < = ' 9 ' ) )
C . nick [ C . maxNickLength - 3 ] = ' \0 ' ;
else
C . nick [ C . maxNickLength - 2 ] = ' \0 ' ;
}
if ( C . nick [ strlen ( C . nick ) - 2 ] = = ' | ' )
C . nick [ strlen ( C . nick ) - 1 ] = append [ 1 ] ;
else
strncat ( C . nick , append , 2 ) ;
append [ 1 ] + + ;
C = load_config ( 2 , C . nick ) ;
irc_cmd_nick ( session , C . nick ) ;
}
void event_numeric ( irc_session_t * session , unsigned int event , const char * origin , const char * * params , unsigned int count )
{
char buf [ 24 ] ;
sprintf ( buf , " %d " , event ) ;
if ( event = = 433 ) { // 433 Nickname is already in use
nick_change ( session ) ;
}
dump_event ( session , buf , origin , params , count ) ;
}
static void event_notice ( irc_session_t * session , const char * event , const char * origin , const char * * params , unsigned int count )
{
char nickBuf [ 1024 ] ; // too big??
configuration C = load_config ( 1 , " " ) ;
dump_event ( session , event , origin , params , count ) ;
if ( ! origin )
return ;
else
irc_target_get_nick ( origin , nickBuf , 1024 ) ;
if ( strcasecmp ( nickBuf , " nickserv " ) )
return ;
if ( ( strstr ( params [ 1 ] , " This nickname is registered " ) ! = NULL ) | | ( strstr ( params [ 1 ] , " This nickname is owned by someone else " ) ! = NULL ) ) {
irc_send_raw ( session , " nickserv IDENTIFY %s " , C . password ) ;
}
else if ( strstr ( params [ 1 ] , " Password accepted " ) ! = NULL )
printf ( " Nickserv authentication succeeded \n " ) ;
}
2017-11-27 06:24:36 +03:00
int makeTemplate ( char * folder ) {
2017-03-27 11:15:34 +03:00
FILE * templ ;
2017-11-21 04:33:57 +03:00
strcat ( folder , " bot.conf " ) ;
2017-03-29 05:19:32 +03:00
2017-11-27 06:24:36 +03:00
printf ( " Attention! This action will remove your current bot.conf file \n "
" Where do you want to generate template?: \n "
" (1) This folder \n "
" (2) /etc/loperIRCLogBot/ \n "
" (A) Abort \n " ) ;
do {
switch ( getchar ( ) ) {
case ' 1 ' :
if ( ( templ = fopen ( folder , " w " ) ) ! = NULL ) {
fprintf ( templ , DEF_DEFAULT_TEMPLATE ) ;
fclose ( templ ) ;
printf ( " Configuratation template created. \n " ) ;
return 0 ;
}
else {
printf ( " Unable to create configuration template file \n "
" Check folder permissions \n " ) ;
return 1 ;
}
case ' 2 ' :
if ( createDir ( " /etc/loperIRCLogBot/ " , " " ) ! = 0 ) {
printf ( " Unable to create diretory /etc/loperIRCLogBot \n "
" Make sure that you have permissions to write there \n " ) ;
return 1 ;
}
if ( ( templ = fopen ( " /etc/loperIRCLogBot/bot.conf " , " w " ) ) ! = NULL ) {
fprintf ( templ , DEF_DEFAULT_TEMPLATE ) ;
fclose ( templ ) ;
printf ( " Configuratation template created. \n " ) ;
return 0 ;
}
else {
printf ( " Unable to create configuration template file \n "
" Check folder permissions \n " ) ;
return 1 ;
}
case ' A ' : case ' a ' :
printf ( " No changes applied \n " ) ;
return 0 ;
case ' \n ' : // ,__o
break ; // _-\_<,
default : // (*)/'(*)
while ( getchar ( ) ! = ' \n ' ) ;
}
printf ( " \033 [A \033 [2K " ) ; // VT100 escape codes
2017-03-27 11:15:34 +03:00
2017-11-27 06:24:36 +03:00
} while ( 1 ) ;
}
2017-03-27 11:15:34 +03:00
2017-11-20 15:09:35 +03:00
void reportSettingsIssues ( int sum ) {
char * setLst [ ] = { " server " ,
" channel " ,
" port " ,
" nick " ,
" username " ,
" realname " ,
" password " ,
" maxNickLenght " ,
" logPath " ,
" link " ,
2017-11-27 06:24:36 +03:00
" reJoin " ,
" floodTimeOut " } ;
2017-11-20 15:09:35 +03:00
int i ;
switch ( sum ) {
case - 1 :
printf ( " Configuration file issue: value or attribute missed or redundant \n " ) ;
break ;
case - 2 :
2017-11-27 06:24:36 +03:00
printf ( " Unable to open configuration files. Please make sure that they exist at one of the next folders \n "
" /etc/loperIRCLogBot/bot.conf \n "
" ./bot.conf \n "
" Please note, that configuration file shouldn't be lager then 15 lines. \n "
" If files exists, please check that user have read permissions for configuraion file \n " ) ;
2017-11-20 15:09:35 +03:00
break ;
case - 3 :
printf ( " Configuration file issue: 'logPath' is not defined as absolute path \n " ) ;
break ;
default :
printf ( " Configuration file issue. Next field(s) not found: \n " ) ;
2017-11-27 06:24:36 +03:00
for ( i = 0 ; i < 12 ; i + + )
2017-11-20 15:09:35 +03:00
if ( ( sum & ( 1 < < i ) ) ! = ( 1 < < i ) )
printf ( " \t '%s' \n " , setLst [ i ] ) ;
2017-03-27 11:15:34 +03:00
}
}
2017-11-27 06:24:36 +03:00
void silentMode ( ) {
// close stdin, redirect stdout & stderr
if ( freopen ( " output.txt " , " a " , stdout ) = = NULL ) // todo - add validation for errno? Write to syslog?
exit ( EXIT_FAILURE ) ;
setlinebuf ( stdout ) ; // line buffer
if ( freopen ( " output.txt " , " a " , stderr ) = = NULL ) // todo - add validation for errno?
exit ( EXIT_FAILURE ) ;
setvbuf ( stderr , NULL , _IONBF , 0 ) ; // no buffering for srderr
if ( freopen ( " /dev/null " , " rb " , stdin ) = = NULL )
exit ( EXIT_FAILURE ) ;
}
2017-03-27 11:15:34 +03:00
int main ( int argc , char * argv [ ] ) {
configuration config ;
irc_callbacks_t callbacks ; // The IRC callbacks structure
irc_session_t * session ;
2017-12-03 02:28:34 +03:00
struct timespec ts ; // time structure for pselect();
2017-03-27 11:15:34 +03:00
fd_set in_set , out_set ;
int maxfd = 0 ;
2017-12-03 02:28:34 +03:00
// ### daemon mode tails.
// Define signals that we will block while using pselect(); See below. pselect set mask provided, then change it to default
sigset_t blockedSignals ; // block all signals; to use in pselect()
sigset_t normalSignals ; // define default mask
sigfillset ( & blockedSignals ) ;
sigdelset ( & blockedSignals , SIGINT ) ;
sigfillset ( & normalSignals ) ; // Not sure that it could make impact on anything
sigprocmask ( SIG_UNBLOCK , & normalSignals , NULL ) ;
// ###
2017-11-21 04:33:57 +03:00
// Let's find out the path to executable file! It's needed, because when we create config file like ../executable -g
// it creates it on the same folder where user located at, not at the folder where the executable is.
// Same for logs.
2017-03-27 11:15:34 +03:00
char dest [ PATH_MAX ] ;
struct stat info ;
if ( readlink ( " /proc/self/exe " , dest , PATH_MAX ) = = - 1 ) {
printf ( " Unable to get the path to this executable \n " ) ;
return 1 ;
}
else {
dest [ strlen ( dest ) - strlen ( strrchr ( dest , ' / ' ) + 1 ) ] = ' \0 ' ; // looks for the last "/" at the path etc.
}
// end detection
if ( argc = = 2 ) {
if ( strcmp ( " --help " , argv [ 1 ] ) = = 0 ) {
2017-11-27 06:24:36 +03:00
printf ( DEF_HELP_MSG ) ;
2017-03-27 11:15:34 +03:00
return 0 ;
}
2017-11-27 06:24:36 +03:00
else if ( ( strcmp ( " -g " , argv [ 1 ] ) = = 0 ) | | ( strcmp ( " --genconf " , argv [ 1 ] ) = = 0 ) ) {
return makeTemplate ( dest ) ;
}
2017-11-21 04:33:57 +03:00
// deamon mode here
else if ( ( strcmp ( " -d " , argv [ 1 ] ) = = 0 ) | | ( strcmp ( " --daemon " , argv [ 1 ] ) = = 0 ) ) {
2017-11-27 06:24:36 +03:00
daemonMode ( ) ;
2017-03-27 11:15:34 +03:00
}
2017-11-20 15:09:35 +03:00
else if ( ( strcmp ( " -s " , argv [ 1 ] ) = = 0 ) | | ( strcmp ( " --silent " , argv [ 1 ] ) = = 0 ) ) {
2017-11-27 06:24:36 +03:00
silentMode ( ) ;
2017-03-27 11:15:34 +03:00
}
else if ( ( strcmp ( " -v " , argv [ 1 ] ) = = 0 ) | | ( strcmp ( " --version " , argv [ 1 ] ) = = 0 ) ) {
2017-11-27 06:24:36 +03:00
printf ( " loperIRCLogBot: " __CUR_VER__ " - build " __TIME__ " " __DATE__ " \n " ) ;
2017-03-27 11:15:34 +03:00
return 0 ;
}
else {
2017-11-20 15:09:35 +03:00
printf ( " Incorrect arguments. \n "
" Use --help for information. \n " ) ;
2017-03-27 11:15:34 +03:00
return 0 ;
}
}
else if ( argc > 2 ) {
2017-11-20 15:09:35 +03:00
printf ( " Too many arguments. \n "
" Use --help for information. \n " ) ;
2017-03-27 11:15:34 +03:00
return 0 ;
}
2017-11-27 06:24:36 +03:00
// -================================================================================================-
2017-03-27 11:15:34 +03:00
config = load_config ( 0 , dest ) ;
2017-11-20 15:09:35 +03:00
if ( config . status = = 0 ) {
2017-11-27 06:24:36 +03:00
printf ( " \t -------------------- \n "
" \t %s \n "
" \t \t SETTINGS \n "
" \t -------------------- \n "
2017-11-20 15:09:35 +03:00
" server - %s \n "
" channel - %s \n "
" port - %d \n "
" nick - %s \n "
" username - %s \n "
" realname - %s \n "
" password - %s \n "
" maxNickLength - %d \n "
" logPath - %s \n "
" link - %s \n "
" reJoin - %s \n "
2017-11-27 06:24:36 +03:00
" floodTimeOut - %d \n "
2017-11-20 15:09:35 +03:00
" \t -------- \n " ,
2017-11-27 06:24:36 +03:00
printTimeStamp ( ) ,
2017-11-20 15:09:35 +03:00
config . server ,
config . channel ,
config . port ,
config . nick ,
config . username ,
config . realname ,
config . password ,
config . maxNickLength ,
config . logPath ,
config . link ,
2017-11-27 06:24:36 +03:00
config . reJoin = = 0 ? " No " : " Yes " ,
config . floodTimeOut ) ;
if ( createDir ( config . logPath , " logs " ) ! = 0 ) { // set logs directory
2017-11-20 15:09:35 +03:00
printf ( " Unable to create directory for log files (%s) \n "
" Please make sure that you have premissions to write in this directory \n " ,
config . logPath ) ;
2017-03-27 11:15:34 +03:00
return 1 ;
}
else {
// Init callbacks
memset ( & callbacks , 0 , sizeof ( callbacks ) ) ;
// Set up the mandatory events
callbacks . event_connect = event_connect ;
callbacks . event_numeric = event_numeric ;
callbacks . event_join = event_join ;
// Set up the rest of events
callbacks . event_ctcp_req = event_ctcp_req ;
callbacks . event_notice = event_notice ;
2017-11-27 06:24:36 +03:00
callbacks . event_ctcp_rep = event_ctcp_rep ; // & dump_event? Now no need to dump. This event is triggered upon receipt of an CTCP response.
//Thus if you generate the CTCP message and the remote user responded, this event handler will be called.
//callbacks.event_unknown = event_unknown; // this event will be triggered when app recieve unknown request from server.
2017-03-27 11:15:34 +03:00
callbacks . event_channel = dump_event ;
callbacks . event_nick = dump_event ;
callbacks . event_quit = dump_event ;
callbacks . event_part = dump_event ;
callbacks . event_mode = dump_event ;
callbacks . event_topic = dump_event ;
callbacks . event_kick = dump_event ;
callbacks . event_invite = dump_event ;
callbacks . event_umode = dump_event ;
callbacks . event_ctcp_action = dump_event ; // "ACTION" handler
// Now create the session
while ( 1 ) {
2017-11-27 06:24:36 +03:00
printf ( " %s Connecting... \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
session = irc_create_session ( & callbacks ) ;
if ( ! session ) {
2017-11-27 06:24:36 +03:00
printf ( " %s Unable to create session \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
// return 1; // give a try once again
}
else {
irc_option_set ( session , LIBIRC_OPTION_SSL_NO_VERIFY ) ;
// Connect to a regular IRC server
if ( irc_connect ( session , config . server , config . port , 0 , config . nick , config . username , config . realname ) ) {
2017-11-27 06:24:36 +03:00
printf ( " %s Could not connect: %s \n " , printTimeStamp ( ) , irc_strerror ( irc_errno ( session ) ) ) ;
2017-03-27 11:15:34 +03:00
// return 1; //give a try once again
2017-11-20 15:09:35 +03:00
// TODO add a counter for few reply and die / do something with this
2017-03-27 11:15:34 +03:00
}
else {
2017-11-20 15:09:35 +03:00
is_alive ( 1 , time ( NULL ) , session ) ; //initialize is_alive internal start time
2017-11-27 06:24:36 +03:00
printf ( " %s Connection established \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
///////////////////// irc_run() replacement ///////////////////
while ( irc_is_connected ( session ) )
{
2017-12-03 02:28:34 +03:00
if ( reloadLogSig = = 1 ) //debugFunction
logsReopen ( ) ;
if ( terminateSig = = 1 ) //debugFunction
killBySignalRecieved ( session ) ;
ts . tv_sec = 0 ;
ts . tv_nsec = 250000000 ;
2017-03-27 11:15:34 +03:00
// Init sets
FD_ZERO ( & in_set ) ;
FD_ZERO ( & out_set ) ;
// +1 chk
2017-11-20 15:09:35 +03:00
if ( is_alive ( 0 , time ( NULL ) , session ) ! = 0 ) {
2017-11-27 06:24:36 +03:00
printf ( " \n \t ----------------------------------- \n \t "
" %s PING timeout \n "
" \t ----------------------------------- \n " ,
printTimeStamp ( ) ) ;
2017-11-20 15:09:35 +03:00
break ; // 1
2017-03-27 11:15:34 +03:00
}
// end +1 chk
2017-11-20 15:09:35 +03:00
2017-03-27 11:15:34 +03:00
irc_add_select_descriptors ( session , & in_set , & out_set , & maxfd ) ;
2017-12-03 02:28:34 +03:00
// Set mask to all signals, while neccessary for us are: SIGHUP, SIGTERM. Exclude SIGINT, 'cause no matter how this app fucks up in case of _exit(2).
if ( pselect ( maxfd + 1 , & in_set , & out_set , 0 , & ts , & blockedSignals ) < 0 ) // and errno == EINTR !
2017-03-27 11:15:34 +03:00
{
2017-11-27 06:24:36 +03:00
printf ( " %s Could not connect or I/O error: LIBIRC_ERR_TERMINATED \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
break ; // 1
}
if ( irc_process_select_descriptors ( session , & in_set , & out_set ) ) {
2017-11-27 06:24:36 +03:00
printf ( " %s Could not connect or I/O error \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
break ; // 1
}
}
irc_disconnect ( session ) ;
2017-11-27 06:24:36 +03:00
printf ( " %s Connection lost \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
//return 0;
////////////////////////////////////////////////////////////////////////////
}
}
2017-11-27 06:24:36 +03:00
printf ( " %s Not connected: will retry in 5 sec \n " , printTimeStamp ( ) ) ;
2017-03-27 11:15:34 +03:00
sleep ( 5 ) ;
} // while end
}
2017-11-20 15:09:35 +03:00
return 0 ; // never happens
2017-03-27 11:15:34 +03:00
}
2017-11-27 06:24:36 +03:00
else {
2017-11-20 15:09:35 +03:00
reportSettingsIssues ( config . status ) ;
2017-11-27 06:24:36 +03:00
return 1 ;
}
2017-03-27 11:15:34 +03:00
return 0 ;
}