From: Francois Fleuret Date: Sun, 6 Oct 2013 09:15:06 +0000 (+0200) Subject: Initial commit, version 2.6.3a X-Git-Url: https://www.fleuret.org/cgi-bin/gitweb/gitweb.cgi?p=tropbot.git;a=commitdiff_plain;h=c6dbdbeafebf079c81ee6046761d09c3c58dfcf8 Initial commit, version 2.6.3a --- c6dbdbeafebf079c81ee6046761d09c3c58dfcf8 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)); + +} + +//-----------------------------------------------------------------------------