1 /*-----------------------------------------------------------------------------
2 TropBot, a small IRC bot, v2.9 Dec 95 - Oct 96
3 Witten by Francois Fleuret.
4 Contact <francois.fleuret@inria.fr> for comments & bug reports
5 Check http://www.eleves.ens.fr:8080/home/fleuret for latest version
6 -----------------------------------------------------------------------------*/
8 #define VERSION "v2.9.3a"
14 #include <sys/types.h>
21 //-----------------------------------------------------------------------------
27 #define ANTI_SPOOF_MAX_TIME 300
29 //-----------------------------------------------------------------------------
31 #define MODE_NUMBER_MAX 3
32 #define SIZE_BANLIST_MAX 20
33 #define NB_CHAR_MAX 400
35 //-----------------------------------------------------------------------------
37 #define DEFAULT_CONTROL_CHAR '|'
38 #define DELAY_NICK_BACK 300
39 #define DELAY_MAX_RECONNECT 120
40 #define DELAY_DEAD_SERVER 900
42 #define DEFAULT_SERVER "sil.polytechnique.fr"
43 #define DEFAULT_PORT 6667
45 #define DEFAULT_USER_NAME "\002TropBot\002 " VERSION OPTIONS
46 #define DEFAULT_LOGIN "tropbot"
47 #define DEFAULT_NICK "TropBot"
48 #define DEFAULT_JAM_NICK "TB-????"
49 #define DEFAULT_HOME_CHANNEL "#tropbot"
50 #define DEFAULT_SOCKET_DELAY 30
51 #define DEFAULT_OP_DELAY 2
52 #define DEFAULT_DEBAN_DELAY 0
54 #define DEFAULT_ANTI_FLOOD_OFF_DURATION 600
56 #define DEFAULT_CONFIG_FILE ".tropbotrc"
58 #define DEFAULT_SHIT_TIME 600
59 #define DEFAULT_RESTRICTED_TIME 900
61 #define DEFAULT_MODE_PROTECT 0
63 // Not needed coz flooding people can't re-JOIN quickly
64 //#define BAN_ON_FLOOD
65 #define BAN_FLOOD_DELAY 30
67 #define MAX_LINE_FLOOD 4
68 #define MAX_CTCP_FLOOD 3
70 #define DELAY_ANSWERS 3
71 #define ANTI_CLONES_DELAY 30
75 #define HISTORY_SIZE 3
77 enum { FL_NOTICE, FL_PUBLIC, FL_CTCP, FL_NICK, FL_PART, FL_JOIN, FL_KICK };
79 //-----------------------------------------------------------------------------
81 // More than 3 kicks in 5 minutes -> 30 mins shit
83 #define COP_NB_KICKS 3
86 #define COP_DURATION 1800
87 #define COP_DELAY_BEETWEEN_KICKS 2
89 //-----------------------------------------------------------------------------
91 #define MAX_DCC_CHAT 16
93 //-----------------------------------------------------------------------------
95 extern "C" void bzero(void *, int);
96 extern "C" int select(int, fd_set *, fd_set *, fd_set *, timeval *);
97 extern "C" pid_t fork(void);
99 //-----------------------------------------------------------------------------
101 int default_port = DEFAULT_PORT;
103 char default_server[MAXHOSTNAME+1] = DEFAULT_SERVER;
104 char wanted_server[MAXHOSTNAME+1];
106 fd_set ready, result;
107 int alive; // Should be set to false (0) to kill the bot
110 int socket_delay = DEFAULT_SOCKET_DELAY;
111 int op_delay = DEFAULT_OP_DELAY;
112 int deban_delay = DEFAULT_DEBAN_DELAY;
113 char control_char = DEFAULT_CONTROL_CHAR;
115 int cop_mode, anti_flood, anti_clone, ctcp_reply;
119 time_t current_time, time_killed, time_last_datas, last_answer_time,
120 anti_flood_off_until;
122 char buffer[BUFFER_SIZE];
124 int IRC_connected, IRC_registred;
125 int global_state, in_channel, was_killed, mode_protect_on;
128 #define STATE_GETING_WHO 1
129 #define STATE_GETING_BAN 2
131 //-----------------------------------------------------------------------------
133 template class List<DCCChat *>;
134 template class List<Person *>;
135 template class List<Welcome *>;
136 template class List<WaitInfos *>;
137 template class List<WaitPing *>;
138 template class List<DelayModeChange *>;
139 template class List<char *>;
141 List<DCCChat *> dcc_chat_list;
142 List<Person *> level_list;
143 List<Welcome *> shit_list;
144 List<WaitInfos *> wait_list;
145 List<WaitPing *> wait_ping;
146 List<DelayModeChange *> mode_change_list;
148 ListChar present_people, banid_list, restricted_list, history;
150 //-----------------------------------------------------------------------------
152 // General buffer for all IRC operations
153 char IRC_buffer[BUFFER_SIZE];
155 // Nick we got after registration
156 char real_nick[SMALL_BUFFER_SIZE];
159 char wanted_nick[SMALL_BUFFER_SIZE];
161 char original_nick[SMALL_BUFFER_SIZE] = DEFAULT_NICK;
163 // Pattern used for random-nick generation if nick-collision occurs
164 // all the '?' will be replaced with random digits
165 char jam_nick[SMALL_BUFFER_SIZE] = DEFAULT_JAM_NICK;
167 // The "user" we want in the nick!user@host stuff
168 char user_login[SMALL_BUFFER_SIZE] = DEFAULT_LOGIN;
170 // The USERNAME string
171 char user_name[SMALL_BUFFER_SIZE] = DEFAULT_USER_NAME;
173 // The channel we want to join
174 char current_channel[SMALL_BUFFER_SIZE], wanted_channel[SMALL_BUFFER_SIZE];
175 char home_channel[SMALL_BUFFER_SIZE] = DEFAULT_HOME_CHANNEL;
177 // The name of the configuration file
178 char config_file[SMALL_BUFFER_SIZE] = DEFAULT_CONFIG_FILE;
180 //-----------------------------------------------------------------------------
182 char *prefix_from_nick(char *nick)
184 NodeList<char *> *node;
189 node = present_people.first;
190 while((node != NULL) && !yep)
195 while((*t != '\0') &&
196 (*s != '!') && (*s != '\0') && yep) yep &= (*s++ == *t++);
197 yep &= ((*t == '\0') && (*s == '!'));
198 if(!yep) node=node->next;
201 if(node != NULL) return node->body; else return NULL;
205 //-----------------------------------------------------------------------------
207 int level_person(char *prefix, char *passwd)
209 NodeList<Person *> *node;
213 for(node = level_list.first; node != NULL; node=node->next)
214 if(match_pattern(node->body->pattern, prefix))
215 if((node->body->level > l) || (l == 0))
217 if(node->body->passwd == NULL) l = node->body->level;
220 if(strcmp(node->body->passwd, passwd) == 0)
221 l = node->body->level;
226 void remove_level(char *prefix)
228 NodeList<Person *> *node, *pred, *next;
229 pred = NULL; next = NULL;
231 node = level_list.first;
235 if(strcmp(node->body->pattern, prefix) == 0)
237 if(pred == NULL) level_list.first = next;
238 else pred->next = next;
249 NodeList<Person *> *node, *next;
251 node = level_list.first;
259 level_list.first = NULL;
264 NodeList<Welcome *> *node, *next;
266 node = shit_list.first;
274 shit_list.first = NULL;
277 void remove_wait_for_chat(DCCChat *chat)
279 NodeList<WaitInfos *> *node, *pred, *next;
280 pred = NULL; next = NULL;
282 node = wait_list.first;
286 if(node->body->chat == chat)
288 if(pred == NULL) wait_list.first = next;
289 else pred->next = next;
298 //-----------------------------------------------------------------------------
300 // This function generates a random nick from a pattern. The pattern
301 // is supposed to contain some '?', which will be replaced with random
304 void rand_nick(char *nick, char *pattern)
307 for(n=0; n<strlen(pattern); n++)
309 if(pattern[n] == '?') nick[n] = '0'+int(rand()%10);
310 else nick[n] = pattern[n];
315 //-----------------------------------------------------------------------------
317 void kill_connection()
319 time_last_datas = current_time;
320 IRC_connected = 0; // We are not connected
321 IRC_registred = 0; // We are not registred
322 FD_CLR(socket_irc, &ready); // Forget the socket
323 close(socket_irc); // Close it
324 present_people.Clear(); // We don't see anyone
325 rand_nick(wanted_nick, jam_nick); // Random the nick (yeah, we are paranoid)
326 was_killed = 1; // We have been killed (I said PARANOID !)
327 time_killed = current_time; // Note when it happens
328 src = buffer; // Forget all the remaining datas
329 endsrc = buffer; // Idem.
330 delay = 0; // We won't wait for reconnection
331 in_channel = 0; // We are not in any channel
335 void clone(char *nick)
339 strcpy(wanted_nick, nick);
340 strcpy(original_nick, nick);
343 FD_CLR(socket_irc, &ready);
344 present_people.Clear();
345 time_killed = current_time;
354 void write_irc(char *stuff)
356 // I'd like to know why do I have to do such a cast ?!?!
357 if((int) write(socket_irc, stuff, strlen(stuff)) < 0)
360 cerr<<"KILLING CONNECTION : write_irc error\n";
368 cout<<"\n!!!!>"<<stuff<<"<\n\n";
370 last_answer_time = current_time;
374 //-----------------------------------------------------------------------------
378 char string_modes[MODE_NUMBER_MAX*2+1];
380 char string_param_modes[MODE_NUMBER_MAX*64+1];
381 char *ptr_param_modes;
387 ptr_modes = string_modes;
388 ptr_param_modes = string_param_modes;
393 void send_mode(char *where)
398 *ptr_param_modes = '\0';
399 sprintf(IRC_buffer, "MODE %s %s %s\n",
400 where, string_modes, string_param_modes);
401 write_irc(IRC_buffer);
406 void add_mode(char *where, char *mode, char *param)
410 // Disgusting hack coz of a FUCKING server bug :-(
413 if(k_mode) send_mode(where);
419 if(o_mode) send_mode(where);
424 while(*s != '\0') *ptr_modes++ = *s++;
428 while(*s != '\0') *ptr_param_modes++ = *s++;
429 *ptr_param_modes++ = ' ';
432 if(++nb_modes == MODE_NUMBER_MAX) send_mode(where);
435 //-----------------------------------------------------------------------------
437 void tell(DCCChat *chat, char *nick, char *msg)
439 char tell_buffer[BUFFER_SIZE];
442 cout<<"tell(chat="<<chat<<", nick="<<nick<<" msg="<<msg<<")\n";
443 if(msg[strlen(msg)-1] != '\n')
444 cout<<"**************************************\n";
447 if(chat != NULL) write(chat->socket, msg, strlen(msg));
451 sprintf(tell_buffer, "NOTICE %s :%s", nick, msg);
452 write_irc(tell_buffer);
456 //-----------------------------------------------------------------------------
458 void smart_shit(DCCChat *chat, char *nick,
459 char *pattern, char *comment, int duration)
461 NodeList<Welcome *> *node, *next, *pred;
465 if(duration < 5) duration = 5;
467 time_max = duration + current_time;
470 node = shit_list.first;
472 while((node != NULL) && !no_shit)
475 if(strcmp(node->body->pattern, pattern) == 0)
477 if(node->body->time_max <= time_max)
479 if(pred == NULL) shit_list.first = next;
480 else pred->next = next;
496 shit_list.Insert(new Welcome(pattern, comment, time_max));
497 if((nick != NULL) || (chat != NULL))
499 char *string_duration;
500 string_duration = seconds_to_string(duration);
501 sprintf(IRC_buffer, "Shit %s for %s (%s)\n",
502 pattern, string_duration, comment);
503 tell(chat, nick, IRC_buffer);
504 delete[] string_duration;
507 else if((nick != NULL) || (chat != NULL))
510 "Can't shit %s, already shitted for a longer time\n", pattern);
511 tell(chat, nick, IRC_buffer);
515 //-----------------------------------------------------------------------------
517 void smart_ban(char *where, char *pattern)
519 NodeList<char *> *node;
522 for(node = banid_list.first; node != NULL; node = node->next)
524 if((n>=SIZE_BANLIST_MAX-1) ||
525 (match_pattern(pattern, node->body) &&
526 (strcmp(pattern, node->body) != 0)))
527 add_mode(where, "-b", node->body);
530 add_mode(current_channel, "+b", pattern);
533 void smart_unban(char *where, char *pattern)
535 if(banid_list.Contains(pattern)) add_mode(where, "-b", pattern);
538 //-----------------------------------------------------------------------------
540 void check_delay_mode_change()
542 NodeList<DelayModeChange *> *node, *pred, *next;
545 node = mode_change_list.first;
549 if(current_time >= node->body->time)
551 add_mode(node->body->where, node->body->mode, node->body->parameter);
552 if(pred == NULL) mode_change_list.first = next;
553 else pred->next = next;
562 void clean_delay_mode_change(char *where, char *mode, char *param)
564 NodeList<DelayModeChange *> *node, *pred, *next;
569 node = mode_change_list.first;
576 if((node->body->parameter != NULL) &&
577 (strcmp(where, node->body->where) == 0) &&
578 (strcmp(param, node->body->parameter) == 0) &&
579 (strcmp(mode, node->body->mode) == 0))
581 if(pred == NULL) mode_change_list.first = next;
582 else pred->next = next;
595 if((strcmp(where, node->body->where) == 0) &&
596 (node->body->parameter == NULL) &&
597 (strcmp(mode, node->body->mode) == 0))
599 if(pred == NULL) mode_change_list.first = next;
600 else pred->next = next;
610 void RemoveDelayModeChange(char *mode, char *parameter)
612 NodeList<DelayModeChange *> *node, *pred, *next;
615 node = mode_change_list.first;
619 if((strcmp(node->body->mode, mode) == 0) &&
620 (strcmp(node->body->parameter, parameter) == 0))
622 if(pred == NULL) mode_change_list.first = next;
623 else pred->next = next;
632 //-----------------------------------------------------------------------------
634 void synch(char *channel)
637 NodeList<char *> *node;
639 for(node = present_people.first; node != NULL; node = node->next)
640 if(level_person(node->body, NULL) >= LEVEL_OP)
642 nick = cut_nick_from_prefix(node->body);
643 add_mode(channel, "+o", nick);
648 //-----------------------------------------------------------------------------
650 ListChar already_kicked;
652 void filter_kick(char *where, char *pattern, char *comment)
654 NodeList<char *> *node;
657 if(already_kicked.Contains(pattern)) return;
659 already_kicked.Add(pattern);
660 for(node = present_people.first; node != NULL;
663 if(match_pattern(pattern, node->body))
664 if(level_person(node->body, NULL) == 0)
666 nick = cut_nick_from_prefix(node->body);
667 sprintf(IRC_buffer, "KICK %s %s :%s\n", where, nick, comment);
668 write_irc(IRC_buffer);
674 //-----------------------------------------------------------------------------
676 // Return a pointer on the first character after the last '@' in the string
677 // Beware of some weird hack-script that add @ in the login
678 char *adr_beginning(char *s)
682 while(*s != '\0') if(*s++ == '@') t = s;
687 char *reach_loginathost(char *s)
689 while((*s != '!') && (*s != '\0')) s++;
694 //-----------------------------------------------------------------------------
703 int individual, site, alreadykicked;
705 FloodLine(char *p, int k, int w)
714 ~FloodLine() { delete[] prefix; }
715 void Reset() { individual = 0; site = 0; }
718 template class List<FloodLine *>;
720 List<FloodLine *> flood_list, kick_list;
722 //-----------------------------------------------------------------------------
724 void alert(char *where, DCCChat *chat, char *nick)
726 int nb_lines, max_nb_lines, duration;
727 NodeList<FloodLine *> *node, *son;
728 char *site, *site2, *guilty_site, *r;
729 char pattern[SMALL_BUFFER_SIZE];
731 for(node = flood_list.first; node != NULL; node = node->next)
732 node->body->individual = 0;
734 node = flood_list.first;
741 while((node != NULL) && node->body->individual) node = node->next;
745 site = adr_beginning(node->body->prefix);
750 while((son != NULL) && son->body->individual) son = son->next;
753 site2 = adr_beginning(son->body->prefix);
755 if(are_same_site(site, site2))
757 son->body->individual = 1;
764 if(nb_lines > max_nb_lines)
765 if(level_person(node->body->prefix, NULL) == 0)
767 max_nb_lines = nb_lines;
774 if(guilty_site != NULL)
776 duration = DEFAULT_SHIT_TIME;
779 concat_pattern_from_host(r, guilty_site);
780 add_mode(where, "+m", NULL);
781 smart_shit(NULL, NULL, pattern, "Alert", duration);
782 smart_ban(where, pattern);
784 filter_kick(where, pattern, "Filter kick on alert");
785 add_mode(where, "-m", NULL);
788 else tell(chat, nick, "\002No flooding site detected\002\n");
791 void check_one_flood(NodeList<FloodLine *> *node)
793 NodeList<FloodLine *> *son;
794 char *userhost, *userhost2, *nick;
797 userhost = reach_loginathost(node->body->prefix);
803 while((son != NULL) && son->body->individual) son = son->next;
806 if(!son->body->alreadykicked)
808 userhost2 = reach_loginathost(son->body->prefix);
810 if(strcmp(userhost, userhost2) == 0)
812 son->body->individual = 1;
820 if(nb_lines >= MAX_LINE_FLOOD)
824 char *pattern, *banid;
825 if((level_person(node->body->prefix, NULL) < LEVEL_OP))
827 pattern = pattern(node->body->prefix, 0);
828 banid = clean_banid(pattern);
829 smart_ban(current_channel, banid);
830 send_mode(current_channel);
831 mode_change_list.Insert(new DelayModeChange(current_channel,
839 nick = cut_nick_from_prefix(node->body->prefix);
840 sprintf(IRC_buffer, "KICK %s %s :%s\n", current_channel, nick, "Flood");
841 write_irc(IRC_buffer);
844 for(son = flood_list.first; son != NULL; son = son->next)
846 userhost2 = reach_loginathost(son->body->prefix);
847 if(strcmp(userhost, userhost2) == 0) son->body->alreadykicked = 1;
855 int nb_lines, nb_ctcp;
856 NodeList<FloodLine *> *node, *next, *pred;
858 nb_lines = 0; nb_ctcp = 0;
860 node = flood_list.first;
864 if(current_time <= node->body->time + FLOOD_DELAY)
866 nb_lines += node->body->weigth;
867 if((node->body->kind == FL_CTCP) && !node->body->alreadykicked)
868 nb_ctcp += node->body->weigth;
874 if(pred == NULL) flood_list.first = next;
875 else pred->next = next;
883 if(nb_ctcp >= MAX_CTCP_FLOOD)
885 add_mode(current_channel, "+m", NULL);
886 send_mode(current_channel);
887 mode_change_list.Insert(new DelayModeChange(current_channel,
893 if(nb_lines >= MAX_LINE_FLOOD)
895 for(node = flood_list.first; node != NULL; node = node->next)
896 node->body->individual = 0;
898 node = flood_list.first;
901 while((node != NULL) && node->body->individual) node = node->next;
904 if(!node->body->alreadykicked) check_one_flood(node);
911 inline void add_flood_line(char *prefix, int kind, int weigth)
913 flood_list.Insert(new FloodLine(prefix, kind, weigth));
916 int bad_line(char *line, char *comment)
918 int length, nb_art_chars, longest_word, nb_caps, nb_letters, nb_ctrl;
931 for(s=(unsigned char *)line; *s != '\0'; s++)
933 if((*s >= 'A') && (*s <= 'Z')) nb_caps++;
934 if(((*s >= 'A') && (*s <= 'Z')) ||
935 ((*s >= 'a') && (*s <= 'z'))) nb_letters++;
936 if((*s > 2) && (*s < 31)) nb_ctrl++;
937 if((*s != ' ') && (*s != '?') && (*s != '!') &&
938 (((*s >= 32) && (*s <= 47)) ||
939 ((*s >= 58) && (*s <= 63)) ||
940 ((*s >= 91) && (*s <= 96)) ||
941 (*s >= 123))) nb_art_chars++;
943 if(s > (unsigned char *) line)
945 if(*s == *(s-1)) l++;
948 if(l > longest_word) longest_word = l;
955 if(l > longest_word) longest_word = l;
960 if(longest_word > 60)
963 concat(t, "repeated chars");
968 if(bad) concat(t, " + ");
969 concat(t, "control chars");
973 if((nb_art_chars > nb_letters) && (nb_art_chars >= 20))
975 if(bad) concat(t, " + ");
976 concat(t, "art chars");
980 if((nb_caps*4 > nb_letters*3) && (nb_caps >= 10))
982 if(bad) concat(t, " + ");
992 void test_kick(char *prefix)
996 NodeList<FloodLine *> *node, *next, *pred;
997 char *pattern, *banid, *loginathost;
999 loginathost = reach_loginathost(prefix);
1002 last_kick_time = current_time + COP_DELAY_BEETWEEN_KICKS + 1;
1004 node = kick_list.first;
1008 if(current_time <= node->body->time + COP_DELAY)
1010 if(strcmp(loginathost, reach_loginathost(node->body->prefix)) == 0)
1012 if(node->body->time < last_kick_time - COP_DELAY_BEETWEEN_KICKS)
1014 nb_kicks += node->body->weigth;
1015 last_kick_time = node->body->time;
1022 if(pred == NULL) kick_list.first = next;
1023 else pred->next = next;
1030 if(nb_kicks >= COP_NB_KICKS)
1032 pattern = pattern_from_prefix(prefix, 0);
1033 banid = clean_banid(pattern);
1035 smart_shit(NULL, NULL,
1036 pattern, "cop : too many kicks", COP_DURATION + deban_delay);
1037 smart_ban(current_channel, pattern);
1038 send_mode(current_channel);
1039 filter_kick(current_channel, pattern, "cop : too many kicks");
1046 inline void add_kick(char *prefix)
1048 if(level_person(prefix, NULL) >= LEVEL_FRIEND) return;
1049 kick_list.Insert(new FloodLine(prefix, FL_KICK, 1));
1053 //-----------------------------------------------------------------------------
1055 // This function is called for all mode changes
1056 // signe indicates if it's a + or - mode change
1057 // param is NULL for mode change without parameters
1059 void IRC_ONE_MODE(char *where, char *who, int signe, char mode, char *param)
1063 int check_ban, nb_bans, ok;
1064 NodeList<char *> *node;
1065 char pattern[SMALL_BUFFER_SIZE];
1068 if(signe < 0) buffer[0] = '-'; else buffer[0] = '+';
1071 if(param != NULL) uncap(param);
1073 clean_delay_mode_change(where, buffer, param);
1075 #ifdef SCREEN_OUTPUT
1076 cout<<"ONE MODE CHANGE CHANNEL ("<<where<<")";
1077 cout<<" BY ("<<who<<") MODE "<<signe<<" "<<mode;
1078 cout<<" ON "<<param<<"\n";
1083 // Basic anti-hack procedure. All +o and +b mode done by servers are
1084 // canceled (we have to look if the person is a friend or not)
1088 if(is_hostname(who))
1090 if(deban_delay == 0)
1092 if(signe>0) add_mode(where, "-b", param);
1093 else add_mode(where, "+b", param);
1098 mode_change_list.Insert(new DelayModeChange(where, "-b", param,
1101 mode_change_list.Insert(new DelayModeChange(where, "+b", param,
1109 banid_list.Add(param);
1111 if(cop_mode && !is_hostname(who))
1113 adr_param = adr_beginning(param);
1116 for(node = banid_list.first; node != NULL; node = node->next)
1117 if(are_same_site(adr_param, adr_beginning(node->body)))
1120 if(nb_bans >= COP_NB_BANS)
1125 concat(c, adr_param);
1130 for(node = present_people.first;
1131 (node != NULL) && ok; node = node->next)
1132 if(match_pattern(pattern, node->body))
1133 if(level_person(node->body, NULL) >= LEVEL_FRIEND)
1138 smart_shit(NULL, NULL,
1140 "cop : too many bans on one site",
1141 COP_DURATION + deban_delay);
1142 smart_ban(where, pattern);
1149 banid = clean_banid(param);
1150 if(strcmp(banid, param) != 0) smart_ban(where, banid);
1154 else banid_list.Remove(param);
1158 if(is_hostname(who))
1160 c = prefix_from_nick(param);
1161 if((signe > 0) && (level_person(c, NULL) < LEVEL_FRIEND))
1162 add_mode(where, "-o", param);
1163 if((signe < 0) && (level_person(c, NULL) >= LEVEL_FRIEND))
1164 add_mode(where, "+o", param);
1168 (level_person(who, NULL) <
1169 level_person(prefix_from_nick(param), NULL)) &&
1170 (level_person(prefix_from_nick(param), NULL) > 0))
1172 c = cut_nick_from_prefix(who);
1173 add_mode(where, "-o", c);
1174 add_mode(where, "+o", param);
1180 if(signe>0) if(is_hostname(who) || mode_protect_on)
1181 add_mode(where, "-i", NULL);
1185 if(signe>0) if(is_hostname(who) || mode_protect_on)
1186 add_mode(where, "-l", NULL);
1190 if(signe>0) if(is_hostname(who) || mode_protect_on)
1191 add_mode(where, "-p", NULL);
1195 if(signe>0) if(is_hostname(who) || mode_protect_on)
1196 add_mode(where, "-k", param);
1200 if(is_hostname(who))
1202 if(signe>0) add_mode(where, "-m", NULL);
1204 else if(mode_protect_on)
1206 if(level_person(who, NULL) < LEVEL_OP)
1208 if(signe>0) add_mode(where, "-m", NULL);
1209 else add_mode(where, "+m", NULL);
1210 c = cut_nick_from_prefix(who);
1211 add_mode(where, "-o", c);
1218 if(signe>0) if(is_hostname(who) || mode_protect_on)
1219 add_mode(where, "-s", NULL);
1227 //-----------------------------------------------------------------------------
1229 // This function is called after a "MODE" command
1230 // You should not modify it, unless you find a bug.
1231 // Modify only IRC_ONE_MODE()
1233 void IRC_MODE(char *where, char *who, char *mode, char **params)
1235 int param_no, signe;
1239 while(*mode != '\0')
1250 if((*mode == 'l') || (*mode == 'v') ||
1251 (*mode == 'k') || (*mode == 'o') ||
1253 IRC_ONE_MODE(where, who, signe, *mode, params[param_no++]);
1255 IRC_ONE_MODE(where, who, signe, *mode, NULL);
1263 //-----------------------------------------------------------------------------
1265 void get_one_line(ifstream *s, char *buffer, int buffer_size)
1276 if(d != '\n') *c++ = d;
1280 ok &= (c<buffer + buffer_size);
1285 #define MAX_INCLUDE_RECURSE 3
1287 int LoadConfigFile(char *name, int recurse)
1290 int duration, level, error;
1292 char pattern[SMALL_BUFFER_SIZE], buffer[SMALL_BUFFER_SIZE];
1294 file = new ifstream(name);
1299 restricted_list.Clear();
1302 get_one_line(file, IRC_buffer, BUFFER_SIZE);
1303 if(strlen(IRC_buffer) > 0)
1306 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1308 if(strcmp(buffer, "INCLUDE") == 0)
1312 if(recurse < MAX_INCLUDE_RECURSE)
1314 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1315 LoadConfigFile(buffer, recurse+1);
1318 else cerr<<"Can't include "
1319 <<buffer<<" : too many recursion.\n";
1323 else if(strcmp(buffer, "FRIEND") == 0)
1327 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1328 level = atoi(buffer);
1331 r = next_word(pattern, r, SMALL_BUFFER_SIZE);
1333 level_list.Insert(new Person(pattern, level, r));
1337 else if(strcmp(buffer, "RESTRICTED") == 0)
1341 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1342 #ifdef SCREEN_OUTPUT
1343 cout<<"RESTRICTED->"<<buffer<<"\n";
1345 restricted_list.Insert(strdup(buffer));
1348 else if(strcmp(buffer, "SHIT") == 0)
1352 r = next_word(pattern, r, SMALL_BUFFER_SIZE);
1356 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1357 duration = atoi(buffer);
1358 if(r == NULL) r = "Shit";
1359 banid = clean_banid(pattern);
1360 smart_shit(NULL, NULL, banid, r, duration);
1365 else if(strcmp(buffer, "OPDELAY") == 0)
1369 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1370 op_delay = atoi(buffer);
1373 else if(strcmp(buffer, "DEBANDELAY") == 0)
1377 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1378 deban_delay = atoi(buffer);
1381 else if(strcmp(buffer, "CONTROLCHAR") == 0)
1385 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1386 if(buffer[0] != '\0') control_char = buffer[0];
1389 else if(strcmp(buffer, "COPMODE") == 0)
1393 else if(strcmp(buffer, "ANTIFLOOD") == 0)
1396 anti_flood_off_until = current_time;
1398 else if(strcmp(buffer, "ANTICLONES") == 0)
1402 else if(strcmp(buffer, "CTCPREPLY") == 0)
1406 else if(strcmp(buffer, "CHANNEL") == 0)
1410 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1411 strncpy(home_channel, buffer, SMALL_BUFFER_SIZE);
1416 shit_list.Reverse();
1417 level_list.Reverse();
1418 restricted_list.Reverse();
1428 int SaveConfigFile(char *name)
1431 NodeList<Welcome *> *welcome;
1432 NodeList<Person *> *person;
1433 NodeList<char *> *restricted;
1436 file = new ofstream(name);
1439 (*file)<<"% Config file for a TropBot " VERSION "\n";
1442 (*file)<<"% Delay in s before +o on level 2 users\n";
1443 (*file)<<"OPDELAY "<<op_delay<<"\n";
1444 (*file)<<"% Delay before auto -b on servers +b\n";
1445 (*file)<<"DEBANDELAY "<<deban_delay<<"\n";
1446 (*file)<<"% Charactere used for public commands\n";
1447 (*file)<<"CONTROLCHAR "<<control_char<<"\n";
1450 (*file)<<"% Home channel\n";
1451 (*file)<<"CHANNEL "<<home_channel<<"\n";
1454 (*file)<<"% Cop-mode activation/desactivation\n";
1455 if(cop_mode) (*file)<<"COPMODE\n";
1456 else (*file)<<"% COPMODE\n";
1458 (*file)<<"% Anti-flood activation/desactivation\n";
1459 if(anti_flood) (*file)<<"ANTIFLOOD\n";
1460 else (*file)<<"% ANTIFLOOD\n";
1462 (*file)<<"% Anti-clones activation/desactivation\n";
1463 if(anti_clone) (*file)<<"ANTICLONES\n";
1464 else (*file)<<"% ANTICLONES\n";
1466 (*file)<<"% Ctcp replies activation/desactivation\n";
1467 if(ctcp_reply) (*file)<<"CTCPREPLY\n";
1468 else (*file)<<"% CTCPREPLY\n";
1472 (*file)<<"% Friends list\n";
1473 for(person=level_list.first; person != NULL; person=person->next)
1475 (*file)<<"FRIEND "<<person->body->level<<" "<<person->body->pattern;
1476 if(person->body->passwd != NULL) (*file)<<" "<<person->body->passwd;
1482 (*file)<<"% Restricted-sites list\n";
1483 for(restricted=restricted_list.first;
1485 restricted=restricted->next)
1486 (*file)<<"RESTRICTED "<<restricted->body<<"\n";
1490 (*file)<<"% Shit list\n";
1491 for(welcome=shit_list.first; welcome != NULL; welcome=welcome->next)
1492 (*file)<<"SHIT "<<welcome->body->pattern<<" "<<
1493 welcome->body->time_max - current_time<<" "<<
1494 welcome->body->comment<<"\n";
1497 (*file)<<"% End of config file\n";
1507 //-----------------------------------------------------------------------------
1509 void notice_list_by_sites(DCCChat *chat, char *nick, char *pattern)
1511 NodeList<char *> *node1, *node2;
1512 int *ok, nb, k, l, level, put_blank;
1514 int nb_total, nb_person[LEVEL_MAX+1];
1517 for(k=0; k<LEVEL_MAX+1; k++) nb_person[k] = 0;
1519 nb = present_people.Lenght();
1521 for(k = 0; k<nb; k++) ok[k] = 0;
1523 node1 = present_people.first;
1529 concat(buf, "\002");
1530 concat(buf, pattern);
1531 concat(buf, "\002 and same site : ");
1534 while(node1 != NULL)
1536 if((pattern == NULL) || match_pattern(pattern, node1->body))
1538 c = adr_beginning(node1->body);
1542 while(node2 != NULL)
1544 d = adr_beginning(node2->body);
1545 if(are_same_site(c, d))
1548 level = level_person(d, NULL);
1551 if(put_blank) *buf++ = '/'; else put_blank = 1;
1553 if(level == LEVEL_OP) *buf++ = '\002';
1554 else if(level >= LEVEL_DEFENCE) *buf++ = '\026';
1556 while((*d != '!') && (*d != '\0')) *buf++ = *d++;
1558 if(level == LEVEL_OP) *buf++ = '\002';
1559 else if(level >= LEVEL_DEFENCE) *buf++ = '\026';
1563 if(buf > IRC_buffer + NB_CHAR_MAX)
1565 *buf++ = '\n'; *buf++ = '\0';
1566 tell(chat, nick, IRC_buffer);
1574 node2 = node2->next;
1576 while((node2 != NULL) && (ok[l] == 1));
1585 node1 = node1->next;
1587 while((node1 != NULL) && (ok[k] == 1));
1590 *buf++ = '\n'; *buf++ = '\0';
1591 tell(chat, nick, IRC_buffer);
1593 sprintf(IRC_buffer, "Total %d (\002%d\002)\n",
1595 nb_person[LEVEL_OP] + nb_person[LEVEL_DEFENCE] +
1596 nb_person[LEVEL_MASTER]);
1597 tell(chat, nick, IRC_buffer);
1602 //-----------------------------------------------------------------------------
1604 void notice_shit_list(DCCChat *chat, char *nick, char *pattern, int matched)
1607 char *string_duration;
1608 NodeList<Welcome *> *node;
1610 if(shit_list.Lenght() == 0) tell(chat, nick, "\002Empty shit-list\002\n");
1614 for(node = shit_list.first; node != NULL; node=node->next)
1615 if((matched && match_pattern(pattern, node->body->pattern))
1616 || (!matched && match_pattern(node->body->pattern, pattern)))
1619 string_duration = seconds_to_string(node->body->time_max
1622 sprintf(IRC_buffer, "%s for %s (%s)\n",
1623 node->body->pattern,
1625 node->body->comment);
1626 tell(chat, nick, IRC_buffer);
1627 delete[] string_duration;
1630 if(n == 0) tell(chat, nick, "\002No corresponding shits\002\n");
1634 //-----------------------------------------------------------------------------
1636 void add_in_history(char *prefix, char *action)
1638 NodeList<char *> *node, *pred, *next;
1642 s = new char[strlen(prefix) + strlen(action) + 4];
1651 pred = NULL; next = NULL; node = history.first;
1658 if(pred == NULL) history.first = next;
1659 else pred->next = next;
1660 delete[] node->body;
1668 void notice_history(DCCChat *chat, char *nick)
1670 NodeList<char *> *node;
1672 for(node = history.first; node != NULL; node = node->next)
1674 sprintf(IRC_buffer, "%s\n", node->body);
1675 tell(chat, nick, IRC_buffer);
1679 //-----------------------------------------------------------------------------
1681 int dont_flood_server()
1683 return current_time >= last_answer_time + DELAY_ANSWERS;
1686 void msg_users(DCCChat *chat, char *nick, char *pattern, char *msg)
1688 NodeList<char *> *node;
1692 sprintf(IRC_buffer, "\002[msg to %s]\002 %s\n", pattern, msg);
1693 tell(chat, nick, IRC_buffer);
1696 for(node = present_people.first; node != NULL; node = node->next)
1698 if(match_pattern(pattern, node->body))
1701 nick_user = cut_nick_from_prefix(node->body);
1702 sprintf(IRC_buffer, "PRIVMSG %s :\002[msg to %s]\002 %s\n",
1703 nick_user, pattern, msg);
1704 write_irc(IRC_buffer);
1709 sprintf(IRC_buffer, "\002[msg sent to %d user(s)]\002\n", nb);
1710 tell(chat, nick, IRC_buffer);
1713 //-----------------------------------------------------------------------------
1715 #define CMD_DEFAULT 0
1721 void tropbot_cmd(DCCChat *chat, char *prefix, char *nick, char *msg)
1723 char buffer[SMALL_BUFFER_SIZE], buffer_time[SMALL_BUFFER_SIZE];
1724 char *r, *s, *c, *banid;
1725 char *string_duration;
1726 int cmd, level, l, no_authorized, od;
1728 NodeList<DCCChat *> *node;
1734 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1738 level = level_person(prefix, buffer+1);
1739 if(r != NULL) r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1742 else level = level_person(prefix, NULL);
1745 if(eq("join", buffer)) cmd = CMD_JOIN;
1746 else if(eq("home", buffer)) cmd = CMD_HOME;
1747 else if(eq("b", buffer) || eq("ban", buffer)) cmd = CMD_BAN;
1748 else if(eq("sb", buffer) || eq("siteban", buffer)) cmd = CMD_SBAN;
1752 if(eq("ms", buffer) || eq("myshit", buffer))
1754 if(dont_flood_server()) notice_shit_list(chat, nick, prefix, 0);
1756 else if(eq("write", buffer) || eq("wr", buffer))
1758 if(level >= LEVEL_MASTER)
1762 sprintf(IRC_buffer, "PRIVMSG %s :%s\n", current_channel, r);
1763 write_irc(IRC_buffer);
1766 else no_authorized = 1;
1768 else if(eq("sl", buffer) || eq("shitlist", buffer))
1770 if(level >= LEVEL_FRIEND)
1774 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1777 if(is_pattern(buffer))
1778 notice_shit_list(chat, nick, buffer, 1);
1781 wait_list.Insert(new WaitInfos(buffer, NULL, chat, nick,
1784 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
1785 write_irc(IRC_buffer);
1790 int s1, s2, s3, s4, d;
1791 NodeList<Welcome *> *node;
1792 s1 = 0; s2 = 0; s3 = 0; s4 = 0;
1793 for(node=shit_list.first; node != NULL; node=node->next)
1795 d = node->body->time_max - current_time;
1797 else if(d < 4*3600) s2++;
1798 else if(d < 24*3600) s3++;
1804 "\002%d\002 < 30min < \002%d\002 < "
1805 "4h < \002%d\002 < 24h < \002%d\002\n",
1806 s1+s2+s3+s4, s1, s2, s3, s4);
1807 tell(chat, nick, IRC_buffer);
1810 else no_authorized = 1;
1812 else if(eq("www", buffer))
1814 if(dont_flood_server())
1815 tell(chat, nick, "THX-1138's home page at "
1816 "http://www.eleves.ens.fr:8080/home/fleuret/\n");
1818 else if(eq("w", buffer) || eq("who", buffer))
1820 if(level >= LEVEL_FRIEND)
1822 if(r == NULL) notice_list_by_sites(chat, nick, NULL);
1823 else while(r != NULL)
1825 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1827 notice_list_by_sites(chat, nick, buffer);
1830 else no_authorized = 1;
1832 else if(eq("cr", buffer) || eq("ctcpreply", buffer))
1834 if(level >= LEVEL_OP)
1836 ctcp_reply = !ctcp_reply;
1837 if(ctcp_reply) tell(chat, nick, "CTCP reply ON\n");
1838 else tell(chat, nick, "CTCP reply OFF\n");
1840 else no_authorized = 1;
1842 else if(eq("ac", buffer) || eq("anticlones", buffer))
1844 if(level >= LEVEL_OP)
1846 anti_clone = !anti_clone;
1847 if(anti_clone) tell(chat, nick, "Anti-clones ON\n");
1848 else tell(chat, nick, "Anti-clones OFF\n");
1850 else no_authorized = 1;
1852 else if(eq("ao", buffer) || eq("antifloodoff", buffer))
1854 if(level >= LEVEL_OP)
1858 r = next_word(buffer_time, r, SMALL_BUFFER_SIZE);
1859 duration = string_to_seconds(buffer_time);
1860 anti_flood_off_until = current_time + duration;
1865 anti_flood_off_until = current_time;
1866 anti_flood = !anti_flood;
1870 tell(chat, nick, "Anti-flood off\n");
1871 if(anti_flood_off_until > current_time)
1873 string_duration = seconds_to_string(anti_flood_off_until -
1875 sprintf(IRC_buffer, "Anti-flood off for %s\n",
1877 delete[] string_duration;
1879 tell(chat, nick, IRC_buffer);
1882 tell(chat, nick, "Anti-flood on\n");
1884 else no_authorized = 1;
1886 else if(eq("msg", buffer) || eq("message", buffer))
1888 if(level >= LEVEL_OP)
1892 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1895 if(is_pattern(buffer)) msg_users(chat, nick, buffer, r);
1899 wait_list.Insert(new WaitInfos(buffer, r, chat,
1903 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
1904 write_irc(IRC_buffer);
1909 else no_authorized = 1;
1911 else if(eq("h", buffer) || eq("history", buffer))
1913 if(level >= LEVEL_OP) notice_history(chat, nick);
1914 else no_authorized = 1;
1916 else if(eq("level", buffer))
1918 if(level >= LEVEL_FRIEND)
1922 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
1925 wait_list.Insert(new WaitInfos(buffer, r, chat, nick,
1927 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
1928 write_irc(IRC_buffer);
1932 sprintf(IRC_buffer, "%s is level %d\n", prefix,
1933 level_person(prefix, NULL));
1934 tell(chat, nick, IRC_buffer);
1937 else no_authorized = 1;
1939 else if(eq("mode", buffer))
1941 if(dont_flood_server() || (level >= LEVEL_FRIEND))
1943 char *cop, *ao, *ac, *cr;
1944 if(cop_mode) cop = "ON"; else cop = "OFF";
1945 if(anti_flood) ao = "ON"; else ao = "OFF";
1946 if(anti_clone) ac = "ON"; else ac = "OFF";
1947 if(ctcp_reply) cr = "ON"; else cr = "OFF";
1951 "control_char '%c' op_delay %ds deban_delay %ds "
1952 "cop-mode %s anti-flood %s anti-clone %s ctcp-reply %s"
1953 " your level is %d. I am a father with %d sons.\n",
1954 control_char, op_delay, deban_delay,
1957 tell(chat, nick, IRC_buffer);
1962 "control_char '%c' op_delay %ds deban_delay %ds "
1963 "cop-mode %s anti-flood %s anti-clone %s ctcp-reply %s"
1964 " your level is %d. I am a son.\n",
1965 control_char, op_delay, deban_delay,
1968 tell(chat, nick, IRC_buffer);
1972 else if(eq("help", buffer))
1974 if(level >= LEVEL_FRIEND)
1976 tell(chat, nick, "\002help\002 \002www\002 \002\037s\037\002hit\037l\037\002ist\002 [<nick>!<pattern>] \002level\002 [<nick>] \002\037w\037\002ho\002 \002\037m\037\002y\037s\037\002hit\002\n");
1979 if(level >= LEVEL_OP)
1981 tell(chat, nick, "\002\037a\037\002ntiflood\037o\037\002ff\002 [<duration>] \002\037a\037\002lert\002 \002home\002 \002cop\002 \002op\002 [{<nick> list}] \002join\002 <#channel> \002nick\002 <nick> \002\037p\037\002rune\037b\037\002an\002 <number> \002\037d\037\002e\037b\037\002an\002 {<pattern> list} \002\037f\037\002ilter\037k\037\002ick\002 <pattern>!<nick> [<comment>] \002\037s\037\002hit/\037s\037\002hit\037s\037\002ite/\037s\037\002hit\037h\037\002ost\002 <pattern>!<nick> [<duration> [<comment>]] \002\037u\037\002n\037s\037\002hit\002 {<pattern>!<nick> list} \002\037pu\037\002n\037s\037\002hit\002 {<pattern> list} \002\037b\037\002an/\037s\037\002ite\037b\037\002an\002 <pattern>!<nick> \002synch\002 \002\037h\037\002istory\002\n \002\037m\037\002es\037s\037\002a\037g\037\002e\002 <nick>!<pattern> <msg>\n");
1984 if(level >= LEVEL_MASTER)
1986 tell(chat, nick, "\002reset\002 \002load\002 [<file>] \002save\002 [<file>] \002friend\002 <pattern> <level> [<passwd>] \002clone\002 [<nick>] \002die\002 \002server\002 <hostname> [<port>] \002controlchar\002 <char> \002opdelay\002 <delay in s> \002debandelay\002 <delay in s>\n");
1991 tell(chat, nick, "\002\037u\037\002sers\002 \002\037t\037\002alk\002 <msg>\n");
1995 else if((cmd == CMD_JOIN) || (cmd == CMD_HOME))
1997 if(level >= LEVEL_OP)
1999 if(global_state == STATE_WAIT)
2001 sprintf(IRC_buffer, "PART %s\n", current_channel);
2002 write_irc(IRC_buffer);
2004 strncpy(wanted_channel, home_channel, SMALL_BUFFER_SIZE);
2008 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2009 strncpy(wanted_channel, buffer, SMALL_BUFFER_SIZE);
2011 uncap(wanted_channel);
2012 sprintf(IRC_buffer, "JOIN %s\n", wanted_channel);
2013 write_irc(IRC_buffer);
2016 tell(chat, nick, "Can't join right now, retry in a while\n");
2017 } else no_authorized = 1;
2019 else if(eq("op", buffer))
2021 if(level >= LEVEL_OP)
2023 if(r == NULL) add_mode(current_channel, "+o", nick);
2027 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2028 add_mode(current_channel, "+o", buffer);
2031 else no_authorized = 1;
2033 else if(eq("nick", buffer))
2035 if(level >= LEVEL_OP)
2039 r = next_word(wanted_nick, r, SMALL_BUFFER_SIZE);
2040 sprintf(buffer, "NICK %s\n", wanted_nick);
2043 } else no_authorized = 1;
2045 else if(eq("db", buffer) || eq("deban", buffer))
2047 if(level >= LEVEL_FRIEND)
2051 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2052 if(is_pattern(buffer))
2054 NodeList<char *> *node;
2055 for(node = banid_list.first; node != NULL; node = node->next)
2056 if(match_pattern(buffer, node->body))
2057 add_mode(current_channel, "-b", node->body);
2061 wait_list.Insert(new WaitInfos(buffer, NULL, chat, nick,
2062 MODE_UNBAN_NICK, 0));
2063 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2064 write_irc(IRC_buffer);
2067 } else no_authorized = 1;
2069 else if((cmd == CMD_BAN) || (cmd == CMD_SBAN))
2071 if(level >= LEVEL_OP)
2075 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2078 if(is_pattern(buffer))
2080 banid = clean_banid(buffer);
2081 smart_ban(current_channel, banid);
2089 wait_list.Insert(new WaitInfos(buffer, r, chat, nick,
2093 wait_list.Insert(new WaitInfos(buffer, r, chat, nick,
2099 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2100 write_irc(IRC_buffer);
2104 else no_authorized = 1;
2106 else if(eq("pb", buffer) || eq("pruneban", buffer))
2108 if(level >= LEVEL_OP)
2112 NodeList<char *> *node;
2114 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2118 for(node = banid_list.first; node != NULL;
2120 if(n == 0) add_mode(current_channel, "-b", node->body);
2124 } else no_authorized = 1;
2126 else if(eq("synch", buffer))
2128 if(level >= LEVEL_OP) synch(current_channel);
2129 else no_authorized = 1;
2131 else if(eq("cop", buffer))
2133 if(level >= LEVEL_OP)
2135 cop_mode = !cop_mode;
2136 if(cop_mode) tell(chat, nick, "cop mode ON\n");
2137 else tell(chat, nick, "cop mode OFF\n");
2139 else no_authorized = 1;
2141 else if(eq("s", buffer) || eq("shit", buffer))
2143 if(level >= LEVEL_OP)
2147 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2149 if(is_pattern(buffer))
2153 r = next_word(buffer_time, r, SMALL_BUFFER_SIZE);
2154 duration = string_to_seconds(buffer_time);
2156 else duration = DEFAULT_SHIT_TIME;
2158 if(r == NULL) r = "Shit nick";
2161 banid = clean_banid(buffer);
2162 smart_shit(chat, nick, banid, r, duration);
2169 r = next_word(buffer_time, r, SMALL_BUFFER_SIZE);
2170 duration = string_to_seconds(buffer_time);
2172 else duration = DEFAULT_SHIT_TIME;
2174 if(r == NULL) r = "Shit nick";
2177 wait_list.Insert(new WaitInfos(buffer, r, chat, nick,
2181 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2182 write_irc(IRC_buffer);
2186 else no_authorized = 1;
2189 else if(eq("fk", buffer) || eq("filterkick", buffer))
2191 if(level >= LEVEL_OP)
2195 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2197 if(is_pattern(buffer))
2199 if(r == NULL) r = "Filter kick";
2200 filter_kick(current_channel, buffer, r);
2204 if(r == NULL) r = "Filter Kick";
2205 wait_list.Insert(new
2206 WaitInfos(buffer, r, chat, nick,
2207 MODE_FILTER_KICK, 0));
2208 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2209 write_irc(IRC_buffer);
2213 else no_authorized = 1;
2216 else if(eq("a", buffer) || eq("alert", buffer))
2218 if(level >= LEVEL_OP) alert(current_channel, chat, nick);
2219 else no_authorized = 1;
2222 else if(eq("sh", buffer) || eq("shithost", buffer))
2224 if(level >= LEVEL_OP)
2228 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2232 r = next_word(buffer_time, r, SMALL_BUFFER_SIZE);
2233 duration = string_to_seconds(buffer_time);
2235 else duration = DEFAULT_SHIT_TIME;
2237 if(r == NULL) r = "Shit host";
2240 wait_list.Insert(new WaitInfos(buffer, r, chat, nick,
2241 MODE_SHIT_HOST, duration));
2243 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2244 write_irc(IRC_buffer);
2247 else no_authorized = 1;
2249 else if(eq("ss", buffer) || eq("shitsite", buffer))
2251 if(level >= LEVEL_OP)
2255 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2259 r = next_word(buffer_time, r, SMALL_BUFFER_SIZE);
2260 duration = string_to_seconds(buffer_time);
2262 else duration = DEFAULT_SHIT_TIME;
2264 if(r == NULL) r = "Shit site";
2267 wait_list.Insert(new WaitInfos(buffer, r, chat, nick,
2268 MODE_SHIT_SITE, duration));
2270 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2271 write_irc(IRC_buffer);
2274 else no_authorized = 1;
2276 else if(eq("pus", buffer) || eq("punshit", buffer))
2278 if(level >= LEVEL_OP)
2282 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2284 if(strlen(buffer) > 0)
2286 NodeList<Welcome *> *node, *next, *pred;
2289 node = shit_list.first;
2293 if(match_pattern(buffer, node->body->pattern))
2295 smart_unban(current_channel, node->body->pattern);
2296 if(pred == NULL) shit_list.first = next;
2297 else pred->next = next;
2300 node->body->pattern,
2301 node->body->comment);
2302 tell(chat, nick, IRC_buffer);
2312 else no_authorized = 1;
2314 else if(eq("us", buffer) || eq("unshit", buffer))
2316 if(level >= LEVEL_OP)
2320 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2323 if(is_pattern(buffer))
2325 NodeList<Welcome *> *node, *next, *pred;
2328 node = shit_list.first;
2332 if(strcmp(node->body->pattern, buffer) == 0)
2334 if(pred == NULL) shit_list.first = next;
2335 else pred->next = next;
2338 buffer, node->body->comment);
2339 tell(chat, nick, IRC_buffer);
2349 wait_list.Insert(new WaitInfos(buffer, NULL, chat, nick,
2350 MODE_UNSHIT_NICK, 0));
2352 sprintf(IRC_buffer, "WHOIS %s\n", buffer);
2353 write_irc(IRC_buffer);
2357 else no_authorized = 1;
2359 else if(eq("friend", buffer))
2361 if(level >= LEVEL_MASTER)
2365 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2368 r = next_word(buffer_time, r, SMALL_BUFFER_SIZE);
2369 l = atoi(buffer_time);
2370 if((l > 0) && (l<=LEVEL_MASTER))
2372 level_list.Insert(new Person(buffer, l, r));
2374 "Adding %s with level %d and passwd %s\n",
2376 tell(chat, nick, IRC_buffer);
2377 SaveConfigFile(config_file);
2382 else no_authorized = 1;
2384 else if(eq("opdelay", buffer))
2386 if(level >= LEVEL_MASTER)
2390 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2392 if((od>=0) && (od <= 60))
2395 sprintf(IRC_buffer,"Op delay set to %ds\n", op_delay);
2396 tell(chat, nick, IRC_buffer);
2400 else no_authorized = 1;
2402 else if(eq("debandelay", buffer))
2404 if(level >= LEVEL_MASTER)
2408 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2410 if((od>=0) && (od <= 60))
2414 "Deban delay set to %ds\n",
2416 tell(chat, nick, IRC_buffer);
2420 else no_authorized = 1;
2422 else if(eq("controlchar", buffer))
2424 if(level >= LEVEL_MASTER)
2428 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2429 control_char = buffer[0];
2430 sprintf(IRC_buffer, "Control char set to '%c'\n",
2432 tell(chat, nick, IRC_buffer);
2435 else no_authorized = 1;
2437 else if(eq("die", buffer))
2439 if(level >= LEVEL_MASTER)
2441 sprintf(IRC_buffer, "QUIT :Killed by %s\n", nick);
2442 write_irc(IRC_buffer);
2445 else no_authorized = 1;
2447 else if(eq("server", buffer))
2449 if(level >= LEVEL_MASTER)
2453 next_word(wanted_server, r, SMALL_BUFFER_SIZE);
2456 next_word(buffer, r, SMALL_BUFFER_SIZE);
2457 wanted_port = atoi(buffer);
2458 if(wanted_port == 0) wanted_port = 6667;
2460 else wanted_port = DEFAULT_PORT;
2463 "Trying to connect %s:%d\n",
2464 wanted_server, wanted_port);
2465 tell(chat, nick, IRC_buffer);
2467 sprintf(IRC_buffer, "QUIT :Changing server\n");
2468 write_irc(IRC_buffer);
2471 cerr<<"KILLING CONNECTION : Changing server\n";
2477 else no_authorized = 1;
2479 else if(eq("clone", buffer))
2481 if(level >= LEVEL_MASTER)
2485 if(nb_sons < NB_SONS_MAX)
2487 if(r != NULL) next_word(buffer, r, SMALL_BUFFER_SIZE);
2491 else tell(chat, nick, "Too many clones, can't clone\n");
2495 tell(chat, nick, "I'm a clone, can't clone\n");
2498 else no_authorized = 1;
2500 else if(eq("save", buffer))
2502 if(level >= LEVEL_MASTER)
2504 if(r != NULL) r = next_word(config_file, r, SMALL_BUFFER_SIZE);
2505 if(SaveConfigFile(config_file))
2508 "Can't save the %s configuration file\n",
2514 "Saving the %s configuration file\n",
2517 tell(chat, nick, IRC_buffer);
2519 else no_authorized = 1;
2521 else if(eq("load", buffer))
2523 if(level >= LEVEL_MASTER)
2525 if(r != NULL) r = next_word(config_file, r, SMALL_BUFFER_SIZE);
2526 if(LoadConfigFile(config_file, 0))
2529 "Can't load the %s configuration file\n",
2535 "Loading the %s configuration file\n",
2538 tell(chat, nick, IRC_buffer);
2540 else no_authorized = 1;
2542 else if(eq("reset", buffer))
2544 if(level >= LEVEL_MASTER)
2546 sprintf(IRC_buffer, "QUIT :reset command sent by %s\n", nick);
2547 write_irc(IRC_buffer);
2549 cerr<<"KILLING CONNECTION : Die\n";
2554 else no_authorized = 1;
2557 // DCC CHAT only commands
2559 else if((chat != NULL) && (eq("t", buffer) || eq("talk", buffer)))
2561 NodeList<DCCChat *> *node;
2562 sprintf(IRC_buffer, "<%s> %s\n", prefix, r);
2563 for(node = dcc_chat_list.first; node != NULL; node = node->next)
2564 if(node->body != chat) tell(node->body, NULL, IRC_buffer);
2566 else if((chat != NULL) && (eq("u", buffer) || eq("users", buffer)))
2568 for(node = dcc_chat_list.first; node != NULL; node = node->next)
2570 sprintf(IRC_buffer, "%s\n", node->body->prefix);
2571 tell(chat, NULL, IRC_buffer);
2577 else if(dont_flood_server())
2579 sprintf(IRC_buffer, "Unknown command \002%s\002\n", buffer);
2580 tell(chat, nick, IRC_buffer);
2586 if(dont_flood_server())
2589 "You are not authorized to use \002%s\002\n", buffer);
2590 tell(chat, nick, IRC_buffer);
2596 if(*r == '&') r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2597 add_in_history(prefix, r);
2601 //-----------------------------------------------------------------------------
2603 int accepting_dcc_chat(char *prefix, char *nick, int level, char *r)
2605 NodeList<DCCChat *> *node;
2606 int socket, port_number;
2607 char host[SMALL_BUFFER_SIZE], port[SMALL_BUFFER_SIZE];
2610 if(r == NULL) return 1;
2611 else r = next_word(host, r, SMALL_BUFFER_SIZE);
2613 if(strcmp(host, "chat") != 0) return 1;
2615 if(r == NULL) return 1;
2616 else r = next_word(host, r, SMALL_BUFFER_SIZE);
2618 if(r == NULL) return 1;
2619 else r = next_word(port, r, SMALL_BUFFER_SIZE);
2621 port_number = atoi(port);
2622 socket = call_socket(host, port_number);
2624 if(socket <= 0) return 1;
2627 dcc_chat = new DCCChat(prefix, socket, port_number);
2628 dcc_chat_list.Insert(dcc_chat);
2630 FD_SET(socket, &ready);
2633 "Welcome to \002TropBot\002 " VERSION " on a DCC CHAT\n"
2634 "You are known as %s, your level is %d.\n"
2635 "You are client #%d on DCC CHAT\n",
2636 prefix, level, dcc_chat_list.Lenght());
2638 write(socket, IRC_buffer, strlen(IRC_buffer));
2640 sprintf(IRC_buffer, "%s arrives with level %d.\n", prefix, level);
2641 for(node=dcc_chat_list.first; node!= NULL; node = node->next)
2642 if(node->body != dcc_chat) tell(node->body, NULL, IRC_buffer);
2648 //-----------------------------------------------------------------------------
2650 void pong_reply(char *nick, char *value)
2652 NodeList<WaitPing *> *node, *next, *pred;
2654 cout<<"pong_reply : nick >"<<nick<<"< value >"<<value<<"<\n";
2657 node = wait_ping.first;
2661 if(current_time > node->body->time_max)
2663 if(pred == NULL) wait_ping.first = next;
2664 else pred->next = next;
2668 if(strcmp(nick, node->body->nick) == 0)
2670 if(strcmp(value, node->body->value) == 0)
2671 add_mode(current_channel, "+o", nick);
2673 if(pred == NULL) wait_ping.first = next;
2674 else pred->next = next;
2683 // This function is called after a NOTICE
2684 void IRC_NOTICE(char *prefix,
2685 char *who, char *msg, char **slice_ctcp, int n_ctcp)
2688 char word[SMALL_BUFFER_SIZE], *r, *nick;
2690 if(strcmp(who, current_channel) == 0) add_flood_line(prefix, FL_NOTICE, 1);
2692 else if(strcmp(who, real_nick) == 0)
2693 for(k=0; k<n_ctcp; k++)
2695 cout<<"ctcp >"<<slice_ctcp[k]<<"<\n";
2697 r = next_word(word, r, SMALL_BUFFER_SIZE);
2698 if(strcmp(word, "PING") == 0)
2700 nick = cut_nick_from_prefix(prefix);
2701 r = next_word(word, r, SMALL_BUFFER_SIZE);
2702 pong_reply(nick, word);
2709 void IRC_PRIVMSG(char *prefix,
2710 char *who, char *msg, char **slice_ctcp, int n_ctcp)
2712 int k, version, ping, level, ctcp, kick;
2713 char *nick, *r, *pattern, *banid;
2714 char buffer[SMALL_BUFFER_SIZE];
2717 nick = cut_nick_from_prefix(prefix);
2719 level = level_person(prefix, NULL);
2722 if(strcmp(who, current_channel) == 0)
2724 add_flood_line(prefix, FL_PUBLIC, 1);
2728 add_flood_line(prefix, FL_CTCP, n_ctcp);
2730 if((cop_mode) && (level < LEVEL_FRIEND))
2733 for(k = 0; (k<n_ctcp) && !ctcp; k++)
2736 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2737 if(strcmp(buffer, "ACTION") != 0) ctcp =1;
2740 if(!kick && (r != NULL) && (bad_line(r, buffer)))
2742 sprintf(IRC_buffer,"KICK %s %s :cop : %s\n",
2743 current_channel, nick, buffer);
2744 write_irc(IRC_buffer);
2750 if(!kick) if(ctcp) if(level < LEVEL_FRIEND)
2752 pattern = pattern_from_prefix(prefix, 0);
2753 banid = clean_banid(pattern);
2754 smart_shit(NULL, NULL, pattern, "cop : no ctcp",
2755 COP_DURATION + deban_delay);
2756 smart_ban(current_channel, pattern);
2757 send_mode(current_channel);
2758 sprintf(IRC_buffer, "KICK %s %s :cop : no ctcp\n",
2759 current_channel, nick);
2760 write_irc(IRC_buffer);
2767 else if(cop_mode) if(level < LEVEL_FRIEND)
2768 if(!kick) if((msg != NULL) && bad_line(msg, buffer))
2770 sprintf(IRC_buffer, "KICK %s %s :cop : %s\n",
2771 current_channel, nick, buffer);
2772 write_irc(IRC_buffer);
2776 if(msg != NULL) if(strlen(msg)>0)
2778 if(msg[0] == control_char) tropbot_cmd(NULL, prefix, nick, msg+1);
2779 else if(strcmp(who, real_nick) == 0)
2780 tropbot_cmd(NULL, prefix, nick, msg);
2787 for(k=0; k<n_ctcp; k++)
2790 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2792 // Reply the CTCP VERSION
2793 if(eq("VERSION", buffer) && !version)
2795 if(dont_flood_server())
2798 sprintf(IRC_buffer, "NOTICE %s :"
2799 "\001VERSION \002TropBot\002 " VERSION OPTIONS
2800 ", " SYSTEM " system, " DATE ". "
2801 "Contact THX-1138 on IRCNet, or <francois.fleuret@inria.fr>"
2804 write_irc(IRC_buffer);
2807 // Reply the CTCP PING
2808 else if(eq("PING", buffer) && !ping)
2812 if(dont_flood_server())
2815 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2817 "NOTICE %s :\001PING %s\001\n", nick, buffer);
2818 write_irc(IRC_buffer);
2822 else if(eq("DCC", buffer))
2824 if(level >= LEVEL_OP)
2828 r = next_word(buffer, r, SMALL_BUFFER_SIZE);
2829 if(eq("CHAT", buffer))
2831 if(accepting_dcc_chat(prefix, nick, level, r) != 0)
2833 sprintf(IRC_buffer, "NOTICE %s :\002Error\002"
2834 " can't connect\n", nick);
2835 write_irc(IRC_buffer);
2841 "NOTICE %s :I can only DCC CHAT\n", nick);
2842 write_irc(IRC_buffer);
2846 else if(dont_flood_server())
2849 "NOTICE %s :Sorry, can't accept DCC from you\n",
2851 write_irc(IRC_buffer);
2859 //-----------------------------------------------------------------------------
2861 // This function is called after a QUIT
2863 void IRC_QUIT(char *prefix)
2865 present_people.Remove(prefix);
2868 //-----------------------------------------------------------------------------
2870 // This function is called after a NICK
2872 void IRC_NICK(char *prefix, char *nick)
2876 add_flood_line(prefix, FL_NICK, 1);
2877 present_people.Remove(prefix);
2879 t = nick; while(*t != '\0') *s++=*t++; // copy the nick
2880 *s++='!'; // put the '!'
2881 t = reach_loginathost(prefix);
2882 while(*t != '\0') *s++=*t++; // copy the user@host
2883 *s = '\0'; // end of string
2884 present_people.Add(IRC_buffer); // hop !
2886 ok = 1; s = prefix; t = real_nick;
2887 while(ok && (*t != '\0') && (*s != '\0')) ok = (*s++ == *t++);
2888 if(ok && (*t == '\0') && (*s == '!'))
2890 strncpy(real_nick, nick, SMALL_BUFFER_SIZE);
2895 //-----------------------------------------------------------------------------
2897 // This function is called after a PART
2899 void IRC_PART(char *prefix, char *where)
2902 add_flood_line(prefix, FL_PART, 1);
2903 present_people.Remove(prefix);
2904 nick = cut_nick_from_prefix(prefix);
2905 if(strcmp(real_nick, nick) == 0)
2907 present_people.Clear();
2913 //-----------------------------------------------------------------------------
2915 // This function is called after a KICK
2917 void IRC_KICK(char *prefix, char *where, char *victim_nick)
2922 if(strcmp(victim_nick, real_nick) == 0)
2924 present_people.Clear();
2926 sprintf(IRC_buffer, "JOIN %s\n", wanted_channel);
2927 write_irc(IRC_buffer);
2930 c = strdup(prefix_from_nick(victim_nick));
2932 if((level_person(prefix, NULL) < level_person(prefix_from_nick(victim_nick), NULL)) &&
2933 (level_person(prefix_from_nick(victim_nick), NULL) > 0))
2935 d = cut_nick_from_prefix(prefix);
2936 add_mode(where, "-o", d);
2939 else if(cop_mode && (c != NULL)) add_kick(c);
2941 if(c != NULL) present_people.Remove(c);
2943 else cerr<<"** ERROR : non present person has been kicked out **\n";
2949 //-----------------------------------------------------------------------------
2951 int check_restricted(char *where, char *prefix)
2953 char pattern[SMALL_BUFFER_SIZE];
2954 NodeList<char *> *node;
2958 p = adr_beginning(prefix);
2960 node = present_people.first;
2962 while((node != NULL) && (n < 2))
2964 s = adr_beginning(node->body);
2965 if(strcmp(s, p) == 0) n++;
2975 duration = DEFAULT_RESTRICTED_TIME + deban_delay;
2976 smart_shit(NULL, NULL, pattern, "Restricted site : no clones", duration);
2977 smart_ban(where, pattern);
2979 filter_kick(where, pattern, "Restricted site : no clones");
2985 // This function is called after a JOIN
2987 void IRC_JOIN(char *prefix, char *where)
2990 int k, l, restricted;
2991 NodeList<Welcome *> *node;
2992 char buffer[SMALL_BUFFER_SIZE];
2994 nick = cut_nick_from_prefix(prefix);
2996 if(strcmp(real_nick, nick) == 0)
2998 strncpy(current_channel, where, SMALL_BUFFER_SIZE);
2999 sprintf(IRC_buffer, "WHO %s\n", where);
3000 write_irc(IRC_buffer);
3001 present_people.Clear();
3002 global_state = STATE_GETING_WHO;
3007 add_flood_line(prefix, FL_JOIN, 1);
3010 present_people.Add(prefix);
3012 l = level_person(prefix, NULL);
3014 if(anti_clone && (l < LEVEL_FRIEND) && (restricted_list.Matches(prefix)))
3015 restricted = check_restricted(where, prefix);
3016 else restricted = 0;
3021 if(l >= LEVEL_FRIEND)
3023 for(k=0; k<10; k++) buffer[k] = 'a'+int(rand()%26);
3025 wait_ping.Insert(new WaitPing(nick, buffer,
3026 current_time+ANTI_SPOOF_MAX_TIME));
3027 sprintf(IRC_buffer, "PRIVMSG %s :\001PING %s\001\n",
3029 write_irc(IRC_buffer);
3036 if(l >= LEVEL_DEFENCE) add_mode(where, "+o", nick);
3038 mode_change_list.Insert(new DelayModeChange(where, "+o",
3044 if(banid_list.Matches(prefix))
3046 sprintf(IRC_buffer, "KICK %s %s :You are banned\n", where, nick);
3047 write_irc(IRC_buffer);
3051 for(node = shit_list.first; node != NULL; node = node->next)
3052 if(match_pattern(node->body->pattern, prefix))
3054 smart_ban(where, node->body->pattern);
3055 send_mode(current_channel);
3057 sprintf(IRC_buffer, "KICK %s %s :%s\n",
3058 where, nick, node->body->comment);
3060 write_irc(IRC_buffer);
3070 //-----------------------------------------------------------------------------
3072 void IRC_RPL_BANLIST(char *prefix, char *channel, char *banid)
3074 banid_list.Add(banid);
3077 void IRC_ENDOFBANLIST(char *prefix)
3079 banid_list.Reverse();
3080 global_state = STATE_WAIT;
3083 void IRC_RPL_ENDOFWHOIS(char *prefix, char *nick)
3085 NodeList<WaitInfos *> *node, *pred, *next;
3088 node = wait_list.first;
3093 if(strcmp(nick, node->body->nick) == 0)
3095 sprintf(IRC_buffer, "Can't find %s\n", nick);
3096 tell(node->body->chat, node->body->user, IRC_buffer);
3098 if(pred == NULL) wait_list.first = next;
3099 else pred->next = next;
3108 void IRC_RPL_WHOISUSER(char *prefix,
3109 char *nick, char *login, char *host, char *real_name)
3111 char *c, *d, *banid;
3113 char buffer[SMALL_BUFFER_SIZE];
3114 NodeList<WaitInfos *> *node, *next, *pred;
3116 int no_pattern, one_login, all_machines;
3123 node = wait_list.first;
3128 if(strcmp(nick, node->body->nick) == 0)
3132 no_pattern = (node->body->mode == MODE_UNSHIT_NICK) ||
3133 (node->body->mode == MODE_LEVEL) ||
3134 (node->body->mode == MODE_UNBAN_NICK) ||
3135 (node->body->mode == MODE_SHITLIST);;
3137 one_login = (node->body->mode == MODE_SHIT_NICK) ||
3138 (node->body->mode == MODE_BAN_NICK);
3140 all_machines = (node->body->mode == MODE_SHIT_NICK) ||
3141 (node->body->mode == MODE_BAN_NICK) ||
3142 (node->body->mode == MODE_BAN_SITE) ||
3143 (node->body->mode == MODE_FILTER_KICK) ||
3144 (node->body->mode == MODE_SHIT_SITE) ||
3145 (node->body->mode == MODE_MSG);
3158 if((*login == '~') || (*login == '+') ||
3159 (*login == '-') || (*login=='^'))
3168 while((*login != '\0') && ((l<7) || *(login+1) == '\0'))
3169 { *c++ = *login++; l++; }
3170 if(*login != '\0') *c++ = '*';
3175 d = adr_beginning(host);
3176 if(host != d) if(*(c-1) != '*') *c++ = '*';
3178 if(all_machines) concat_pattern_from_host(c, d);
3188 banid = clean_banid(buffer);
3190 if(node->body->mode == MODE_MSG)
3191 msg_users(node->body->chat, node->body->user,
3192 banid, node->body->comment);
3194 else if(node->body->mode == MODE_FILTER_KICK)
3195 filter_kick(current_channel, banid, node->body->comment);
3197 else if((node->body->mode == MODE_BAN_NICK) ||
3198 (node->body->mode == MODE_BAN_SITE))
3199 smart_ban(current_channel, banid);
3201 else if((node->body->mode == MODE_SHIT_NICK)
3202 || (node->body->mode == MODE_SHIT_SITE)
3203 || (node->body->mode == MODE_SHIT_HOST))
3205 smart_shit(node->body->chat,
3206 node->body->user, banid, node->body->comment,
3207 node->body->duration);
3209 if(prefix_from_nick(nick) != NULL)
3211 smart_ban(current_channel, banid);
3212 send_mode(current_channel);
3213 sprintf(IRC_buffer, "KICK %s %s :%s\n",
3214 current_channel, nick, node->body->comment);
3215 write_irc(IRC_buffer);
3220 else if(node->body->mode == MODE_SHITLIST)
3221 notice_shit_list(NULL, node->body->user, banid, 0);
3223 else if(node->body->mode == MODE_LEVEL)
3225 sprintf(IRC_buffer, "%s is level %d\n",
3226 banid, level_person(banid, NULL));
3227 tell(node->body->chat, node->body->user, IRC_buffer);
3229 else if(node->body->mode == MODE_UNSHIT_NICK)
3231 NodeList<Welcome *> *node2, *pred, *next;
3233 node2 = shit_list.first;
3235 while(node2 != NULL)
3238 if(match_pattern(node2->body->pattern, banid))
3240 if(pred == NULL) shit_list.first = next;
3241 else pred->next = next;
3243 sprintf(IRC_buffer, "Unshit %s (%s)\n",
3244 node2->body->pattern, node2->body->comment);
3246 tell(node->body->chat, node->body->user, IRC_buffer);
3248 smart_unban(current_channel, node2->body->pattern);
3257 else if(node->body->mode == MODE_UNBAN_NICK)
3259 NodeList<char *> *node2;
3260 for(node2 = banid_list.first; node2 != NULL; node2 = node2->next)
3261 if(match_pattern(node2->body, banid))
3262 add_mode(current_channel, "-b", node2->body);
3268 if(pred == NULL) wait_list.first = next;
3269 else pred->next = next;
3278 // This function is called after a RPL_WHOREPLY
3280 void IRC_RPL_WHOREPLY(char *prefix,
3282 char *login, char *host, char *server,
3283 char *nick, char *state)
3290 if((global_state == STATE_GETING_WHO) &&
3291 (strcmp(where, current_channel) == 0))
3294 t = nick; while(*t != '\0') *s++ = *t++;
3296 t = login; while(*t != '\0') *s++ = *t++;
3298 t = host; while(*t != '\0') *s++ = *t++;
3300 present_people.Add(IRC_buffer);
3303 else cerr<<where<<"!="<<current_channel<<"\n";
3307 void IRC_RPL_ENDOFWHO(char *prefix, char *name)
3309 sprintf(IRC_buffer, "MODE %s +b\n", current_channel);
3310 write_irc(IRC_buffer);
3312 if(in_channel) global_state = STATE_GETING_BAN;
3313 else global_state = STATE_WAIT;
3316 //-----------------------------------------------------------------------------
3318 void IRC_ERR_NICKNAMEINUSE(char *prefix, char *channel)
3320 rand_nick(wanted_nick, jam_nick);
3321 #ifdef SCREEN_OUTPUT
3322 cout<<"Nickname collision, trying "<<wanted_nick<<"\n";
3324 sprintf(IRC_buffer, "NICK %s\n", wanted_nick);
3325 write_irc(IRC_buffer);
3326 was_killed = 1; // We have been killed (I said PARANOID !)
3327 time_killed = current_time; // Note when it happens
3330 //-----------------------------------------------------------------------------
3332 // This function is called after a ERR_NOSUCHCHANNEL
3334 void IRC_ERR_NOSUCHCHANNEL(char *prefix, char *nick)
3336 strncpy(wanted_channel, home_channel, SMALL_BUFFER_SIZE);
3337 uncap(wanted_channel);
3340 //-----------------------------------------------------------------------------
3342 // This function is called after a ERR_NICKCOLLISION (436)
3344 void IRC_ERR_NICKCOLLISION(char *prefix, char *nick)
3346 rand_nick(wanted_nick, jam_nick);
3347 #ifdef SCREEN_OUTPUT
3348 cout<<"Nickname collision, trying "<<wanted_nick<<"\n";
3350 sprintf(IRC_buffer, "NICK %s\n", wanted_nick);
3351 write_irc(IRC_buffer);
3352 was_killed = 1; // We have been killed (I said PARANOID !)
3353 time_killed = current_time; // Note when it happens
3356 //-----------------------------------------------------------------------------
3358 // This is the main command. It is called for each line from the server.
3360 void get_command(char *prefix,
3361 char **slice_cmd, int n_cmd,
3362 char **slice_ctcp, int n_ctcp)
3364 char small_buffer[SMALL_BUFFER_SIZE];
3369 #ifdef SCREEN_OUTPUT
3371 cout<<"from "<<prefix<<" ";
3373 for(k=0; k<n_cmd; k++) cout<<"["<<slice_cmd[k]<<"]";
3374 if(n_ctcp > 0) for(k=0; k<n_ctcp; k++) cout<<"<"<<slice_ctcp[k]<<">";
3376 for(k=0; k<n_cmd; k++) cout<<"["<<(void *) slice_cmd[k]<<"]";
3377 if(n_ctcp > 0) for(k=0; k<n_ctcp; k++) cout<<"<"<<(void *) slice_ctcp[k]<<">";
3381 if(prefix != NULL) uncap(prefix);
3383 // No prefix : server command
3386 if(n_cmd > 0) if(eq("PING", cmd))
3388 if(n_cmd >= 2) sprintf(IRC_buffer, "PONG :%s\n", slice_cmd[1]);
3389 else sprintf(IRC_buffer, "PONG\n");
3390 write_irc(IRC_buffer);
3395 // If we are not registred yet, then get the registration
3400 strncpy(real_nick, slice_cmd[1], SMALL_BUFFER_SIZE);
3403 sprintf(small_buffer, "MODE %s +i\n", real_nick);
3404 write_irc(small_buffer);
3405 sprintf(small_buffer, "JOIN %s\n", wanted_channel);
3406 write_irc(small_buffer);
3409 else if(eq("PRIVMSG", cmd))
3410 IRC_PRIVMSG(prefix, slice_cmd[1], slice_cmd[2], slice_ctcp, n_ctcp);
3411 else if(eq("NOTICE", cmd))
3412 IRC_NOTICE(prefix, slice_cmd[1], slice_cmd[2], slice_ctcp, n_ctcp);
3413 else if(eq("MODE", cmd))
3414 IRC_MODE(slice_cmd[1], prefix, slice_cmd[2], slice_cmd+3);
3415 else if(eq("JOIN", cmd)) IRC_JOIN(prefix, slice_cmd[1]);
3416 else if(eq("KICK", cmd)) IRC_KICK(prefix, slice_cmd[1], slice_cmd[2]);
3417 else if(eq("QUIT", cmd)) IRC_QUIT(prefix);
3418 else if(eq("NICK", cmd)) IRC_NICK(prefix, slice_cmd[1]);
3419 else if(eq("PART", cmd)) IRC_PART(prefix, slice_cmd[1]);
3420 else if(eq("311", cmd))
3421 IRC_RPL_WHOISUSER(prefix,
3422 slice_cmd[2], slice_cmd[3], slice_cmd[4],
3424 else if(eq("318", cmd)) IRC_RPL_ENDOFWHOIS(prefix, slice_cmd[2]);
3425 else if(eq("352", cmd))
3426 IRC_RPL_WHOREPLY(prefix,
3427 slice_cmd[2], slice_cmd[3], slice_cmd[4],
3428 slice_cmd[5], slice_cmd[6], slice_cmd[7]);
3429 else if(eq("315", cmd)) IRC_RPL_ENDOFWHO(prefix, slice_cmd[1]);
3430 else if(eq("367", cmd))
3431 IRC_RPL_BANLIST(prefix, slice_cmd[2], slice_cmd[3]);
3432 else if(eq("368", cmd)) IRC_ENDOFBANLIST(prefix);
3433 else if(eq("403", cmd)) IRC_ERR_NOSUCHCHANNEL(prefix, slice_cmd[1]);
3434 else if(eq("433", cmd)) IRC_ERR_NICKNAMEINUSE(prefix, slice_cmd[1]);
3435 else if(eq("436", cmd)) IRC_ERR_NICKCOLLISION(prefix, slice_cmd[1]);
3439 //-----------------------------------------------------------------------------
3441 // Rough routine to read the options
3443 void get_options(int argc, char **argv)
3451 if(eq("-p", argv[n]))
3454 if(n<argc) default_port = atoi(argv[n]);
3456 else cerr<<"*** No port parameter ***\n";
3459 else if(eq("-h", argv[n]))
3462 if(n<argc) strncpy(default_server, argv[n], MAXHOSTNAME);
3464 else cerr<<"*** No hostname parameter ***\n";
3467 else if(eq("-o", argv[n]))
3472 strncpy(config_file, argv[n], SMALL_BUFFER_SIZE);
3473 LoadConfigFile(argv[n], 0);
3476 else cerr<<"*** No friends list parameter ***\n";
3479 else if(eq("-c", argv[n]))
3482 if(n<argc) strncpy(home_channel, argv[n], SMALL_BUFFER_SIZE);
3484 else cerr<<"*** No channel parameter ***\n";
3487 else if(eq("-l", argv[n]))
3490 if(n<argc) strncpy(user_login, argv[n], SMALL_BUFFER_SIZE);
3492 else cerr<<"*** No username parameter ***\n";
3495 else if(eq("-n", argv[n]))
3498 if(n<argc) strncpy(original_nick, argv[n], SMALL_BUFFER_SIZE);
3500 else cerr<<"*** No nickname parameter ***\n";
3503 else if(eq("-j", argv[n]))
3506 if(n<argc) strncpy(jam_nick, argv[n], SMALL_BUFFER_SIZE);
3508 else cerr<<"*** No jam nickname parameter ***\n";
3511 else if(eq("-cop", argv[n]))
3515 else if(eq("-d", argv[n]))
3521 if(d>=1) socket_delay = d;
3523 else cerr<<"*** Delay error ***\n";
3527 else cerr<<"*** No delay parameter ***\n";
3530 else if(eq("-od", argv[n]))
3536 if(d>=1) op_delay = d;
3538 else cerr<<"*** Op delay error ***\n";
3542 else cerr<<"*** No delay parameter ***\n";
3545 else if(eq("-dd", argv[n]))
3551 if(d>=1) deban_delay = d;
3553 else cerr<<"*** Deban delay error ***\n";
3557 else cerr<<"*** No delay parameter ***\n";
3560 else if(eq("-?", argv[n])) help = 1;
3564 cerr<<"*** Unknown option "<<argv[n]<<" ***\n";
3571 #ifdef SCREEN_OUTPUT
3572 cout<<"Tropbot " VERSION " Written by Francois Fleuret.\n";
3573 cout<<"Compiled the " DATE " on a " SYSTEM " system.\n";
3574 cout<<"Contact <francois.fleuret@inria.fr>.\n";
3576 cout<<"Options are :\n";
3577 cout<<"-c <#channel> sets the channel\n";
3578 cout<<"-d <delay in s> sets the reconnection delay\n";
3579 cout<<"-h <hostname> sets the server name\n";
3580 cout<<"-j <jam nickname> sets the pattern nick for collision\n";
3581 cout<<"-l <user> sets the user in the user@host stuff\n";
3582 cout<<"-n <nickname> sets the nickname\n";
3583 cout<<"-o <friends file> loads configuration file\n";
3584 cout<<"-od <delay in s> set the delay before +o\n";
3585 cout<<"-dd <delay in s> set the delay before -b\n";
3586 cout<<"-p <port number> sets the server port\n";
3587 cout<<"-? shows this help\n";
3596 //-----------------------------------------------------------------------------
3598 void clean_shit_list()
3600 NodeList<Welcome *> *node, *pred, *next;
3602 node = shit_list.first;
3607 if(current_time >= node->body->time_max)
3609 if(pred == NULL) shit_list.first = next;
3610 else pred->next = next;
3612 smart_unban(current_channel, node->body->pattern);
3620 send_mode(current_channel);
3623 //-----------------------------------------------------------------------------
3625 void try_reconnect()
3627 if(delay < DELAY_MAX_RECONNECT)
3629 if(delay == 0) delay = 1;
3631 if(delay > DELAY_MAX_RECONNECT) delay = DELAY_MAX_RECONNECT;
3633 strncpy(wanted_server, default_server, MAXHOSTNAME+1);
3634 wanted_port = default_port;
3636 cerr<<"*** Can't contact IRC server ***\n";
3637 cerr<<"*** Next try in "<<delay<<" s\n";
3641 void got_connection()
3643 time_last_datas = current_time;
3645 FD_SET(socket_irc, &ready);
3646 sprintf(buffer, "USER %s x x :%s\n", user_login, user_name);
3648 sprintf(buffer, "NICK %s\n", wanted_nick);
3650 delay = socket_delay;
3653 void got_datas_from_server()
3656 char *prefix, *r, *t;
3658 char *slice_cmd[NB_SLICES_MAX], *slice_ctcp[NB_SLICES_MAX];
3659 char *dest_cmd, *dest_ctcp;
3662 char buffer_cmd[BUFFER_SIZE], buffer_ctcp[BUFFER_SIZE];
3664 time_last_datas = current_time;
3666 // If the buffer is already full, purge it
3667 if(endsrc >= buffer+BUFFER_SIZE)
3670 cerr<<"*** Buffer full, erase it ***\n";
3675 s = read(socket_irc, endsrc, buffer+BUFFER_SIZE-1-endsrc);
3682 cerr<<"KILLING CONNECTION : Read error\n";
3693 dest_cmd = buffer_cmd;
3694 dest_ctcp = buffer_ctcp;
3696 if(slice_buffer(src, endsrc,
3698 dest_cmd, slice_cmd, n_cmd,
3699 dest_ctcp, slice_ctcp, n_ctcp))
3700 // No more \r in the buffer
3704 while(r<endsrc) *t++ = *r++;
3710 // Oki, we got a full line
3711 get_command(prefix, slice_cmd, n_cmd, slice_ctcp, n_ctcp);
3719 NodeList<DelayModeChange *> *md;
3721 if(anti_flood && (current_time > anti_flood_off_until)) check_flood();
3722 check_delay_mode_change();
3723 send_mode(current_channel);
3725 delay = socket_delay;
3726 NodeList<Welcome *> *nd;
3727 for(nd = shit_list.first; nd != NULL; nd = nd->next)
3728 if(delay > nd->body->time_max-current_time)
3729 delay = nd->body->time_max-current_time;
3731 for(md = mode_change_list.first; md != NULL; md = md->next)
3732 if(delay > md->body->time-current_time)
3733 delay = md->body->time-current_time;
3735 if(delay < 0) delay = 0;
3740 NodeList<DCCChat *> *node, *pred, *next, *node2;
3741 char CHAT_buffer[BUFFER_SIZE];
3744 pred = NULL; next = NULL;
3745 node = dcc_chat_list.first;
3749 if(FD_ISSET(node->body->socket, &result))
3751 s = read(node->body->socket, CHAT_buffer, BUFFER_SIZE-1);
3754 FD_CLR(node->body->socket, &ready); // Forget the socket
3755 close(node->body->socket);
3757 sprintf(IRC_buffer, "%s leaves.\n", node->body->prefix);
3758 for(node2=dcc_chat_list.first; node2!= NULL; node2 = node2->next)
3759 if(node2->body != node->body)
3760 tell(node2->body, NULL, IRC_buffer);
3762 remove_wait_for_chat(node->body);
3764 if(pred == NULL) dcc_chat_list.first = next;
3765 else pred->next = next;
3771 *(CHAT_buffer+s-1) = '\0';
3772 tropbot_cmd(node->body, node->body->prefix, NULL, CHAT_buffer);
3782 int main(int argc, char **argv)
3785 // I think it's a good idea to have few friends forever (me ? yeahhh !)
3786 level_list.Insert(new Person("*!*fleuret@*.inria.fr", LEVEL_MASTER, NULL));
3787 level_list.Insert(new Person("*!*fleuret@*.ens.fr", LEVEL_MASTER, NULL));
3788 level_list.Insert(new Person("*!*fleuret@*.curie.fr", LEVEL_MASTER, NULL));
3789 level_list.Insert(new Person("*!*jolibot@*.inria.fr", LEVEL_DEFENCE, NULL));
3790 level_list.Insert(new Person("*!*jolibot@*.ens.fr", LEVEL_DEFENCE, NULL));
3791 level_list.Insert(new Person("*!*jolibot@*.curie.fr", LEVEL_DEFENCE, NULL));
3793 #ifdef SCREEN_OUTPUT
3794 cout<<"TropBot, written by Francois Fleuret,"
3795 " contact <francois.fleuret@inria.fr>\n";
3798 get_options(argc, argv);
3800 uncap(home_channel);
3801 strncpy(wanted_channel, home_channel, SMALL_BUFFER_SIZE);
3802 strncpy(wanted_nick, original_nick, SMALL_BUFFER_SIZE);
3803 strncpy(wanted_server, default_server, MAXHOSTNAME+1);
3804 wanted_port = default_port;
3806 IRC_registred = 0; // true if we are registred
3807 IRC_connected = 0; // true if we have a connection with an IRC server
3808 global_state = STATE_WAIT;
3810 mode_protect_on = DEFAULT_MODE_PROTECT;
3819 last_answer_time = 0;
3820 anti_flood_off_until = 0;
3822 // The bot does NOTHING at first
3830 delay = socket_delay;
3832 struct sigaction action;
3833 memset(&action, 0, sizeof(action));
3834 action.sa_handler = SIG_IGN;
3836 // We'll ignore the SIGPIPE signal, which will be catch somewhere else
3837 sigaction(SIGPIPE, &action, NULL);
3843 delay_pause.tv_sec = delay;
3844 delay_pause.tv_usec = 0;
3847 while(waitpid(-1, NULL, WNOHANG) > 0) nb_sons--;
3849 select(64, &result, NULL, NULL, &delay_pause);
3851 time(¤t_time);
3857 #ifdef SCREEN_OUTPUT
3858 cout<<"No connection yet\n";
3859 cout<<"Try to contact "<<wanted_server<<":"<<wanted_port<<"\n";
3861 socket_irc = call_socket(wanted_server, wanted_port);
3863 if(socket_irc <= 0) try_reconnect();
3864 else got_connection();
3868 if((current_time > time_killed+DELAY_NICK_BACK) && was_killed)
3871 strncpy(wanted_nick, original_nick, SMALL_BUFFER_SIZE);
3872 sprintf(buffer, "NICK %s\n", wanted_nick);
3876 if(IRC_registred) clean_shit_list();
3878 if(FD_ISSET(socket_irc, &result)) got_datas_from_server();
3881 if(current_time > time_last_datas+DELAY_DEAD_SERVER)
3884 cerr<<"KILLING CONNECTION : Quiet server\n";
3889 else if(!in_channel) if(global_state == STATE_WAIT)
3891 sprintf(IRC_buffer, "JOIN %s\n", wanted_channel);
3892 write_irc(IRC_buffer);
3896 if(IRC_connected) check_stuffs();
3897 already_kicked.Clear();
3900 #ifdef SCREEN_OUTPUT
3901 cout<<present_people.Lenght()<<" person(s) inside\n";
3902 // banid_list.Print();
3906 while(alive || (socket_irc > 0));
3910 //-----------------------------------------------------------------------------