From c6dbdbeafebf079c81ee6046761d09c3c58dfcf8 Mon Sep 17 00:00:00 2001 From: Francois Fleuret Date: Sun, 6 Oct 2013 11:15:06 +0200 Subject: [PATCH] Initial commit, version 2.6.3a --- Makefile | 33 + README.txt | 347 ++++++ list.cc | 134 +++ objects.cc | 85 ++ objects.h | 83 ++ tblib.cc | 672 +++++++++++ tblib.h | 154 +++ tropbot.cc | 3363 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 4871 insertions(+) create mode 100644 Makefile create mode 100644 README.txt create mode 100644 list.cc create mode 100644 objects.cc create mode 100644 objects.h create mode 100644 tblib.cc create mode 100644 tblib.h create mode 100644 tropbot.cc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1c72caf --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ + +# Makefile for TropBot +# Bug report & comments to + +CC = g++ + +OPTIONS = -g + +# add -lsocket -lnsl for SOLARIS +LIBS = + + +all: tropbot + +tropbot: tropbot.cc tblib.o objects.o + $(CC) $(OPTIONS) -D SYSTEM=\"`uname`\" -D DATE="\"`date`\"" -o tropbot tblib.o objects.o tropbot.cc $(LIBS) + +tblib.o: tblib.cc tblib.h list.cc + $(CC) $(OPTIONS) -c tblib.cc + +objects.o: objects.cc objects.h list.cc + $(CC) $(OPTIONS) -c objects.cc + +clean: + rm *.o tropbot + +archive: + rm -f ../tropbot.tar.gz + tar -cvf ../tropbot.tar *.cc *.h *.txt Makefile + gzip ../tropbot.tar + +count: + wc *.cc *.h Makefile diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..55b7d7f --- /dev/null +++ b/README.txt @@ -0,0 +1,347 @@ +------------------------------------------------------------------------------- + TropBot v2.5 + Documentation written by Francois Fleuret + Contact for comments & bug reports + Check http://www.eleves.ens.fr:8080/home/fleuret for latest version +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +I - INTRODUCTION -------------------------------------------------------------- +------------------------------------------------------------------------------- + +TropBot is an IRC bot for UNIX system. If you don't know what is IRC, +you should get some infos about it before trying to use TropBot. + +Many IRC Bots exist, and most of them are just extensions of +"classical" bots like VladBot. TropBot was written from scratch, in +C++. My goal was at first to make a short and easy to extend bot, just +a small thing used in emergency cases on the #france channel on +EFNet. After few months, TropBot became a real bot, with many +features. He's still a fast and small bot, but he can defend a channel +by himself. The version 2.2.0 was so stable that I have run one on +#france for longer than one month with no reboot (when I finally +killed it it was just to run a newer version). + +------------------------------------------------------------------------------- +II - TROPBOT CAPABILITIES ----------------------------------------------------- +------------------------------------------------------------------------------- + +TropBot has all the main functions of an IRC bot. First, he has a list +of friends. A friend is a pattern which matches nick!user@host +stuff. Each of those pattern is associated with a level beetween 1 and +4. Anybody matching one of those pattern will be considered to have +the associated level. + +* Level 1 (FRIEND) allows someone to use the simplest functions of the +bot (asking for the level of people, the shit-list, etc.) + +* Level 2 (OP) is the op level. With this level, one will get the +o +automatically after a short delay, and will be allowed to use the +shit-list (see below). + +* Level 3 (DEFENCE) is almost the same thing as level 2, except that +the person will gain the +o without delay. This level is reserved to +other bots of the channel. That way, during collide-attacks, the +TropBot will re-op them faster. + +* Level 4 (MASTER) is the highest level and allows the person to use +all the functions (like adding friends, etc.) + +The shit-list is a list of people who shouldn't be in the +channel. Each entry of this list is a pattern, associated with a delay +and a comment. Each time someone matching the pattern enters the +channel, he will be banned & kicked with the comment. This feature is +usefull against unpleasant people. + +Since v2.0.0, the TropBot has a smart handling of ban-list. He has in +memory a copy of the ban list, so he is able to detect people entering +the channel using hack and kick them; and he is also able to ban or +unban only the required banid and avoid redudant +b/-b change mode. + +TropBot has also a anti-flood procedure. Each person sending more than +4 lines in 2 s will be kicked. TropBot detects all kind of lines +(PRIVMSG, NOTICE, NICK and even JOIN). It's pretty hard to make a good +flood-detection algorithm. TropBot's one is far from perfection, but +works quite nicely. If you have smart ideas to improve it, please mail +me (I wrote a site-flood detection, but didn't include it ... I also +made a routine that counts the number of caractere lines on the +screen, but this kind of algorithm kick both bad and good guys :-)) + +Since v2.3.4 TropBot accepts passwords for users on unsafe sites +(i.e. sites where other people can fake their login). It's done in a +very primitive way, so I guess one should use them only in critical +situations. To use a command when you are a user with passwd, you just +have to add &passwd before all command. In any case, the bot won't be +able to recognize you after a split. + +Since v2.5.5, TropBot accepts DCC CHAT connections; those are direct +socket connections, with no use of servers. This has few interests : +the connection is most of the time faster, you can keep the connection +even if splits occurs and if the bot and you are not on the same part +of the net, there are no flood detection and you won't loose the +connection even if the bots send you a lot of datas, etc. This kind of +connection is great for commands like "who" "help" "shitlist" etc. + +------------------------------------------------------------------------------- +II - HOW TO INSTALL TROPBOT --------------------------------------------------- +------------------------------------------------------------------------------- + +I assume you've already downloaded & detared all the stuff, so you are +supposed to have some files like tblic.cc, tblic.h, tropbot.cc, +Makefile, etc. You just have to do a "make -k", and after a short +compilation, you should obtain a tropbot executable file. I developped +this program on both a Linux system, SunOS and SOLARIS (with gcc +2.7.*), I really don't know what can happen on other systems ... hope +it will work :-) If it doesn't, I fear I can't help you, I'm +definitively NOT a UNIX wizard :-P + +------------------------------------------------------------------------------- +III - HOW TO RUN TROPBOT ------------------------------------------------------ +------------------------------------------------------------------------------- + +The main goal of a bot is to stay in a channel 24h/day to keep the +o +privileges so that he can give them to his friends when they enter the +channel. To do that, a bot must run on a station which is connected to +the net all the time. + +To run the TropBot, you need a tropbot binary (obtained after +compilation). Then, just type "./tropbot" to run it. If you don't +indicate any parameter, you will run a tropbot with the nickname +"TropBot", connected to the server "sil.polytechnique.fr" on the port +6667, and this bot will go to the channel "#tropbot". So, the best way +to run a TropBot is the following : + + nohup ./tropbot -n nickname -c "#channel" -h server + +The 'nohup' UNIX command will make the bot go on running even after +you log out, and the parameters will set the nickname, the channel and +the server (you MUST use your local servers, AFTER you have got an +autorization from an IRCop ! Using a bot on a server without +autorization can make you be killed or even K-lined. Being K-lined +means that you'll never be able to use this server again). + + NEVER PUT A BOT ON A SERVER WITH NO AUTHORIZATION FROM AN IRCOP + +The options you can use in the command lines are the following : + + -c <#channel> sets the channel + -d sets the reconnection delay + -h sets the server name + -j sets the pattern nick for collision + -l sets the user in the user@host stuff + -n sets the nickname + -o loads config file + -od set the delay before +o of level 2 people + -p sets the server port + -? shows help + +Most of those options are quite clear. The -d option allowed you to +modify the delay the bot will wait before he tries to reconnect after +he lost his connection. The -j option is used to give the bot a +"generic nickname". The form of such a nickname is a caractere string +with some '?' which will be replaced with random digits before +use. That way the bot is able to generate random nicks if the nick you +gave him is already used, or after a nick collision (by default the +jam nick is "TB-????"). The -o option is used to load a configuration +file (see below). + +------------------------------------------------------------------------------- +IV - TROPBOT CONFIGURATION FILE ----------------------------------------------- +------------------------------------------------------------------------------- + +If you use the '-o' option, or the 'load' command on IRC, the TropBot +will load a configuration file (.tropbotrc by default, but can be +changed by both the '-o' option and the 'load'/'save' commands). + +The configuration file contains two kinds of lines. The "FRIEND" +lines, who indicate a new friend, and the "SHIT" lines, who indicate +who is supposed to be shit-listed. + +If you want this bot to op all people from united kingdom, just put in +the configuration file the following line : + +FRIEND 2 *!*@*.uk + +This way, the bot will give a level 2 to all people whose +nick!user@host matches *!*@*.uk, and a level 2 is an op level. + + YOU MUST BE IN THIS LIST TO MAKE THE BOT RECOGNIZE YOU + +If you want the bot to shit-list all people from Ecole Polytechnique +in France for 2 days (172800 s), with the comment "I don't like people +with strange hats", just add : + +SHIT *!*@*.polytechnique.fr 172800 I don't like people with strange hats + +You can add a password in the configuration file just as a third +parameter. For example + +FRIEND 2 *!*@*.fr trululu + +will make all people from France able to execute functions with a +level 2 if they use the password trululu. + +------------------------------------------------------------------------------- +V - TROPBOT FUNCTIONS --------------------------------------------------------- +------------------------------------------------------------------------------- + +The functions you can use on a TropBot depend with your level in his +list. For functions that accept both or parameter, a +pattern must contain at least one '@' and one '!' or the bot will +consider it as a nickname. + +LEVEL 1 FUNCTIONS : + +"www" prints the URL of my home page, where you can find the latest +version of TropBot + +"level []" prints the level of the given person (or your own +level if you don't give any parameter). + +"shitlist [!]" (alias "sl") prints the shit-list, or +the shits matched by a given pattern, or the shits that match a nick. + +LEVEL 2 & 3 FUNCTIONS : + +"alert" (alias "a") will put the channel in +m, shit the site who +flooded the more during the last seconds, will kick all the nick on +this site, and then will put the channel -m. This command is VERY +usefull against clone bots. + +"antifloodoff " (alias "ao") will make suppress the +anti-flood system for the given duration. This feature is interesting +when the bot lagged because of network troubles. + +"deban { list of patterns }" (alias "db") remove from the ban-list all +the entries who match one of the pattern + +"filterkick ! []" (alias "fk") will kick all +the people matching the pattern, or on the same site as the given nick + +"home" sends back to the home channel + +"join <#channel>" makes the bot leave the current channel and join +#channel + +"op [{ nicks list }]" makes the bot give you the +o if no list is +given, and give the +o to the given nicks if there is a list + +"pruneban " (alias "pb") check the ban-list and keeps only the + first bans + +"punshit { list }" (alias "pus") removes all the pattern +that matches from the shit-list (so "punshit *" will clear +the shit-list). + +"shit ! [ []]" (alias "s") adds a +pattern in the shit-list for a given duration. Each time a person +matching this pattern enters the channel, he will be banned & kicked +(with the given comment). If the parameter is a nickname, the bot will +compute a nick!user@host pattern by itself (matching all the person +with the same login, on the same site). The duration is the string +like #d#h#m#s where the # are integer numbers. For example, 2d4h means +2 days and 4 hours. + +"shithost " (alias "sh") adds a pattern +that matches all the person from the 's host. Usefull for +providers'people who can easily change their login in the login@host +but have to hangup to change the modem/machine name. + +"shitsite " (alias "ss") adds a pattern +that matches all the person from the 's site. + +"synch" makes the bot give the +o to all the people in the channel +with a level greater than 2 (usefull when you want to re-synchronize a +channel after hacks/splits, etc.) + +"unshit { ! list }" (alias "us") removes patterns from +the shit-list, or all the pattern who match a given user. + +LEVEL 4 FUNCTIONS : + +"save []" saves the configuration file + +"load []" loads the configuration file + +"reset" kills the connection, randoms the nickname, reconnects, and +rejoins. + +"friend []" adds someone in the friend list +with the given level and passwd if specified + +"clone []" starts a new session with the given nick or a random +one + +"die" kills the bot + +"server []" set the server name and the port number +(6667 by default) + +"controlchar " set the control character + +"opdelay " set the delay before +o when level 2 enter the +channel + +"debandelay " set the delay before -b in case of +b by +split + +USING PASSWORDS : + +If you have the password abcdef, to use a command with your level, you +just have to add &abcdef before any command. For example, + +/msg TropBot &abcdef op + +will make the bot op you. + +USING DCCHAT : + +With a DCC CHAT connection to tropbot, you can send all the commands +you use to send with /msg or with the control character in public. The +bot will send you back the answers throught the DCC CHAT too. + +You gain two new commands, which can be used only in DCC CHAT : + +"users" (alias "u") that gives you the nick!user@host of all the +people connected to the bot with DCC CHAT at the same time. + +"talk " (alias "t") that send a message to all the people +connected with DCC CHAT. + +------------------------------------------------------------------------------- +VI - CONCLUSION/BUGS ---------------------------------------------------------- +------------------------------------------------------------------------------- + +A bot is a complex program. This one, even if it's not very long has +to deal with many buffers and strings operations. Such kind of program +can't be 100% safe. I hope current version is stable, but I fear some +buggs are still creeping somewhere. + +------------------------------------------------------------------------------- +- AUTHOR ---------------------------------------------------------------------- +------------------------------------------------------------------------------- + +Francois Fleuret + +E-mail : . +IRCNICK : THX-1138 (EFNet, eureopean part, #france channel) + +I really DON'T appreciate questions about compilation, and I HATE +questions whose answers are in this file. I appreciate suggestions and +bug reports :P + +Snail mail : + + Francois Fleuret + 16 Rue Pasteur + 78330 Fontenay le Fleury, FRANCE + +------------------------------------------------------------------------------- +- ACKNOWLEDGEMENT ------------------------------------------------------------- +------------------------------------------------------------------------------- + +Linux, GCC, Emacs and XFree86 people ... I love you men ! :-) + +Jim Frost for http://world.std.com/~jimf/sockets.html + +------------------------------------------------------------------------------- diff --git a/list.cc b/list.cc new file mode 100644 index 0000000..48c8ab2 --- /dev/null +++ b/list.cc @@ -0,0 +1,134 @@ +/*----------------------------------------------------------------------------- + List class + Written by Francois Fleuret +-----------------------------------------------------------------------------*/ + +#ifndef LIST_H +#define LIST_H + +#include + +//----------------------------------------------------------------------------- + +template +class NodeList +{ +public: + T body; + NodeList *next; +}; + +template +class List +{ +public: + NodeList *first; + + List(); + ~List(); + void Insert(T t); + NodeList *DeleteNext(NodeList *node); + void Remove(T t); + void Reverse(); + int Lenght(); +}; + +//----------------------------------------------------------------------------- + +template +List::List() { first = NULL; } + +template +List::~List() +{ + NodeList *node, *suivant; + for(node=first; node != NULL;) + { + suivant = node->next; + delete node; + node = suivant; + }; +} + +template +void List::Insert(T t) +{ + NodeList *node; + node = new NodeList; + node->body = t; + node->next = first; + first = node; +} + +template +NodeList *List::DeleteNext(NodeList *node) +{ + NodeList *next; + if(node == 0) + { + next = first; + delete first; + } + else + { + next = node->next; + delete node; + } + return next; +} + +template +void List::Remove(T t) +{ + NodeList *node, *pred, *next; + node = first; + pred = NULL; + while(node != NULL) + { + if(node->body == t) + { + next = node->next; + if(pred == NULL) first = next; + else pred->next = next; + delete node; + node = next; + } + else + { + pred = node; + node = node->next; + }; + }; +} + +template +void List::Reverse() +{ + NodeList *node, *next, *pred; + node = first; + pred = NULL; + while(node != NULL) + { + next = node->next; + node->next = pred; + pred = node; + node = next; + }; + first = pred; +}; + +template +int List::Lenght() +{ + NodeList *node; + int l; + l = 0; + for(node=first; node != NULL; node = node->next) l++; + return l; +} + +//----------------------------------------------------------------------------- + +#endif + +//----------------------------------------------------------------------------- diff --git a/objects.cc b/objects.cc new file mode 100644 index 0000000..7b305fc --- /dev/null +++ b/objects.cc @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- + +#include "objects.h" +#include "tblib.h" + +extern int current_time; + +//----------------------------------------------------------------------------- + +WaitInfos::WaitInfos(char *n, char *c, DCCChat *ch, char *u, int m, int d) +{ + nick = strdup(n); + comment = strdup(c); + chat = ch; + user = strdup(u); + mode = m; + duration = d; +}; + +WaitInfos::~WaitInfos() +{ + delete nick; + delete comment; + delete user; +}; + +Welcome::Welcome(char *p, char *c, int time) +{ + pattern = strdup(p); + comment = strdup(c); + time_max = time; +}; + +Welcome::~Welcome() +{ + delete pattern; + delete comment; +}; + +//----------------------------------------------------------------------------- + +DelayModeChange::DelayModeChange(char *w, char *m, char *p, int delay) +{ + where = strdup(w); + mode = strdup(m); + parameter = strdup(p); + time = delay+current_time; +}; + +DelayModeChange::~DelayModeChange() +{ + delete where; + delete mode; + delete parameter; +}; + +//----------------------------------------------------------------------------- + +Person::Person(char *p, int l, char *pwd) +{ + pattern = strdup(p); + level = l; + passwd = strdup(pwd); +}; + +Person::~Person() +{ + delete pattern; +}; + +//----------------------------------------------------------------------------- + +DCCChat::DCCChat(char *p, int s, int l) +{ + prefix = strdup(p); + socket = s; + level = l; +}; + +DCCChat::~DCCChat() +{ + delete prefix; +}; + +//----------------------------------------------------------------------------- diff --git a/objects.h b/objects.h new file mode 100644 index 0000000..48a92a8 --- /dev/null +++ b/objects.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- + +#ifndef OBJECTS_H +#define OBJECTS_H + +#include "list.cc" + +#define LEVEL_MAX 4 +#define LEVEL_MASTER 4 +#define LEVEL_DEFENCE 3 +#define LEVEL_OP 2 +#define LEVEL_FRIEND 1 + +enum +{ + MODE_SHIT_NICK, MODE_SHIT_SITE, MODE_SHIT_HOST, + MODE_UNSHIT_NICK, MODE_UNBAN_NICK, + MODE_FILTER_KICK, + MODE_BAN_NICK, MODE_BAN_SITE, + MODE_LEVEL, + MODE_SHITLIST, + MODE_MSG +}; + +//----------------------------------------------------------------------------- + +class DCCChat +{ +public: + char *prefix; + int socket; + int level; +public: + DCCChat(char *p, int s, int l); + ~DCCChat(); +}; + +class WaitInfos +{ +public: + DCCChat *chat; + char *nick, *comment, *user; + int mode; + int duration; +public: + WaitInfos(char *n, char *c, DCCChat *ch, char *u, int m, int d); + ~WaitInfos(); +}; + +class Welcome +{ +public: + char *pattern, *comment; + int time_max; +public: + Welcome(char *p, char *c, int time); + ~Welcome(); +}; + +class DelayModeChange +{ +public: + char *where, *mode, *parameter; + int time; +public: + DelayModeChange(char *w, char *m, char *p, int delay); + ~DelayModeChange(); +}; + +class Person +{ +public: + char *pattern; + char *passwd; + int level; +public: + Person(char *p, int l, char *pwd); + ~Person(); +}; + +//----------------------------------------------------------------------------- + +#endif diff --git a/tblib.cc b/tblib.cc new file mode 100644 index 0000000..b396fe9 --- /dev/null +++ b/tblib.cc @@ -0,0 +1,672 @@ +//----------------------------------------------------------------------------- + +#include "tblib.h" + +//----------------------------------------------------------------------------- + +char *cut_nick_from_prefix(char *name) +{ + char *s, *nick; + s = name; + while((*s != '!') && (*s != '\0')) s++; + nick = new char[s-name+1]; + strncpy(nick, name, s-name); + *(nick+(s-name)) = '\0'; + return nick; +}; + +//----------------------------------------------------------------------------- + +int is_weird_login(char *login) +{ + char *s; + s = login; + while((*s != '\0') && (*s != '!') && + ((*s & 128) == 0) && (*s >= 32) && (*s != 127)) s++; + return (*s != '\0') || (*s == '!'); +} + +//----------------------------------------------------------------------------- + +int is_pattern(char *s) +{ + while((*s != '\0') && (*s != '!') && (*s != '*')) s++; + return (*s == '*') || (*s == '!'); +} + +//----------------------------------------------------------------------------- + +char *clean_banid(char *banid) +{ + char *result, *s, *t; + int joker_mode, min_car, k; + + result = new char[strlen(banid)+1]; + + s = banid; + t = result; + + joker_mode = 0; + while(*s != '\0') + { + if(*s == '*') + { + if(!joker_mode) min_car = 0; + joker_mode = 1; + } + else + { + if(*s == '?') + { + if(joker_mode) min_car++; + else *t++ = *s; + } + else + { + if(joker_mode) + { + *t++ = '*'; + for(k=0; k='0') && (*c <= '9')) || (*c == '.')); + r |= (*c == '@'); + }; + + return r; +}; + +int are_same_site(char *u1, char *u2) +{ + int n1, n2; + char *c1, *c2; + + n1 = is_numeric(u1); + n2 = is_numeric(u2); + + if(n1 && n2) + { + c1 = u1; c2 = u2; + while(*c1 != '\0') c1++; while((c1 > u1) && (*c1 != '.')) c1--; + while(*c2 != '\0') c2++; while((c2 > u2) && (*c2 != '.')) c2--; + if((c1-u1) == (c2-u2)) return strncmp(u1, u2, c1-u1) == 0; + else return 0; + }; + + if(!n1 && !n2) + { + while(*u1 != '.') u1++; + while(*u2 != '.') u2++; + return strcmp(u1, u2) == 0; + }; + + return 0; +}; + +// :CISMhp.Univ-Lyon1.FR 311 chose Johnkee gponcet@bi bi@192.93.80.208 * :Gael +// :sil.polytechnique.fr 311 chose Johnkee gponcet bi@192.93.80.208 * :Gael + +void concat_pattern_from_host(char *result, char *host) +{ + int r; + if(is_numeric(host)) + { + r = 0; + while((*host != '\0') && (r<3)) + { + *result++ = *host++; + if(*host == '.') r++; + }; + concat(result, ".*"); + } + else + { + *result++ = '*'; + while((*host != '.') && (*host != '\0')) host++; + concat(result, host); + }; + *result++ = '\0'; +}; + +char *pattern_from_prefix(char *prefix, int site_pattern) +{ + char small_buffer[SMALL_BUFFER_SIZE]; + int l; + char *c, *d, *pattern; + + c = small_buffer; + d = prefix; + concat(c, "*!*"); + + // Skip the nickname + while((*d != '!') && (*d != '\0')) d++; + + if(*d == '!') + if(site_pattern) + { + while((*d != '@') && (*d != '\0')) d++; + } + else + { + // Copy the login + d++; + if((*d == '~') || (*d == '+') || + (*d == '-') || (*d=='^')) d++; + l = 0; + while((*d != '@') && (*d != '\0') && + ((l<7) || *(d+1) == '\0')) { *c++ = *d++; l++; } + if((*d != '\0') && (*d != '@')) *c++ = '*'; + while(*d != '@') d++; + }; + *c++ = *d++; + + concat_pattern_from_host(c, d); + + pattern = new char[strlen(small_buffer)+1]; + strcpy(pattern, small_buffer); + + return pattern; +}; + +//----------------------------------------------------------------------------- + +int Contains(char *string, char c) +{ + while((*string != '\0') && (*string != c)) string++; + return *string == c; +}; + +int find_substring(char *&sub, char *&str) +{ + char *s, *u; + u = sub; + s = str; + + if(*u == '\0') while(*s != '\0') s++; + else + while((*u != '\0') && (*u != '*') && (*s != '\0') && + ((*u == *s) || (*u == '?'))) { u++; s++; }; + + if((*u == '\0') || (*u == '*')) + { + sub = u; + str = s; + return 1; + } + else + { + str++; + return 0; + }; +}; + +int match_pattern(char *pattern, char *string) +{ + char *p, *s; + int ok; + + if(pattern == NULL) return -1; + if(string == NULL) return 0; + + p = pattern; s = string; + + if(*p != '*') ok = find_substring(p, s); + else ok = 1; + + while((*s != '\0') && (*p != '\0') && ok) + { + while(*p == '*') p++; + ok = 0; + while((*s != '\0') && !ok) ok = find_substring(p, s); + }; + + if((*p == '\0') && (*s != '\0') && ok) + { + while(*s != '\0') s++; + while((p>=pattern) && (s>=string) && ok && (*p != '*')) + { + ok = (*p == '?') || (*p == *s); + p--; s--; + } + return (*p == '*') || (p == pattern-1); + } + else return (*p == '\0') && (*s == '\0') && ok; +}; + +void uncap(char *string) +{ + char *s; + if(string != NULL) + { + s = string; + for(s = string; *s != '\0'; s++) + if((*s >= 'A') && (*s <= 'Z')) *s -= 'A'-'a'; + }; +}; + +char *strdup(char *s) +{ + char *t; + if(s == NULL) return NULL; + else + { + t = new char[strlen(s)+1]; + strcpy(t, s); + return t; + }; +}; + +int eq(char *s, char *t) +{ + return strcmp(s, t) == 0; +}; + +//----------------------------------------------------------------------------- + +char *seconds_to_string(int time) +{ + char buffer[128]; + char *t, *u, *result; + int d, h, m, s; + int before; + + s = time%60; + m = (time/60)%60; + h = (time/3600)%24; + d = time/86400; + + t = buffer; + + before = 0; + if(s>0) + { + before = 1; + *t++ = 's'; + *t++ = (s%10)+'0'; + if(s/10>0) *t++ = (s/10)+'0'; + }; + + if(m>0) + { + if(before) *t++ = ' '; + else before = 1; + concat(t, "nim"); + *t++ = (m%10)+'0'; + if(m/10>0) *t++ = (m/10)+'0'; + }; + + if(h>0) + { + if(before) *t++ = ' '; + else before = 1; + *t++ = 'h'; + *t++ = (h%10)+'0'; + if(h/10>0) *t++ = (h/10)+'0'; + }; + + if(d>0) + { + if(before) *t++ = ' '; + else before = 1; + *t++ = 'd'; + while(d>0) + { + *t++ = (d%10)+'0'; + d /= 10; + }; + }; + + result = new char[(t-buffer)+1]; + u = result; + + t--; + while(t>=buffer) *u++ = *t--; + *u = '\0'; + + return result; +}; + +int string_to_seconds(char *string) +{ + int total; + int acc; + total = 0; acc = 0; + while(*string != '\0') + { + if((*string >= '0') && (*string <= '9')) acc = 10*acc + (*string - '0'); + if(*string == 'd') { total = total+86400*acc; acc = 0; }; + if(*string == 'h') { total = total+3600*acc; acc = 0; }; + if(*string == 'm') { total = total+60*acc; acc = 0; } + if((*string == 's') || (*string == '\0')) { total = total+acc; acc = 0; } + string++; + }; + return total; +}; + +//----------------------------------------------------------------------------- + +int call_socket(char *hostname, unsigned short portnum) +{ + struct sockaddr_in sa; + struct hostent *hp; + int s; + + if ((sa.sin_addr.s_addr = inet_addr(hostname)) == -1) + { + if ((hp = gethostbyname(hostname)) != NULL) + { + bzero((char *) &sa, sizeof(sa)); + bcopy(hp->h_addr, (char *) &sa.sin_addr, + hp->h_length); + sa.sin_family = hp->h_addrtype; + } + else + return (-2); + } + else sa.sin_family = AF_INET; + + sa.sin_port = (unsigned short) htons(portnum); + + + +/* + if(is_numeric(hostname)) + { +// if ((hp = gethostbyaddr(hostname)) == NULL) + cerr<<"*** Can't use numeric IP ***\n"; + return -1; + } + else + { + if ((hp = gethostbyname(hostname)) == NULL) + return -1; + }; + + cout<<"HP->H_ADDR >"; + for(int k=0; kh_length; k++) cout<<"["<h_addr[k]<<"]"; + cout<<"<\n"; + + memset(&sa,0,sizeof(sa)); + memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); + sa.sin_family= hp->h_addrtype; + sa.sin_port= htons((u_short)portnum); +*/ + + if ((s= socket(AF_INET, SOCK_STREAM, 0)) < 0) /* get socket */ + return(-1); + + if (connect(s, (struct sockaddr *) &sa, sizeof sa) < 0) /* connect */ + { + close(s); + return(-1); + } + return(s); +} + +//----------------------------------------------------------------------------- + +int establish(unsigned short portnum) +{ + char myname[MAXHOSTNAME+1]; + int s; + struct sockaddr_in sa; + struct hostent *hp; + + memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear our address */ + gethostname(myname, MAXHOSTNAME); /* who are we? */ + hp= gethostbyname(myname); /* get our address info */ + if (hp == NULL) /* we don't exist !? */ + return(-1); + sa.sin_family= hp->h_addrtype; /* this is our host address */ + sa.sin_port= htons(portnum); /* this is our port number */ + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) /* create socket */ + return(-1); + + if (bind(s,(struct sockaddr *)&sa,sizeof(struct sockaddr_in)) < 0) + { + close(s); + return(-1); /* bind address to socket */ + } + + listen(s, NB_QUEUE_CONNECTS); /* max # of queued connects */ + return(s); +} + +//----------------------------------------------------------------------------- + +int get_connection(int s) +{ + int t; /* socket of connection */ + if ((t = accept(s,NULL,NULL)) < 0) /* accept connection if there is one */ + return(-1); + return(t); +} + +//----------------------------------------------------------------------------- + +int slice_buffer(char *&src, char *endsrc, + char *&prefix, + char *&dest_cmd, + char **slice_cmd, int &n_cmd, + char *&dest_ctcp, + char **slice_ctcp, int &n_ctcp) +{ + int end; + int ctcp, queue; + + char *s; + + s = src; + while((*s != '\r') && (*s != '\n') && (s *node; + for(node = first; node != NULL; node = node->next) + cout<body<<"\n"; +}; + +void ListChar::Clear() +{ + NodeList *node, *next; + node = first; + while(node != NULL) + { + next = node->next; + delete node->body; + delete node; + node = next; + }; + first = NULL; +}; + +void ListChar::Add(char *s) +{ + char *p; + p = strdup(s); + uncap(p); + Insert(p); +}; + +void ListChar::Remove(char *s) +{ + NodeList *node, *pred, *next; + + pred = NULL; + node = first; + while(node != NULL) + { + next = node->next; + if(strcmp(node->body, s) == 0) + { + if(pred == NULL) first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + }; +}; + +int ListChar::Matches(char *s) +{ + NodeList *node; + int yep; + yep = 0; + for(node = first; (node != NULL) && !yep; node = node->next) + yep = match_pattern(node->body, s); + return yep; +}; + +int ListChar::IsMatchedBy(char *s) +{ + NodeList *node; + int yep; + yep = 0; + for(node = first; (node != NULL) && !yep; node = node->next) + yep = match_pattern(s, node->body); + return yep; +}; + +int ListChar::Contains(char *s) +{ + NodeList *node; + int yep; + yep = 0; + for(node = first; (node != NULL) && !yep; node = node->next) + yep = (strcmp(node->body, s) == 0); + return yep; +}; + diff --git a/tblib.h b/tblib.h new file mode 100644 index 0000000..c5c1de7 --- /dev/null +++ b/tblib.h @@ -0,0 +1,154 @@ +/*----------------------------------------------------------------------------- + Thanks to http://world.std.com/~jimf/sockets.html +-----------------------------------------------------------------------------*/ + +#ifndef TBLIB_H +#define TBLIB_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _AIX +#include +#endif + +#include +#include + +#include "list.cc" + +//----------------------------------------------------------------------------- + +/* +extern "C" int close(int); +extern "C" int read(int, char *, int); +extern "C" int gethostname(); +extern "C" size_t write(int, char *, int); +extern "C" int socket(int, int, int); +extern "C" int connect(int, struct sockaddr *, int); +extern "C" int bind(int, struct sockaddr *, int); +extern "C" int listen(int, int); +extern "C" int accept(int, struct sockaddr *, int *); +*/ + +//----------------------------------------------------------------------------- + +#define BUFFER_SIZE 4096 +#define SMALL_BUFFER_SIZE 128 + +#define NB_QUEUE_CONNECTS 3 +#define MAXHOSTNAME 64 +#define NB_SLICES_MAX 32 + +//----------------------------------------------------------------------------- + +// This routine gets the nickame from the nick!user@host stuff + +char *cut_nick_from_prefix(char *name); + +int is_weird_login(char *login); + +int is_pattern(char *s); + +char *clean_banid(char *banid); + +//----------------------------------------------------------------------------- + +// This routine copy r in s, and move r to the next ' ', or sets it to +// NULL if no more ' ' is find + +char *next_word(char *buffer, char *r, int buffer_size); + +//----------------------------------------------------------------------------- + +// This routine add the d string at the c car, and modify c +// ** USE WITH CARE ** + +inline void concat(char *&c, char *d) +{ + while(*d != '\0') *c++ = *d++; +}; + +//----------------------------------------------------------------------------- + +int is_hostname(char *host); + +// Evaluate if an IP is numerical or not + +int is_numeric(char *host); + +int are_same_site(char *u1, char *u2); + +//----------------------------------------------------------------------------- + +void concat_pattern_from_host(char *result, char *host); +char *patter_from_prefix(char *prefix, int site_ban, char *&pattern); + +//----------------------------------------------------------------------------- + +int Contains(char *string, char c); +int find_substring(char *string, char *substring, + int *debut, int *debut_sub); + +// Send back if the string match pattern or not (pattern with '?' and +// '*') + +int match_pattern(char *pattern, char *string); + +//----------------------------------------------------------------------------- + +// Uncap string +void uncap(char *string); + +// Duplicate a string +char *strdup(char *s); + +// Evaluate if strings are equal +int eq(char *s, char *t); + +//----------------------------------------------------------------------------- + +char *seconds_to_string(int time); +int string_to_seconds(char *string); + +//----------------------------------------------------------------------------- + +// Socket stuff + +int call_socket_ip(unsigned short host, unsigned short portnum); +int call_socket(char *hostname, unsigned short portnum); +int establish(unsigned short portnum); +int get_connection(int s); + +//----------------------------------------------------------------------------- + +int slice_buffer(char *&src, char *endsrc, + char *&dest_cmd, + char *&prefix, + char **slice_cmd, int &n_cmd, + char *&dest_ctcp, + char **slice_ctcp, int &n_ctcp); + +//----------------------------------------------------------------------------- + +class ListChar : public List +{ +public: + void Print(); + void Clear(); + void Add(char *s); + void Remove(char *s); + int Matches(char *s); + int IsMatchedBy(char *s); + int Contains(char *s); +}; + +//----------------------------------------------------------------------------- + +#endif diff --git a/tropbot.cc b/tropbot.cc new file mode 100644 index 0000000..13b85b2 --- /dev/null +++ b/tropbot.cc @@ -0,0 +1,3363 @@ +/*----------------------------------------------------------------------------- + TropBot, a small IRC bot, v2.6 Dec 95 - Oct 96 + Witten by Francois Fleuret. + Contact for comments & bug reports + Check http://www.eleves.ens.fr:8080/home/fleuret for latest version +-----------------------------------------------------------------------------*/ + +#define VERSION "v2.6.3a" +#define OPTIONS " + patch for mode -ko stuff" + +#include +#include + +#include +#include + +#include "tblib.h" +#include "objects.h" +#include "list.cc" + +//----------------------------------------------------------------------------- + +#define SCREEN_OUTPUT + +//----------------------------------------------------------------------------- + +#define MODE_NUMBER_MAX 3 +#define SIZE_BANLIST_MAX 20 +#define NB_CHAR_MAX 400 + +//----------------------------------------------------------------------------- + +#define DEFAULT_CONTROL_CHAR '|' +#define DELAY_NICK_BACK 300 +#define DELAY_MAX_RECONNECT 300 +#define DELAY_DEAD_SERVER 900 + +#define DEFAULT_SERVER "sil.polytechnique.fr" +#define DEFAULT_PORT 6667 + +#define DEFAULT_USER_NAME "\002TropBot\002 " VERSION OPTIONS +#define DEFAULT_LOGIN "tropbot" +#define DEFAULT_NICK "TropBot" +#define DEFAULT_JAM_NICK "TB-????" +#define DEFAULT_HOME_CHANNEL "#tropbot" +#define DEFAULT_SOCKET_DELAY 30 +#define DEFAULT_OP_DELAY 2 +#define DEFAULT_DEBAN_DELAY 0 + +#define DEFAULT_ANTI_FLOOD_OFF_DURATION 600 + +#define DEFAULT_CONFIG_FILE ".tropbotrc" + +#define DEFAULT_SHIT_TIME 600 +#define DEFAULT_RESTRICTED_TIME 900 + +#define DEFAULT_MODE_PROTECT 0 + +// Not needed coz flooding people can't re-JOIN quickly +//#define BAN_ON_FLOOD +#define BAN_FLOOD_DELAY 30 + +#define MAX_LINE_FLOOD 4 +#define MAX_CTCP_FLOOD 3 +#define FLOOD_DELAY 2 +#define DELAY_ANSWERS 3 +#define ANTI_CLONES_DELAY 30 + +#define NB_SONS_MAX 3 + +#define HISTORY_SIZE 3 + +enum { FL_NOTICE, FL_PUBLIC, FL_CTCP, FL_NICK, FL_PART, FL_JOIN }; + +//----------------------------------------------------------------------------- + +#define MAX_DCC_CHAT 16 + +//----------------------------------------------------------------------------- + +extern "C" void bzero(void *, int); +extern "C" int select(int, fd_set *, fd_set *, fd_set *, timeval *); +extern "C" pid_t fork(void); + +//----------------------------------------------------------------------------- + +int default_port = DEFAULT_PORT; +int wanted_port; +char default_server[MAXHOSTNAME+1] = DEFAULT_SERVER; +char wanted_server[MAXHOSTNAME+1]; + +fd_set ready, result; +int alive; // Should be set to false (0) to kill the bot +int father, nb_sons; +int socket_irc; +int socket_delay = DEFAULT_SOCKET_DELAY; +int op_delay = DEFAULT_OP_DELAY; +int deban_delay = DEFAULT_DEBAN_DELAY; +char control_char = DEFAULT_CONTROL_CHAR; + +int delay; +timeval delay_pause; +time_t current_time, time_killed, time_last_datas, last_answer_time, + anti_flood_off_until; +char *src, *endsrc; +char buffer[BUFFER_SIZE]; + +int IRC_connected, IRC_registred; +int global_state, in_channel, was_killed, mode_protect_on; + +#define STATE_WAIT 0 +#define STATE_GETING_WHO 1 +#define STATE_GETING_BAN 2 + +//----------------------------------------------------------------------------- + +template class List; +template class List; +template class List; +template class List; +template class List; +template class List; + +List dcc_chat_list; +List level_list; +List shit_list; +List wait_list; +List mode_change_list; + +ListChar present_people, banid_list, restricted_list, history; + +//----------------------------------------------------------------------------- + +// General buffer for all IRC operations +char IRC_buffer[BUFFER_SIZE]; + +// Nick we got after registration +char real_nick[SMALL_BUFFER_SIZE]; + +// Nick we want +char wanted_nick[SMALL_BUFFER_SIZE]; + +char original_nick[SMALL_BUFFER_SIZE] = DEFAULT_NICK; + +// Pattern used for random-nick generation if nick-collision occurs +// all the '?' will be replaced with random digits +char jam_nick[SMALL_BUFFER_SIZE] = DEFAULT_JAM_NICK; + +// The "user" we want in the nick!user@host stuff +char user_login[SMALL_BUFFER_SIZE] = DEFAULT_LOGIN; + +// The USERNAME string +char user_name[SMALL_BUFFER_SIZE] = DEFAULT_USER_NAME; + +// The channel we want to join +char current_channel[SMALL_BUFFER_SIZE], wanted_channel[SMALL_BUFFER_SIZE]; +char home_channel[SMALL_BUFFER_SIZE] = DEFAULT_HOME_CHANNEL; + +// The name of the configuration file +char config_file[SMALL_BUFFER_SIZE] = DEFAULT_CONFIG_FILE; + +//----------------------------------------------------------------------------- + +char *prefix_from_nick(char *nick) +{ + NodeList *node; + char *s, *t; + int yep; + yep = 0; + + node = present_people.first; + while((node != NULL) && !yep) + { + yep = 1; + s = node->body; + t = nick; + while((*t != '\0') && + (*s != '!') && (*s != '\0') && yep) yep &= (*s++ == *t++); + yep &= ((*t == '\0') && (*s == '!')); + if(!yep) node=node->next; + } + + if(node != NULL) return node->body; else return NULL; + +} + +//----------------------------------------------------------------------------- + +int level_person(char *prefix, char *passwd) +{ + NodeList *node; + int l; + l = 0; + + for(node = level_list.first; node != NULL; node=node->next) + if(match_pattern(node->body->pattern, prefix)) + if((node->body->level > l) || (l == 0)) + { + if(node->body->passwd == NULL) l = node->body->level; + else + if(passwd != NULL) + if(strcmp(node->body->passwd, passwd) == 0) + l = node->body->level; + } + return l; +} + +void remove_level(char *prefix) +{ + NodeList *node, *pred, *next; + pred = NULL; next = NULL; + + node = level_list.first; + while(node != NULL) + { + next = node->next; + if(strcmp(node->body->pattern, prefix) == 0) + { + if(pred == NULL) level_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +void clear_levels() +{ + NodeList *node, *next; + + node = level_list.first; + while(node != NULL) + { + next = node->next; + delete node->body; + delete node; + node = next; + } + level_list.first = NULL; +} + +void clear_shit() +{ + NodeList *node, *next; + + node = shit_list.first; + while(node != NULL) + { + next = node->next; + delete node->body; + delete node; + node = next; + } + shit_list.first = NULL; +} + +void remove_wait_for_chat(DCCChat *chat) +{ + NodeList *node, *pred, *next; + pred = NULL; next = NULL; + + node = wait_list.first; + while(node != NULL) + { + next = node->next; + if(node->body->chat == chat) + { + if(pred == NULL) wait_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +//----------------------------------------------------------------------------- + +// This function generates a random nick from a pattern. The pattern +// is supposed to contain some '?', which will be replaced with random +// digits. + +void rand_nick(char *nick, char *pattern) +{ + unsigned int n; + for(n=0; n"<0) + { + *ptr_modes = '\0'; + *ptr_param_modes = '\0'; + sprintf(IRC_buffer, "MODE %s %s %s\n", + where, string_modes, string_param_modes); + write_irc(IRC_buffer); + reset_mode(); + } +} + +void add_mode(char *where, char *mode, char *param) +{ + char *s; + + // Disgusting hack coz of a FUCKING server bug :-( + if(mode[1] == 'o') + { + if(k_mode) send_mode(where); + o_mode = 1; + } + + if(mode[1] == 'k') + { + if(o_mode) send_mode(where); + k_mode = 1; + } + + s = mode; + while(*s != '\0') *ptr_modes++ = *s++; + if(param != NULL) + { + s = param; + while(*s != '\0') *ptr_param_modes++ = *s++; + *ptr_param_modes++ = ' '; + } + + if(++nb_modes == MODE_NUMBER_MAX) send_mode(where); +} + +//----------------------------------------------------------------------------- + +void tell(DCCChat *chat, char *nick, char *msg) +{ + char tell_buffer[BUFFER_SIZE]; + +#ifdef SCREEN_OUTPUT + cout<<"tell(chat="<socket, msg, strlen(msg)); + + if(nick != NULL) + { + sprintf(tell_buffer, "NOTICE %s :%s", nick, msg); + write_irc(tell_buffer); + } +} + +//----------------------------------------------------------------------------- + +void smart_shit(DCCChat *chat, char *nick, + char *pattern, char *comment, int duration) +{ + NodeList *node, *next, *pred; + int no_shit; + int time_max, l; + + if(duration < 5) duration = 5; + + time_max = duration + current_time; + + no_shit = 0; + node = shit_list.first; + pred = NULL; + while((node != NULL) && !no_shit) + { + next = node->next; + if(strcmp(node->body->pattern, pattern) == 0) + { + if(node->body->time_max <= time_max) + { + if(pred == NULL) shit_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else + { + no_shit = 1; + pred = node; + } + } + else pred = node; + node = next; + } + + if(!no_shit) + { + shit_list.Insert(new Welcome(pattern, comment, time_max)); + if((nick != NULL) || (chat != NULL)) + { + char *string_duration; + string_duration = seconds_to_string(duration); + sprintf(IRC_buffer, "Shit %s for %s (%s)\n", + pattern, string_duration, comment); + tell(chat, nick, IRC_buffer); + delete string_duration; + } + } + else if((nick != NULL) || (chat != NULL)) + { + sprintf(IRC_buffer, + "Can't shit %s, already shitted for a longer time\n", pattern); + tell(chat, nick, IRC_buffer); + } +} + +//----------------------------------------------------------------------------- + +void smart_ban(char *where, char *pattern) +{ + NodeList *node; + int n; + n = 0; + for(node = banid_list.first; node != NULL; node = node->next) + { + if((n>=SIZE_BANLIST_MAX-1) || + (match_pattern(pattern, node->body) && + (strcmp(pattern, node->body) != 0))) + add_mode(where, "-b", node->body); + else n++; + } + add_mode(current_channel, "+b", pattern); +} + +void smart_unban(char *where, char *pattern) +{ + if(banid_list.Contains(pattern)) add_mode(where, "-b", pattern); +} + +//----------------------------------------------------------------------------- + +void check_delay_mode_change() +{ + NodeList *node, *pred, *next; + + pred = NULL; + node = mode_change_list.first; + while(node != NULL) + { + next = node->next; + if(current_time >= node->body->time) + { + add_mode(node->body->where, node->body->mode, node->body->parameter); + if(pred == NULL) mode_change_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +void clean_delay_mode_change(char *where, char *mode, char *param) +{ + NodeList *node, *pred, *next; + + uncap(param); + uncap(where); + pred = NULL; + node = mode_change_list.first; + + if(param != NULL) + { + while(node != NULL) + { + next = node->next; + if((node->body->parameter != NULL) && + (strcmp(where, node->body->where) == 0) && + (strcmp(param, node->body->parameter) == 0) && + (strcmp(mode, node->body->mode) == 0)) + { + if(pred == NULL) mode_change_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } + } + else + { + while(node != NULL) + { + next = node->next; + if((strcmp(where, node->body->where) == 0) && + (node->body->parameter == NULL) && + (strcmp(mode, node->body->mode) == 0)) + { + if(pred == NULL) mode_change_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } + } +} + +void RemoveDelayModeChange(char *mode, char *parameter) +{ + NodeList *node, *pred, *next; + + pred = NULL; + node = mode_change_list.first; + while(node != NULL) + { + next = node->next; + if((strcmp(node->body->mode, mode) == 0) && + (strcmp(node->body->parameter, parameter) == 0)) + { + if(pred == NULL) mode_change_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +//----------------------------------------------------------------------------- + +void synch(char *channel) +{ + char *nick; + NodeList *node; + + for(node = present_people.first; node != NULL; node = node->next) + if(level_person(node->body, NULL) >= LEVEL_OP) + { + nick = cut_nick_from_prefix(node->body); + add_mode(channel, "+o", nick); + delete nick; + } +} + +//----------------------------------------------------------------------------- + +void filter_kick(char *where, char *pattern, char *comment) +{ + NodeList *node; + char *nick; + for(node = present_people.first; node != NULL; node = node->next) + { + if(match_pattern(pattern, node->body)) + if(level_person(node->body, NULL) == 0) + { + nick = cut_nick_from_prefix(node->body); + sprintf(IRC_buffer, "KICK %s %s :%s\n", where, nick, comment); + write_irc(IRC_buffer); + delete nick; + } + } +} + +//----------------------------------------------------------------------------- + +// Return a pointer on the first character after the last '@' in the string +// Beware of some weird hack-script that add @ in the login +char *adr_beginning(char *s) +{ + char *t; + t = s; + while(*s != '\0') if(*s++ == '@') t = s; + if(*t == '@') t++; + return t; +} + +char *reach_loginathost(char *s) +{ + while((*s != '!') && (*s != '\0')) s++; + if(s != '\0') s++; + return s; +} + +//----------------------------------------------------------------------------- + +class FloodLine +{ +public: + char *prefix; + int kind; + int weigth; + int time; + int individual, site, alreadykicked; + + FloodLine(char *p, int k, int w) + { + prefix = strdup(p); + time = current_time; + alreadykicked = 0; + kind = k; + weigth = w; + } + + ~FloodLine() { delete prefix; } + void Reset() { individual = 0; site = 0; } +}; + +template class List; + +List flood_list; + +//----------------------------------------------------------------------------- + +void alert(char *where, DCCChat *chat, char *nick) +{ + int nb_lines, max_nb_lines, duration; + NodeList *node, *son; + char *site, *site2, *guilty_site, *r; + char pattern[SMALL_BUFFER_SIZE]; + + for(node = flood_list.first; node != NULL; node = node->next) + node->body->individual = 0; + + node = flood_list.first; + + max_nb_lines = 1; + guilty_site = NULL; + + while(node != NULL) + { + while((node != NULL) && node->body->individual) node = node->next; + if(node != NULL) + { + nb_lines = 0; + site = adr_beginning(node->body->prefix); + + son = node; + while(son != NULL) + { + while((son != NULL) && son->body->individual) son = son->next; + if(son != NULL) + { + site2 = adr_beginning(son->body->prefix); + + if(are_same_site(site, site2)) + { + son->body->individual = 1; + nb_lines++; + } + son = son->next; + } + } + + if(nb_lines > max_nb_lines) + if(level_person(node->body->prefix, NULL) == 0) + { + max_nb_lines = nb_lines; + guilty_site = site; + } + node = node->next; + } + } + + if(guilty_site != NULL) + { + duration = DEFAULT_SHIT_TIME; + r = pattern; + *r++ = '*'; *r++ = '!'; *r++ = '*'; *r++ = '@'; + concat_pattern_from_host(r, guilty_site); + add_mode(where, "+m", NULL); + smart_shit(NULL, NULL, pattern, "Alert", duration); + smart_ban(where, pattern); + send_mode(where); + filter_kick(where, pattern, "Filter kick on alert"); + add_mode(where, "-m", NULL); + send_mode(where); + } + else tell(chat, nick, "\002No flooding site detected\002\n"); +} + +void check_one_flood(NodeList *node) +{ + NodeList *son; + char *userhost, *userhost2, *nick; + int nb_lines; + + userhost = reach_loginathost(node->body->prefix); + + nb_lines = 0; + son = node; + while(son != NULL) + { + while((son != NULL) && son->body->individual) son = son->next; + if(son != NULL) + { + if(!son->body->alreadykicked) + { + userhost2 = reach_loginathost(son->body->prefix); + + if(strcmp(userhost, userhost2) == 0) + { + son->body->individual = 1; + nb_lines++; + } + } + son = son->next; + } + } + + if(nb_lines >= MAX_LINE_FLOOD) + { + +#ifdef BAN_ON_FLOOD + char *pattern, *banid; + if((level_person(node->body->prefix, NULL) < LEVEL_OP)) + { + pattern = pattern(node->body->prefix, 0); + banid = clean_banid(pattern); + smart_ban(current_channel, banid); + send_mode(current_channel); + mode_change_list.Insert(new DelayModeChange(current_channel, + "-b", banid, + BAN_FLOOD_DELAY)); + delete banid; + delete pattern; + } +#endif + + nick = cut_nick_from_prefix(node->body->prefix); + sprintf(IRC_buffer, "KICK %s %s :%s\n", current_channel, nick, "Flood"); + write_irc(IRC_buffer); + delete nick; + + for(son = flood_list.first; son != NULL; son = son->next) + { + userhost2 = reach_loginathost(son->body->prefix); + if(strcmp(userhost, userhost2) == 0) son->body->alreadykicked = 1; + } + } + +} + +void check_flood() +{ + int nb_lines, nb_ctcp; + NodeList *node, *next, *pred; + + nb_lines = 0; nb_ctcp = 0; + pred = NULL; + node = flood_list.first; + while(node != NULL) + { + next = node->next; + if(node->body->time >= current_time - FLOOD_DELAY) + { + nb_lines += node->body->weigth; + if((node->body->kind == FL_CTCP) && !node->body->alreadykicked) + nb_ctcp += node->body->weigth; + pred = node; + node->body->Reset(); + } + else + { + if(pred == NULL) flood_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + node = next; + } + + /* + if(nb_ctcp >= MAX_CTCP_FLOOD) + { + add_mode(current_channel, "+m", NULL); + send_mode(current_channel); + mode_change_list.Insert(new DelayModeChange(current_channel, + "-m", NULL, + ANTI_CLONES_DELAY)); + } + */ + + if(nb_lines >= MAX_LINE_FLOOD) + { + for(node = flood_list.first; node != NULL; node = node->next) + node->body->individual = 0; + + node = flood_list.first; + while(node != NULL) + { + while((node != NULL) && node->body->individual) node = node->next; + if(node != NULL) + { + if(!node->body->alreadykicked) check_one_flood(node); + node = node->next; + } + } + } +} + +inline void add_flood_line(char *prefix, int kind, int weigth) +{ + flood_list.Insert(new FloodLine(prefix, kind, weigth)); +} + +//----------------------------------------------------------------------------- + +// This function is called for all mode changes +// signe indicates if it's a + or - mode change +// param is NULL for mode change without parameters + +void IRC_ONE_MODE(char *where, char *who, int signe, char mode, char *param) +{ + char *c, *banid; + char buffer[3]; + + if(signe < 0) buffer[0] = '-'; else buffer[0] = '+'; + buffer[1] = mode; + buffer[2] = '\0'; + if(param != NULL) uncap(param); + + clean_delay_mode_change(where, buffer, param); + +#ifdef SCREEN_OUTPUT + cout<<"ONE MODE CHANGE CHANNEL ("<0) add_mode(where, "-b", param); + else add_mode(where, "+b", param); + } + else + { + if(signe>0) + mode_change_list.Insert(new DelayModeChange(where, "-b", param, + deban_delay)); + else + mode_change_list.Insert(new DelayModeChange(where, "+b", param, + deban_delay)); + } + } + + if(signe > 0) + { + banid_list.Add(param); + banid = clean_banid(param); + if(strcmp(banid, param) != 0) smart_ban(where, banid); + delete banid; + } + else banid_list.Remove(param); + break; + + case 'o': + if(is_hostname(who)) + { + c = prefix_from_nick(param); + if((signe > 0) && (level_person(c, NULL) < LEVEL_FRIEND)) + add_mode(where, "-o", param); + if((signe < 0) && (level_person(c, NULL) >= LEVEL_FRIEND)) + add_mode(where, "+o", param); + } + else + if((signe<0) && + (level_person(who, NULL) < + level_person(prefix_from_nick(param), NULL)) && + (level_person(prefix_from_nick(param), NULL) > 0)) + { + c = cut_nick_from_prefix(who); + add_mode(where, "-o", c); + add_mode(where, "+o", param); + delete c; + } + break; + + case 'i': + if(signe>0) if(is_hostname(who) || mode_protect_on) + add_mode(where, "-i", NULL); + break; + + case 'l': + if(signe>0) if(is_hostname(who) || mode_protect_on) + add_mode(where, "-l", NULL); + break; + + case 'p': + if(signe>0) if(is_hostname(who) || mode_protect_on) + add_mode(where, "-p", NULL); + break; + + case 'k': + if(signe>0) if(is_hostname(who) || mode_protect_on) + add_mode(where, "-k", param); + break; + + case 'm': + if(is_hostname(who)) + { + if(signe>0) add_mode(where, "-m", NULL); + } + else if(mode_protect_on) + { + if(level_person(who, NULL) < LEVEL_OP) + { + if(signe>0) add_mode(where, "-m", NULL); + else add_mode(where, "+m", NULL); + c = cut_nick_from_prefix(who); + add_mode(where, "-o", c); + delete c; + } + } + break; + + case 's': + if(signe>0) if(is_hostname(who) || mode_protect_on) + add_mode(where, "-s", NULL); + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- + +// This function is called after a "MODE" command +// You should not modify it, unless you find a bug. +// Modify only IRC_ONE_MODE() + +void IRC_MODE(char *where, char *who, char *mode, char **params) +{ + int param_no, signe; + + param_no = 0; + signe = 1; + while(*mode != '\0') + { + switch(*mode) + { + case '+': + signe = 1; + break; + case '-': + signe = -1; + break; + default: + if((*mode == 'l') || (*mode == 'v') || + (*mode == 'k') || (*mode == 'o') || + (*mode == 'b')) + IRC_ONE_MODE(where, who, signe, *mode, params[param_no++]); + else + IRC_ONE_MODE(where, who, signe, *mode, NULL); + + break; + } + mode++; + } +} + +//----------------------------------------------------------------------------- + +void get_one_line(ifstream *s, char *buffer, int buffer_size) +{ + char *c; + int d, ok; + c = buffer; + ok = 1; + while(ok) + { + d = s->get(); + if(d>0) + { + if(d != '\n') *c++ = d; + else ok = 0; + } + else ok = 0; + ok &= (cfail()) + { + clear_levels(); + clear_shit(); + restricted_list.Clear(); + while(!file->eof()) + { + get_one_line(file, IRC_buffer, BUFFER_SIZE); + if(strlen(IRC_buffer) > 0) + { + r = IRC_buffer; + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + if(strcmp(buffer, "FRIEND") == 0) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + level = atoi(buffer); + if(r!= NULL) + { + r = next_word(pattern, r, SMALL_BUFFER_SIZE); + uncap(pattern); + level_list.Insert(new Person(pattern, level, r)); + } + } + } + else if(strcmp(buffer, "RESTRICTED") == 0) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + cout<<"RESTRICTED->"< *welcome; + NodeList *person; + NodeList *restricted; + int error; + + file = new ofstream(name); + if(!file->fail()) + { + (*file)<<"% Config file for a TropBot " VERSION "\n"; + (*file)<<"\n"; + + (*file)<<"OPDELAY "<next) + { + (*file)<<"FRIEND "<body->level<<" "<body->pattern; + if(person->body->passwd != NULL) (*file)<<" "<body->passwd; + (*file)<<"\n"; + } + + (*file)<<"\n"; + + for(restricted=restricted_list.first; + restricted != NULL; + restricted=restricted->next) + (*file)<<"RESTRICTED "<body<<"\n"; + + (*file)<<"\n"; + + for(welcome=shit_list.first; welcome != NULL; welcome=welcome->next) + (*file)<<"SHIT "<body->pattern<<" "<< + welcome->body->time_max - current_time<<" "<< + welcome->body->comment<<"\n"; + + error = 0; + } + else error = 1; + + delete file; + + return error; +} + +//----------------------------------------------------------------------------- + +void notice_list_by_sites(DCCChat *chat, char *nick, char *pattern) +{ + NodeList *node1, *node2; + int *ok, nb, k, l, level, put_blank; + char *c, *d, *buf; + int nb_total, nb_person[LEVEL_MAX+1]; + + nb_total = 0; + for(k=0; k"<"<body<<"\n"; + + if((pattern == NULL) || match_pattern(pattern, node1->body)) + { + c = adr_beginning(node1->body); + put_blank = 0; + l = k; + node2 = node1; + while(node2 != NULL) + { + d = adr_beginning(node2->body); + if(are_same_site(c, d)) + { + d = node2->body; + level = level_person(d, NULL); + nb_total++; + nb_person[level]++; + if(put_blank) *buf++ = '/'; else put_blank = 1; + + if(level == LEVEL_OP) *buf++ = '\002'; + else if(level >= LEVEL_DEFENCE) *buf++ = '\026'; + + while((*d != '!') && (*d != '\0')) *buf++ = *d++; + + if(level == LEVEL_OP) *buf++ = '\002'; + else if(level >= LEVEL_DEFENCE) *buf++ = '\026'; + + ok[l] = 1; + + if(buf > IRC_buffer + NB_CHAR_MAX) + { + *buf++ = '\n'; *buf++ = '\0'; + tell(chat, nick, IRC_buffer); + buf = IRC_buffer; + } + } + + do + { + l++; + node2 = node2->next; + } + while((node2 != NULL) && (ok[l] == 1)); + } + + concat(buf, " "); + } + + do + { + k++; + node1 = node1->next; + } + while((node1 != NULL) && (ok[k] == 1)); + } + + *buf++ = '\n'; *buf++ = '\0'; + tell(chat, nick, IRC_buffer); + + sprintf(IRC_buffer, "Total %d (\002%d\002)\n", + nb_total, + nb_person[LEVEL_OP] + nb_person[LEVEL_DEFENCE] + + nb_person[LEVEL_MASTER]); + tell(chat, nick, IRC_buffer); + + delete ok; +} + +//----------------------------------------------------------------------------- + +void notice_shit_list(DCCChat *chat, char *nick, char *pattern, int matched) +{ + int n; + char *string_duration; + NodeList *node; + + if(shit_list.Lenght() == 0) tell(chat, nick, "\002Empty shit-list\002\n"); + else + { + n = 0; + for(node = shit_list.first; node != NULL; node=node->next) + if((matched && match_pattern(pattern, node->body->pattern)) + || (!matched && match_pattern(node->body->pattern, pattern))) + { + n++; + string_duration = seconds_to_string(node->body->time_max + - current_time); + + sprintf(IRC_buffer, "%s for %s (%s)\n", + node->body->pattern, + string_duration, + node->body->comment); + tell(chat, nick, IRC_buffer); + delete string_duration; + } + + if(n == 0) tell(chat, nick, "\002No corresponding shits\002\n"); + } +} + +//----------------------------------------------------------------------------- + +void add_in_history(char *prefix, char *action) +{ + NodeList *node, *pred, *next; + char *s, *t; + int n; + + s = new char[strlen(prefix) + strlen(action) + 4]; + t = s; + concat(t, prefix); + concat(t, " : "); + concat(t, action); + *t++ = '\0'; + history.Add(s); + + n = 0; + pred = NULL; next = NULL; node = history.first; + while(node != NULL) + { + n++; + next = node->next; + if(n>HISTORY_SIZE) + { + if(pred == NULL) history.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +void notice_history(DCCChat *chat, char *nick) +{ + NodeList *node; + + for(node = history.first; node != NULL; node = node->next) + { + sprintf(IRC_buffer, "%s\n", node->body); + tell(chat, nick, IRC_buffer); + } +} + +//----------------------------------------------------------------------------- + +int dont_flood_server() +{ + return current_time >= last_answer_time + DELAY_ANSWERS; +} + +void msg_users(DCCChat *chat, char *nick, char *pattern, char *msg) +{ + NodeList *node; + char *nick_user; + int nb; + + sprintf(IRC_buffer, "\002[msg to %s]\002 %s\n", pattern, msg); + tell(chat, nick, IRC_buffer); + + nb = 0; + for(node = present_people.first; node != NULL; node = node->next) + { + if(match_pattern(pattern, node->body)) + { + nb++; + nick_user = cut_nick_from_prefix(node->body); + sprintf(IRC_buffer, "PRIVMSG %s :\002[msg to %s]\002 %s\n", + nick_user, pattern, msg); + write_irc(IRC_buffer); + delete nick_user; + } + } + + sprintf(IRC_buffer, "\002[msg sent to %d user(s)]\002\n", nb); + tell(chat, nick, IRC_buffer); +}; + +//----------------------------------------------------------------------------- + +#define CMD_DEFAULT 0 +#define CMD_JOIN 1 +#define CMD_HOME 2 +#define CMD_BAN 3 +#define CMD_SBAN 4 + +void tropbot_cmd(DCCChat *chat, char *prefix, char *nick, char *msg) +{ + char buffer[SMALL_BUFFER_SIZE], buffer_time[SMALL_BUFFER_SIZE]; + char *r, *s, *c, *banid; + char *string_duration; + int cmd, level, l, no_authorized, od; + int duration; + NodeList *node; + + r = msg; + + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + + if(*buffer == '&') + { + level = level_person(prefix, buffer+1); + if(r != NULL) r = next_word(buffer, r, SMALL_BUFFER_SIZE); + else return; + } + else level = level_person(prefix, NULL); + + cmd = CMD_DEFAULT; + if(eq("join", buffer)) cmd = CMD_JOIN; + else if(eq("home", buffer)) cmd = CMD_HOME; + else if(eq("b", buffer) || eq("ban", buffer)) cmd = CMD_BAN; + else if(eq("sb", buffer) || eq("siteban", buffer)) cmd = CMD_SBAN; + + no_authorized = 0; + + if(eq("ms", buffer) || eq("myshit", buffer)) + { + if(dont_flood_server()) notice_shit_list(chat, nick, prefix, 0); + } + else if(eq("sl", buffer) || eq("shitlist", buffer)) + { + if(level >= LEVEL_FRIEND) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + uncap(buffer); + + if(is_pattern(buffer)) notice_shit_list(chat, nick, buffer, 1); + else + { + wait_list.Insert(new WaitInfos(buffer, NULL, chat, nick, + MODE_SHITLIST, 0)); + + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + else notice_shit_list(chat, nick, "*", 1); + } + else no_authorized = 1; + } + else if(eq("www", buffer)) + { + if(dont_flood_server()) + tell(chat, nick, "THX-1138's home page at " + "http://www.eleves.ens.fr:8080/home/fleuret/\n"); + } + else if(eq("w", buffer) || eq("who", buffer)) + { + if(level >= LEVEL_FRIEND) + { + if(r == NULL) notice_list_by_sites(chat, nick, NULL); + else while(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + /* + if(!is_pattern(buffer)) + { + s = buffer; + while(*s != '\0') s++; + concat(s, "!*"); + *s++ == '\0'; + } + */ + uncap(buffer); + notice_list_by_sites(chat, nick, buffer); + } + } + else no_authorized = 1; + } + else if(eq("ao", buffer) || eq("antifloodoff", buffer)) + { + if(r != NULL) + { + r = next_word(buffer_time, r, SMALL_BUFFER_SIZE); + duration = string_to_seconds(buffer_time); + anti_flood_off_until = current_time + duration; + } + if(anti_flood_off_until > current_time) + { + string_duration = seconds_to_string(anti_flood_off_until - + current_time); + sprintf(IRC_buffer, "Anti-flood off for %s\n", + string_duration); + delete string_duration; + + tell(chat, nick, IRC_buffer); + } + else tell(chat, nick, "Anti-flood on\n"); + } + else if(eq("msg", buffer) || eq("message", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + if(r != NULL) + { + if(is_pattern(buffer)) msg_users(chat, nick, buffer, r); + else + { + uncap(buffer); + wait_list.Insert(new WaitInfos(buffer, r, chat, + nick, + MODE_MSG, 0)); + + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + } + } + else no_authorized = 1; + } + else if(eq("h", buffer) || eq("history", buffer)) + { + if(level >= LEVEL_OP) notice_history(chat, nick); + else no_authorized = 1; + } + else if(eq("level", buffer)) + { + if(level >= LEVEL_FRIEND) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + uncap(buffer); + + wait_list.Insert(new WaitInfos(buffer, r, chat, nick, + MODE_LEVEL, 0)); + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + else + { + sprintf(IRC_buffer, "%s is level %d\n", prefix, + level_person(prefix, NULL)); + tell(chat, nick, IRC_buffer); + } + } + else no_authorized = 1; + } + else if(eq("help", buffer)) + { + if(dont_flood_server() || (level >= LEVEL_FRIEND)) + { + if(father) + { + sprintf(IRC_buffer, + "control_char '%c' op_delay %ds deban_delay %ds" + " your level is %d. I am a father with %d sons.\n", + control_char, op_delay, deban_delay, + level, nb_sons); + tell(chat, nick, IRC_buffer); + } + else + { + sprintf(IRC_buffer, + "control_char '%c' op_delay %ds deban_delay %ds" + " your level is %d. I'm a clone.\n", + control_char, op_delay, deban_delay, level); + tell(chat, nick, IRC_buffer); + } + } + + if(level >= LEVEL_FRIEND) + { + tell(chat, nick, "\002help\002 \002www\002 \002\037s\037\002hit\037l\037\002ist\002 [!] \002level\002 [] \002\037w\037\002ho\002 \002\037m\037\002y\037s\037\002hit\002\n"); + } + + if(level >= LEVEL_OP) + { + tell(chat, nick, "\002\037a\037\002ntiflood\037o\037\002ff\002 [] \002\037a\037\002lert\002 \002home\002 \002op\002 [{ list}] \002join\002 <#channel> \002nick\002 \002\037p\037\002rune\037b\037\002an\002 \002\037d\037\002e\037b\037\002an\002 { list} \002\037f\037\002ilter\037k\037\002ick\002 ! [] \002\037s\037\002hit/\037s\037\002hit\037s\037\002ite/\037s\037\002hit\037h\037\002ost\002 ! [ []] \002\037u\037\002n\037s\037\002hit\002 {! list} \002\037pu\037\002n\037s\037\002hit\002 { list} \002\037b\037\002an/\037s\037\002ite\037b\037\002an\002 ! \002synch\002 \002\037h\037\002istory\002\n \002\037m\037\002es\037s\037\002a\037g\037\002e\002 ! \n"); + } + + if(level >= LEVEL_MASTER) + { + tell(chat, nick, "\002reset\002 \002load\002 [] \002save\002 [] \002friend\002 [] \002clone\002 [] \002die\002 \002server\002 [] \002controlchar\002 \002opdelay\002 \002debandelay\002 \n"); + } + + if(chat != NULL) + { + tell(chat, nick, "\002\037u\037\002sers\002 \002\037t\037\002alk\002 \n"); + } + + } + else if((cmd == CMD_JOIN) || (cmd == CMD_HOME)) + { + if(level >= LEVEL_OP) + { + if(global_state == STATE_WAIT) + { + sprintf(IRC_buffer, "PART %s\n", current_channel); + write_irc(IRC_buffer); + if(cmd == CMD_HOME) + strncpy(wanted_channel, home_channel, SMALL_BUFFER_SIZE); + else + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + strncpy(wanted_channel, buffer, SMALL_BUFFER_SIZE); + } + uncap(wanted_channel); + sprintf(IRC_buffer, "JOIN %s\n", wanted_channel); + write_irc(IRC_buffer); + } + else + tell(chat, nick, "Can't join right now, retry in a while\n"); + } else no_authorized = 1; + } + else if(eq("op", buffer)) + { + if(level >= LEVEL_OP) + { + if(r == NULL) add_mode(current_channel, "+o", nick); + else + while(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + add_mode(current_channel, "+o", buffer); + } + } + else no_authorized = 1; + } + else if(eq("nick", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(wanted_nick, r, SMALL_BUFFER_SIZE); + sprintf(buffer, "NICK %s\n", wanted_nick); + write_irc(buffer); + } + } else no_authorized = 1; + } + else if(eq("db", buffer) || eq("deban", buffer)) + { + if(level >= LEVEL_FRIEND) + { + while(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + if(is_pattern(buffer)) + { + NodeList *node; + for(node = banid_list.first; node != NULL; node = node->next) + if(match_pattern(buffer, node->body)) + add_mode(current_channel, "-b", node->body); + } + else + { + wait_list.Insert(new WaitInfos(buffer, NULL, chat, nick, + MODE_UNBAN_NICK, 0)); + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + } else no_authorized = 1; + } + else if((cmd == CMD_BAN) || (cmd == CMD_SBAN)) + { + if(level >= LEVEL_OP) + { + while(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + uncap(buffer); + + if(is_pattern(buffer)) + { + banid = clean_banid(buffer); + smart_ban(current_channel, banid); + delete banid; + } + else + { + switch(cmd) + { + case CMD_BAN: + wait_list.Insert(new WaitInfos(buffer, r, chat, nick, + MODE_BAN_NICK, 0)); + break; + case CMD_SBAN: + wait_list.Insert(new WaitInfos(buffer, r, chat, nick, + MODE_BAN_SITE, 0)); + break; + default: + break; + } + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + } + else no_authorized = 1; + } + else if(eq("pb", buffer) || eq("pruneban", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + NodeList *node; + int n; + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + n = atoi(buffer); + if(n >= 0) + { + for(node = banid_list.first; node != NULL; + node = node->next) + if(n == 0) add_mode(current_channel, "-b", node->body); + else n--; + } + } + } else no_authorized = 1; + } + else if(eq("synch", buffer)) + { + if(level >= LEVEL_OP) synch(current_channel); + else no_authorized = 1; + } + else if(eq("s", buffer) || eq("shit", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + + if(is_pattern(buffer)) + { + if(r != NULL) + { + r = next_word(buffer_time, r, SMALL_BUFFER_SIZE); + duration = string_to_seconds(buffer_time); + } + else duration = DEFAULT_SHIT_TIME; + + if(r == NULL) r = "Shit nick"; + + uncap(buffer); + banid = clean_banid(buffer); + smart_shit(chat, nick, banid, r, duration); + delete banid; + } + else + { + if(r != NULL) + { + r = next_word(buffer_time, r, SMALL_BUFFER_SIZE); + duration = string_to_seconds(buffer_time); + } + else duration = DEFAULT_SHIT_TIME; + + if(r == NULL) r = "Shit nick"; + + uncap(buffer); + wait_list.Insert(new WaitInfos(buffer, r, chat, nick, + MODE_SHIT_NICK, + duration)); + + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + } + else no_authorized = 1; + } + + else if(eq("fk", buffer) || eq("filterkick", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + uncap(buffer); + if(is_pattern(buffer)) + { + if(r == NULL) r = "Filter kick"; + filter_kick(current_channel, buffer, r); + } + else + { + if(r == NULL) r = "Filter Kick"; + wait_list.Insert(new + WaitInfos(buffer, r, chat, nick, + MODE_FILTER_KICK, 0)); + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + } + else no_authorized = 1; + } + + else if(eq("a", buffer) || eq("alert", buffer)) + { + if(level >= LEVEL_OP) alert(current_channel, chat, nick); + else no_authorized = 1; + } + + else if(eq("sh", buffer) || eq("shithost", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + + if(r != NULL) + { + r = next_word(buffer_time, r, SMALL_BUFFER_SIZE); + duration = string_to_seconds(buffer_time); + } + else duration = DEFAULT_SHIT_TIME; + + if(r == NULL) r = "Shit host"; + + uncap(buffer); + wait_list.Insert(new WaitInfos(buffer, r, chat, nick, + MODE_SHIT_HOST, duration)); + + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + else no_authorized = 1; + } + else if(eq("ss", buffer) || eq("shitsite", buffer)) + { + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + + if(r != NULL) + { + r = next_word(buffer_time, r, SMALL_BUFFER_SIZE); + duration = string_to_seconds(buffer_time); + } + else duration = DEFAULT_SHIT_TIME; + + if(r == NULL) r = "Shit site"; + + uncap(buffer); + wait_list.Insert(new WaitInfos(buffer, r, chat, nick, + MODE_SHIT_SITE, duration)); + + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + else no_authorized = 1; + } + else if(eq("pus", buffer) || eq("punshit", buffer)) + { + if(level >= LEVEL_OP) + { + while(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + + if(strlen(buffer) > 0) + { + NodeList *node, *next, *pred; + pred = NULL; + uncap(buffer); + node = shit_list.first; + while(node != NULL) + { + next = node->next; + if(match_pattern(buffer, node->body->pattern)) + { + smart_unban(current_channel, node->body->pattern); + if(pred == NULL) shit_list.first = next; + else pred->next = next; + sprintf(IRC_buffer, + "Unshit %s (%s)\n", + node->body->pattern, + node->body->comment); + tell(chat, nick, IRC_buffer); + delete node->body; + delete node; + } + else pred = node; + node = next; + } + } + } + } + else no_authorized = 1; + } + else if(eq("us", buffer) || eq("unshit", buffer)) + { + if(level >= LEVEL_OP) + { + while(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + + uncap(buffer); + if(is_pattern(buffer)) + { + NodeList *node, *next, *pred; + pred = NULL; + uncap(buffer); + node = shit_list.first; + while(node != NULL) + { + next = node->next; + if(strcmp(node->body->pattern, buffer) == 0) + { + if(pred == NULL) shit_list.first = next; + else pred->next = next; + sprintf(IRC_buffer, + "Unshit %s (%s)\n", + buffer, node->body->comment); + tell(chat, nick, IRC_buffer); + delete node->body; + delete node; + } + else pred = node; + node = next; + } + } + else + { + wait_list.Insert(new WaitInfos(buffer, NULL, chat, nick, + MODE_UNSHIT_NICK, 0)); + + sprintf(IRC_buffer, "WHOIS %s\n", buffer); + write_irc(IRC_buffer); + } + } + } + else no_authorized = 1; + } + else if(eq("friend", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + if(r != NULL) + { + r = next_word(buffer_time, r, SMALL_BUFFER_SIZE); + l = atoi(buffer_time); + if((l > 0) && (l<=LEVEL_MASTER)) + { + level_list.Insert(new Person(buffer, l, r)); + sprintf(IRC_buffer, + "Adding %s with level %d and passwd %s\n", + buffer, l, r); + tell(chat, nick, IRC_buffer); + SaveConfigFile(config_file); + } + } + } + } + else no_authorized = 1; + } + else if(eq("opdelay", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + od = atoi(buffer); + if((od>=0) && (od <= 60)) + { + op_delay = od; + sprintf(IRC_buffer,"Oping delay set to %ds\n", op_delay); + tell(chat, nick, IRC_buffer); + } + } + } + else no_authorized = 1; + } + else if(eq("debandelay", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + od = atoi(buffer); + if((od>=0) && (od <= 60)) + { + deban_delay = od; + sprintf(IRC_buffer, + "Deban delay set to %ds\n", + deban_delay); + tell(chat, nick, IRC_buffer); + } + } + } + else no_authorized = 1; + } + else if(eq("controlchar", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + control_char = buffer[0]; + sprintf(IRC_buffer, "Control char set to '%c'\n", + control_char); + tell(chat, nick, IRC_buffer); + } + } + else no_authorized = 1; + } + else if(eq("die", buffer)) + { + if(level >= LEVEL_MASTER) + { + sprintf(IRC_buffer, "QUIT :Killed by %s\n", nick); + write_irc(IRC_buffer); + alive = 0; + } + else no_authorized = 1; + } + else if(eq("server", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) + { + next_word(wanted_server, r, SMALL_BUFFER_SIZE); + if(r != NULL) + { + next_word(buffer, r, SMALL_BUFFER_SIZE); + wanted_port = atoi(buffer); + if(wanted_port == 0) wanted_port = 6667; + } + else wanted_port = DEFAULT_PORT; + + sprintf(IRC_buffer, + "Trying to connect %s:%d\n", + wanted_server, wanted_port); + tell(chat, nick, IRC_buffer); + + sprintf(IRC_buffer, "QUIT :Changing server\n"); + write_irc(IRC_buffer); + + cerr<<"KILLING CONNECTION : Changing server\n"; + cerr.flush(); + kill_connection(); + } + } + else no_authorized = 1; + } + else if(eq("clone", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(father) + { + if(nb_sons < NB_SONS_MAX) + { + if(r != NULL) next_word(buffer, r, SMALL_BUFFER_SIZE); + clone(buffer); + nb_sons++; + } + else tell(chat, nick, "Too many clones, can't clone\n"); + } + else + { + tell(chat, nick, "I'm a clone, can't clone\n"); + } + } + else no_authorized = 1; + } + else if(eq("save", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) r = next_word(config_file, r, SMALL_BUFFER_SIZE); + if(SaveConfigFile(config_file)) + { + sprintf(IRC_buffer, + "Can't save the %s configuration file\n", + config_file); + } + else + { + sprintf(IRC_buffer, + "Saving the %s configuration file\n", + config_file); + } + tell(chat, nick, IRC_buffer); + } + else no_authorized = 1; + } + else if(eq("load", buffer)) + { + if(level >= LEVEL_MASTER) + { + if(r != NULL) r = next_word(config_file, r, SMALL_BUFFER_SIZE); + if(LoadConfigFile(config_file)) + { + sprintf(IRC_buffer, + "Can't load the %s configuration file\n", + config_file); + } + else + { + sprintf(IRC_buffer, + "Loading the %s configuration file\n", + config_file); + } + tell(chat, nick, IRC_buffer); + } + else no_authorized = 1; + } + else if(eq("reset", buffer)) + { + if(level >= LEVEL_MASTER) + { + sprintf(IRC_buffer, "QUIT :reset command sent by %s\n", nick); + write_irc(IRC_buffer); + cerr<<"KILLING CONNECTION : Die\n"; + cerr.flush(); + kill_connection(); + } + else no_authorized = 1; + } + + // DCC CHAT only commands + + else if((chat != NULL) && (eq("t", buffer) || eq("talk", buffer))) + { + NodeList *node; + sprintf(IRC_buffer, "<%s> %s\n", prefix, r); + for(node = dcc_chat_list.first; node != NULL; node = node->next) + if(node->body != chat) tell(node->body, NULL, IRC_buffer); + } + else if((chat != NULL) && (eq("u", buffer) || eq("users", buffer))) + { + for(node = dcc_chat_list.first; node != NULL; node = node->next) + { + sprintf(IRC_buffer, "%s\n", node->body->prefix); + tell(chat, NULL, IRC_buffer); + } + } + + // Unknown command + + else if(dont_flood_server()) + { + sprintf(IRC_buffer, "Unknown command \002%s\002\n", buffer); + tell(chat, nick, IRC_buffer); + } + } + + if(no_authorized) + { + if(dont_flood_server()) + { + sprintf(IRC_buffer, + "You are not authorized to use \002%s\002\n", buffer); + tell(chat, nick, IRC_buffer); + } + } + else + { + r = msg; + if(*r == '&') r = next_word(buffer, r, SMALL_BUFFER_SIZE); + add_in_history(prefix, r); + } +} + +//----------------------------------------------------------------------------- + +int accepting_dcc_chat(char *prefix, char *nick, int level, char *r) +{ + NodeList *node; + int socket, port_number; + char host[SMALL_BUFFER_SIZE], port[SMALL_BUFFER_SIZE]; + DCCChat *dcc_chat; + + if(r == NULL) return 1; + else r = next_word(host, r, SMALL_BUFFER_SIZE); + + if(strcmp(host, "chat") != 0) return 1; + + if(r == NULL) return 1; + else r = next_word(host, r, SMALL_BUFFER_SIZE); + + if(r == NULL) return 1; + else r = next_word(port, r, SMALL_BUFFER_SIZE); + + port_number = atoi(port); + socket = call_socket(host, port_number); + + if(socket <= 0) return 1; + else + { + dcc_chat = new DCCChat(prefix, socket, port_number); + dcc_chat_list.Insert(dcc_chat); + + FD_SET(socket, &ready); + + sprintf(IRC_buffer, + "Welcome to \002TropBot\002 " VERSION " on a DCC CHAT\n" + "You are known as %s, your level is %d.\n" + "You are client #%d on DCC CHAT\n", + prefix, level, dcc_chat_list.Lenght()); + + write(socket, IRC_buffer, strlen(IRC_buffer)); + + sprintf(IRC_buffer, "%s arrives with level %d.\n", prefix, level); + for(node=dcc_chat_list.first; node!= NULL; node = node->next) + if(node->body != dcc_chat) tell(node->body, NULL, IRC_buffer); + + return 0; + } +} + +//----------------------------------------------------------------------------- + +// This function is called after a NOTICE + +void IRC_NOTICE(char *prefix, + char *who, char *msg, char **slice_ctcp, int n_ctcp) +{ + uncap(who); + if(strcmp(who, current_channel) == 0) add_flood_line(prefix, FL_NOTICE, 1); +} + +void IRC_PRIVMSG(char *prefix, + char *who, char *msg, char **slice_ctcp, int n_ctcp) +{ + int k, version, ping, level; + char *nick, *r; + char buffer[SMALL_BUFFER_SIZE]; + + uncap(who); + nick = cut_nick_from_prefix(prefix); + if(strcmp(who, current_channel) == 0) + { + add_flood_line(prefix, FL_PUBLIC, 1); + if(n_ctcp > 0) add_flood_line(prefix, FL_CTCP, n_ctcp); + } + + if(msg != NULL) if(strlen(msg)>0) + { + if(msg[0] == control_char) tropbot_cmd(NULL, prefix, nick, msg+1); + else if(strcmp(who, real_nick) == 0) tropbot_cmd(NULL, prefix, nick, msg); + } + + + + version = 0; + ping = 0; + for(k=0; k" + "\001\n", + nick); + write_irc(IRC_buffer); + } + } + // Reply the CTCP PING + else if(eq("PING", buffer) && !ping) + { + ping = 1; + if(r != NULL) + if(dont_flood_server()) + { + ping = 1; + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + sprintf(IRC_buffer, + "NOTICE %s :\001PING %s\001\n", nick, buffer); + write_irc(IRC_buffer); + } + } + // DCC (chat) + else if(eq("DCC", buffer)) + { + level = level_person(prefix, NULL); + if(level >= LEVEL_OP) + { + if(r != NULL) + { + r = next_word(buffer, r, SMALL_BUFFER_SIZE); + if(eq("CHAT", buffer)) + { + if(accepting_dcc_chat(prefix, nick, level, r) != 0) + { + sprintf(IRC_buffer, "NOTICE %s :\002Error\002" + " can't connect\n", nick); + write_irc(IRC_buffer); + } + } + else + { + sprintf(IRC_buffer, + "NOTICE %s :I can only DCC CHAT\n", nick); + write_irc(IRC_buffer); + } + } + } + else if(dont_flood_server()) + { + sprintf(IRC_buffer, + "NOTICE %s :Sorry, can't accept DCC from you\n", + nick, buffer); + write_irc(IRC_buffer); + } + } + } + delete nick; +} + +//----------------------------------------------------------------------------- + +// This function is called after a QUIT + +void IRC_QUIT(char *prefix) +{ + present_people.Remove(prefix); +} + +//----------------------------------------------------------------------------- + +// This function is called after a NICK + +void IRC_NICK(char *prefix, char *nick) +{ + char *s, *t; + int ok; + add_flood_line(prefix, FL_NICK, 1); + present_people.Remove(prefix); + s = IRC_buffer; + t = nick; while(*t != '\0') *s++=*t++; // copy the nick + *s++='!'; // put the '!' + t = reach_loginathost(prefix); + while(*t != '\0') *s++=*t++; // copy the user@host + *s = '\0'; // end of string + present_people.Add(IRC_buffer); // hop ! + + ok = 1; s = prefix; t = real_nick; + while(ok && (*t != '\0') && (*s != '\0')) ok = (*s++ == *t++); + if(ok && (*t == '\0') && (*s == '!')) + { + strncpy(real_nick, nick, SMALL_BUFFER_SIZE); + uncap(real_nick); + } +} + +//----------------------------------------------------------------------------- + +// This function is called after a PART + +void IRC_PART(char *prefix, char *where) +{ + char *nick; + add_flood_line(prefix, FL_PART, 1); + present_people.Remove(prefix); + nick = cut_nick_from_prefix(prefix); + if(strcmp(real_nick, nick) == 0) + { + present_people.Clear(); + in_channel = 0; + } + delete nick; +} + +//----------------------------------------------------------------------------- + +// This function is called after a KICK + +void IRC_KICK(char *prefix, char *where, char *victim_nick) +{ + char *c; + uncap(victim_nick); + + if(strcmp(victim_nick, real_nick) == 0) + { + present_people.Clear(); + in_channel = 0; + sprintf(IRC_buffer, "JOIN %s\n", wanted_channel); + write_irc(IRC_buffer); + } + + if((level_person(prefix, NULL) < level_person(prefix_from_nick(victim_nick), NULL)) && + (level_person(prefix_from_nick(victim_nick), NULL) > 0)) + { + c = cut_nick_from_prefix(prefix); + add_mode(where, "-o", c); + delete c; + } + + c = prefix_from_nick(victim_nick); + if(c != NULL) present_people.Remove(c); + else cerr<<"** ERROR : non present person has been kicked out **\n"; +} + +//----------------------------------------------------------------------------- + +int check_restricted(char *where, char *prefix) +{ + char pattern[SMALL_BUFFER_SIZE]; + NodeList *node; + char *p, *s; + int n, duration; + + p = adr_beginning(prefix); + + node = present_people.first; + n = 0; + while((node != NULL) && (n < 2)) + { + s = adr_beginning(node->body); + if(strcmp(s, p) == 0) n++; + node = node->next; + } + + if(n >= 2) + { + s = pattern; + concat(s, "*!*@"); + concat(s, p); + *s++ = '\0'; + duration = DEFAULT_RESTRICTED_TIME; + smart_shit(NULL, NULL, pattern, "Restricted site : no clones", duration); + smart_ban(where, pattern); + send_mode(where); + filter_kick(where, pattern, "Restricted site : no clones"); + return 1; + } + else return 0; +} + +// This function is called after a JOIN + +void IRC_JOIN(char *prefix, char *where) +{ + char *nick; + int l, restricted; + NodeList *node; + + nick = cut_nick_from_prefix(prefix); + + if(strcmp(real_nick, nick) == 0) + { + strncpy(current_channel, where, SMALL_BUFFER_SIZE); + sprintf(IRC_buffer, "WHO %s\n", where); + write_irc(IRC_buffer); + present_people.Clear(); + global_state = STATE_GETING_WHO; + in_channel = 1; + } + else + { + add_flood_line(prefix, FL_JOIN, 1); + + uncap(where); + present_people.Add(prefix); + + if(restricted_list.Matches(prefix)) + restricted = check_restricted(where, prefix); + else restricted = 0; + + if(!restricted) + { + l = level_person(prefix, NULL); + + if(l >= LEVEL_OP) + { + if(l >= LEVEL_DEFENCE) add_mode(where, "+o", nick); + else + mode_change_list.Insert(new DelayModeChange(where, "+o", + nick, op_delay)); + } + else + { + if(banid_list.Matches(prefix)) + { + sprintf(IRC_buffer, "KICK %s %s :You are banned\n", where, nick); + write_irc(IRC_buffer); + } + else + { + for(node = shit_list.first; node != NULL; node = node->next) + if(match_pattern(node->body->pattern, prefix)) + { + smart_ban(where, node->body->pattern); + send_mode(current_channel); + + sprintf(IRC_buffer, "KICK %s %s :%s\n", + where, nick, node->body->comment); + + write_irc(IRC_buffer); + } + } + } + } + } + + delete nick; +} + +//----------------------------------------------------------------------------- + +void IRC_RPL_BANLIST(char *prefix, char *channel, char *banid) +{ + banid_list.Add(banid); +} + +void IRC_ENDOFBANLIST(char *prefix) +{ + banid_list.Reverse(); + global_state = STATE_WAIT; +} + +void IRC_RPL_ENDOFWHOIS(char *prefix, char *nick) +{ + NodeList *node, *pred, *next; + + uncap(nick); + node = wait_list.first; + pred = NULL; + while(node != NULL) + { + next = node->next; + if(strcmp(nick, node->body->nick) == 0) + { + sprintf(IRC_buffer, "Can't find %s\n", nick); + tell(node->body->chat, node->body->user, IRC_buffer); + + if(pred == NULL) wait_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +void IRC_RPL_WHOISUSER(char *prefix, + char *nick, char *login, char *host, char *real_name) +{ + char *c, *d, *banid; + int l; + char buffer[SMALL_BUFFER_SIZE]; + NodeList *node, *next, *pred; + + int no_pattern, one_login, all_machines; + + uncap(nick); + uncap(login); + uncap(host); + + pred = NULL; + node = wait_list.first; + while(node != NULL) + { + next = node->next; + + if(strcmp(nick, node->body->nick) == 0) + { + c = buffer; + + no_pattern = (node->body->mode == MODE_UNSHIT_NICK) || + (node->body->mode == MODE_LEVEL) || + (node->body->mode == MODE_UNBAN_NICK) || + (node->body->mode == MODE_SHITLIST);; + + one_login = (node->body->mode == MODE_SHIT_NICK) || + (node->body->mode == MODE_BAN_NICK); + + all_machines = (node->body->mode == MODE_SHIT_NICK) || + (node->body->mode == MODE_BAN_NICK) || + (node->body->mode == MODE_BAN_SITE) || + (node->body->mode == MODE_FILTER_KICK) || + (node->body->mode == MODE_SHIT_SITE) || + (node->body->mode == MODE_MSG); + + if(no_pattern) + { + concat(c, nick); + *c++ = '!'; + concat(c, login); + *c++ = '@'; + concat(c, host); + *c++ = '\0'; + } + else + { + if((*login == '~') || (*login == '+') || + (*login == '-') || (*login=='^')) + login++; + + concat(c, "*!*"); + + if(one_login) + { + + l = 0; + while((*login != '\0') && ((l<7) || *(login+1) == '\0')) + { *c++ = *login++; l++; } + if(*login != '\0') *c++ = '*'; + + } + *c++ = '@'; + + d = adr_beginning(host); + if(host != d) if(*(c-1) != '*') *c++ = '*'; + + if(all_machines) concat_pattern_from_host(c, d); + else + { + concat(c, d); + *c++ = '\0'; + } + + } + + uncap(buffer); + banid = clean_banid(buffer); + + if(node->body->mode == MODE_MSG) + msg_users(node->body->chat, node->body->user, + banid, node->body->comment); + + else if(node->body->mode == MODE_FILTER_KICK) + filter_kick(current_channel, banid, node->body->comment); + + else if((node->body->mode == MODE_BAN_NICK) || + (node->body->mode == MODE_BAN_SITE)) + smart_ban(current_channel, banid); + + else if((node->body->mode == MODE_SHIT_NICK) + || (node->body->mode == MODE_SHIT_SITE) + || (node->body->mode == MODE_SHIT_HOST)) + { + smart_shit(node->body->chat, + node->body->user, banid, node->body->comment, + node->body->duration); + + if(prefix_from_nick(nick) != NULL) + { + smart_ban(current_channel, banid); + send_mode(current_channel); + sprintf(IRC_buffer, "KICK %s %s :%s\n", + current_channel, nick, node->body->comment); + write_irc(IRC_buffer); + } + + } + + else if(node->body->mode == MODE_SHITLIST) + notice_shit_list(NULL, node->body->user, banid, 0); + + else if(node->body->mode == MODE_LEVEL) + { + sprintf(IRC_buffer, "%s is level %d\n", + banid, level_person(banid, NULL)); + tell(node->body->chat, node->body->user, IRC_buffer); + } + else if(node->body->mode == MODE_UNSHIT_NICK) + { + NodeList *node2, *pred, *next; + + node2 = shit_list.first; + pred = NULL; + while(node2 != NULL) + { + next = node2->next; + if(match_pattern(node2->body->pattern, banid)) + { + if(pred == NULL) shit_list.first = next; + else pred->next = next; + + sprintf(IRC_buffer, "Unshit %s (%s)\n", + node2->body->pattern, node2->body->comment); + + tell(node->body->chat, node->body->user, IRC_buffer); + + smart_unban(current_channel, node2->body->pattern); + + delete node2->body; + delete node2; + } + else pred = node2; + node2 = next; + } + } + else if(node->body->mode == MODE_UNBAN_NICK) + { + NodeList *node2; + for(node2 = banid_list.first; node2 != NULL; node2 = node2->next) + if(match_pattern(node2->body, banid)) + add_mode(current_channel, "-b", node2->body); + } + + + delete banid; + + if(pred == NULL) wait_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else pred = node; + node = next; + } +} + +// This function is called after a RPL_WHOREPLY + +void IRC_RPL_WHOREPLY(char *prefix, + char *where, + char *login, char *host, char *server, + char *nick, char *state) +{ + char *s, *t; + + uncap(where); + uncap(nick); + + if((global_state == STATE_GETING_WHO) && + (strcmp(where, current_channel) == 0)) + { + s = IRC_buffer; + t = nick; while(*t != '\0') *s++ = *t++; + *s++ = '!'; + t = login; while(*t != '\0') *s++ = *t++; + *s++ = '@'; + t = host; while(*t != '\0') *s++ = *t++; + *s++ = '\0'; + present_people.Add(IRC_buffer); + } + else cerr< 0) for(k=0; k"; + cout<<"\n"; +#endif + + if(prefix != NULL) uncap(prefix); + + // No prefix : server command + if(prefix == NULL) + { + if(n_cmd > 0) if(eq("PING", cmd)) + { + if(n_cmd > 2) sprintf(IRC_buffer, "PONG :%s\n", slice_cmd[1]); + else sprintf(IRC_buffer, "PONG\n"); + write_irc(IRC_buffer); + } + } + else + { + // If we are not registred yet, then get the registration + if(eq("002", cmd)) + { + if(!IRC_registred) + { + strncpy(real_nick, slice_cmd[1], SMALL_BUFFER_SIZE); + uncap(real_nick); + IRC_registred = 1; + sprintf(small_buffer, "MODE %s +i\n", real_nick); + write_irc(small_buffer); + sprintf(small_buffer, "JOIN %s\n", wanted_channel); + write_irc(small_buffer); + } + } + else if(eq("PRIVMSG", cmd)) + IRC_PRIVMSG(prefix, slice_cmd[1], slice_cmd[2], slice_ctcp, n_ctcp); + else if(eq("NOTICE", cmd)) + IRC_NOTICE(prefix, slice_cmd[1], slice_cmd[2], slice_ctcp, n_ctcp); + else if(eq("MODE", cmd)) + IRC_MODE(slice_cmd[1], prefix, slice_cmd[2], slice_cmd+3); + else if(eq("JOIN", cmd)) IRC_JOIN(prefix, slice_cmd[1]); + else if(eq("KICK", cmd)) IRC_KICK(prefix, slice_cmd[1], slice_cmd[2]); + else if(eq("QUIT", cmd)) IRC_QUIT(prefix); + else if(eq("NICK", cmd)) IRC_NICK(prefix, slice_cmd[1]); + else if(eq("PART", cmd)) IRC_PART(prefix, slice_cmd[1]); + else if(eq("311", cmd)) + IRC_RPL_WHOISUSER(prefix, + slice_cmd[2], slice_cmd[3], slice_cmd[4], + slice_cmd[6]); + else if(eq("318", cmd)) IRC_RPL_ENDOFWHOIS(prefix, slice_cmd[2]); + else if(eq("352", cmd)) + IRC_RPL_WHOREPLY(prefix, + slice_cmd[2], slice_cmd[3], slice_cmd[4], + slice_cmd[5], slice_cmd[6], slice_cmd[7]); + else if(eq("315", cmd)) IRC_RPL_ENDOFWHO(prefix, slice_cmd[1]); + else if(eq("367", cmd)) + IRC_RPL_BANLIST(prefix, slice_cmd[2], slice_cmd[3]); + else if(eq("368", cmd)) IRC_ENDOFBANLIST(prefix); + else if(eq("403", cmd)) IRC_ERR_NOSUCHCHANNEL(prefix, slice_cmd[1]); + else if(eq("433", cmd)) IRC_ERR_NICKNAMEINUSE(prefix, slice_cmd[1]); + else if(eq("436", cmd)) IRC_ERR_NICKCOLLISION(prefix, slice_cmd[1]); + } +} + +//----------------------------------------------------------------------------- + +// Rough routine to read the options + +void get_options(int argc, char **argv) +{ + int n, help, d; + n = 1; + help = 0; + + while(n=1) socket_delay = d; + else cerr<<"*** Delay error ***\n"; + } + else cerr<<"*** No delay parameter ***\n"; + } + else if(eq("-od", argv[n])) + { + n++; + if(n=1) op_delay = d; + else cerr<<"*** Op delay error ***\n"; + } + else cerr<<"*** No delay parameter ***\n"; + } + else if(eq("-dd", argv[n])) + { + n++; + if(n=1) deban_delay = d; + else cerr<<"*** Deban delay error ***\n"; + } + else cerr<<"*** No delay parameter ***\n"; + } + else if(eq("-?", argv[n])) help = 1; + else + { + cerr<<"*** Unknown option "<.\n"; + cout<<"\n"; + cout<<"Options are :\n"; + cout<<"-c <#channel> sets the channel\n"; + cout<<"-d sets the reconnection delay\n"; + cout<<"-h sets the server name\n"; + cout<<"-j sets the pattern nick for collision\n"; + cout<<"-l sets the user in the user@host stuff\n"; + cout<<"-n sets the nickname\n"; + cout<<"-o loads configuration file\n"; + cout<<"-od set the delay before +o\n"; + cout<<"-dd set the delay before -b\n"; + cout<<"-p sets the server port\n"; + cout<<"-? shows this help\n"; + exit(0); + } + + n++; + } +} + +//----------------------------------------------------------------------------- + +void clean_shit_list() +{ + NodeList *node, *pred, *next; + + node = shit_list.first; + pred = NULL; + while(node != NULL) + { + next = node->next; + if(current_time >= node->body->time_max) + { + if(pred == NULL) shit_list.first = next; + else pred->next = next; + + smart_unban(current_channel, node->body->pattern); + + delete node->body; + delete node; + } + else pred = node; + node = next; + } + send_mode(current_channel); +} + +//----------------------------------------------------------------------------- + +void try_reconnect() +{ + if(delay < DELAY_MAX_RECONNECT) + { + if(delay == 0) delay = 1; + else delay *= 2; + if(delay > DELAY_MAX_RECONNECT) delay = DELAY_MAX_RECONNECT; + } + strncpy(wanted_server, default_server, MAXHOSTNAME+1); + wanted_port = default_port; + cerr<<"*** Can't contact IRC server ***\n"; + cerr<<"*** Next try in "<= buffer+BUFFER_SIZE) + { + cerr<<"*** Buffer full, erase it ***\n"; + endsrc = buffer; + } + + s = read(socket_irc, endsrc, buffer+BUFFER_SIZE-1-endsrc); + src = buffer; + endsrc += s; + + if(s <= 0) + { + cerr<<"KILLING CONNECTION : Read error\n"; + cerr.flush(); + kill_connection(); + } + else + { + end = 0; + reset_mode(); + while(!end) + { + dest_cmd = buffer_cmd; + dest_ctcp = buffer_ctcp; + + if(slice_buffer(src, endsrc, + prefix, + dest_cmd, slice_cmd, n_cmd, + dest_ctcp, slice_ctcp, n_ctcp)) + // No more \r in the buffer + { + r = src; + t = buffer; + while(r *md; + + if(current_time > anti_flood_off_until) check_flood(); + check_delay_mode_change(); + send_mode(current_channel); + + delay = socket_delay; + NodeList *nd; + for(nd = shit_list.first; nd != NULL; nd = nd->next) + if(delay > nd->body->time_max-current_time) + delay = nd->body->time_max-current_time; + + for(md = mode_change_list.first; md != NULL; md = md->next) + if(delay > md->body->time-current_time) + delay = md->body->time-current_time; + + if(delay < 0) delay = 0; +} + +void dcc_chat() +{ + NodeList *node, *pred, *next, *node2; + char CHAT_buffer[BUFFER_SIZE]; + int s; + + pred = NULL; next = NULL; + node = dcc_chat_list.first; + while(node != NULL) + { + next = node->next; + if(FD_ISSET(node->body->socket, &result)) + { + s = read(node->body->socket, CHAT_buffer, BUFFER_SIZE-1); + if(s <= 0) + { + FD_CLR(node->body->socket, &ready); // Forget the socket + close(node->body->socket); + + sprintf(IRC_buffer, "%s leaves.\n", node->body->prefix); + for(node2=dcc_chat_list.first; node2!= NULL; node2 = node2->next) + if(node2->body != node->body) + tell(node2->body, NULL, IRC_buffer); + + remove_wait_for_chat(node->body); + + if(pred == NULL) dcc_chat_list.first = next; + else pred->next = next; + delete node->body; + delete node; + } + else + { + *(CHAT_buffer+s-1) = '\0'; + tropbot_cmd(node->body, node->body->prefix, NULL, CHAT_buffer); + pred = node; + } + } + else pred = node; + + node = next; + } +} + +int main(int argc, char **argv) +{ + + // I think it's a good idea to have few friends forever (me ? yeahhh !) + level_list.Insert(new Person("*!*fleuret@*.inria.fr", LEVEL_MASTER, NULL)); + level_list.Insert(new Person("*!*fleuret@*.ens.fr", LEVEL_MASTER, NULL)); + level_list.Insert(new Person("*!*fleuret@*.curie.fr", LEVEL_MASTER, NULL)); + level_list.Insert(new Person("*!*jolibot@*.inria.fr", LEVEL_DEFENCE, NULL)); + level_list.Insert(new Person("*!*jolibot@*.ens.fr", LEVEL_DEFENCE, NULL)); + level_list.Insert(new Person("*!*jolibot@*.curie.fr", LEVEL_DEFENCE, NULL)); + + cout<<"TropBot, written by Francois Fleuret," + " contact \n"; + + get_options(argc, argv); + + uncap(home_channel); + strncpy(wanted_channel, home_channel, SMALL_BUFFER_SIZE); + strncpy(wanted_nick, original_nick, SMALL_BUFFER_SIZE); + strncpy(wanted_server, default_server, MAXHOSTNAME+1); + wanted_port = default_port; + + IRC_registred = 0; // true if we are registred + IRC_connected = 0; // true if we have a connection with an IRC server + global_state = STATE_WAIT; + in_channel = 0; + mode_protect_on = DEFAULT_MODE_PROTECT; + + FD_ZERO(&ready); + + alive = 1; + father = 1; + nb_sons = 0; + was_killed = 0; + last_answer_time = 0; + anti_flood_off_until = 0; + + src = buffer; + endsrc = buffer; + delay = socket_delay; + + struct sigaction action; + action.sa_handler = SIG_IGN; + + // We'll ignore the SIGPIPE signal, which will be catch somewhere else + sigaction(SIGPIPE, &action, NULL); + + delay = 0; + + do + { + delay_pause.tv_sec = delay; + delay_pause.tv_usec = 0; + result = ready; + + while(waitpid(-1, NULL, WNOHANG) > 0) nb_sons--; + + select(64, &result, NULL, NULL, &delay_pause); + + time(¤t_time); + + dcc_chat(); + + if(!IRC_connected) + { +#ifdef SCREEN_OUTPUT + cout<<"No connection yet\n"; + cout<<"Try to contact "< time_killed+DELAY_NICK_BACK) && was_killed) + { + was_killed = 0; + strncpy(wanted_nick, original_nick, SMALL_BUFFER_SIZE); + sprintf(buffer, "NICK %s\n", wanted_nick); + write_irc(buffer); + } + + if(IRC_registred) clean_shit_list(); + + if(FD_ISSET(socket_irc, &result)) got_datas_from_server(); + else + { + if(current_time > time_last_datas+DELAY_DEAD_SERVER) + { + cerr<<"KILLING CONNECTION : Quiet server\n"; + cerr.flush(); + kill_connection(); + } + else if(!in_channel) if(global_state == STATE_WAIT) + { + sprintf(IRC_buffer, "JOIN %s\n", wanted_channel); + write_irc(IRC_buffer); + } + } + + if(IRC_connected) check_stuffs(); + } + +#ifdef SCREEN_OUTPUT + cout< 0)); + +} + +//----------------------------------------------------------------------------- -- 2.20.1