#include #include #include #include #include #include #include #include #include #include //epoch's libirc. should be included with segfault. //might want to change some of these. #define SERVER "192.168.0.2" #define PORT "6667" #define NICK "SegFault" //override with argv[0] #define LINE_LIMIT line_limit #define LINES_SENT_LIMIT 1 #define LINELEN 400 #define RAWLOG "/home/segfault/files/rawlog" #define LOG "/home/segfault/files/log" #define MAXTAILS 400 //just to have it more than the system default. #define BS 502 // !c uses 56 for its tail. // 56 == 32 + 16 + 8 == 0x38 == 0x20+0x10+0x8 == SPAM | BEGIN | MSG #define TAILO_RAW (0x1) //output gets sent directly to server #define TAILO_EVAL (0x2) //interpret the lines read from the tail as if they were messages to segfault #define TAILO_CLOSE (0x4) //close the file at EOF, default is to leave it open. #define TAILO_MSG (0x8) //output gets sent as a PM to the place the tail was made. #define TAILO_BEGIN (0x10) //start the tail at the beginning of the file instead of the end. #define TAILO_SPAM (0x20) //Spam control is enabled for this stream. #define TAILO_ENDMSG (0x40) //show a message when the tail reaches the end of a chunk #define TAILO_Q_EVAL (TAILO_EVAL|TAILO_CLOSE|TAILO_BEGIN) //0x2+0x4+0x10 = 2+4+16 = 22 #define TAILO_Q_COUT (TAILO_SPAM|TAILO_BEGIN|TAILO_MSG) //0x20+0x10+0x8 = 32+16+8 = 56 int start_time; char *segnick; char *redo; int redirect_to_fd; int line_limit; int debug; timer_t timer; int lines_sent; unsigned long oldtime; struct tail { FILE *fp; char *file; char *to; char *args; char opt; unsigned int inode; int lines; } tailf[MAXTAILS]; struct alias { char *original; char *target; struct alias *prev;// doubly linked list. struct alias *next; } *a_start,*a_end; void message_handler(int fd,char *from,char *nick,char *msg,int redones); void c_untail(int fd,char *from, char *file); void mywrite(int fd,char *b) { if(!b) return; if(fd<0) return; write(fd,b,strlen(b)); lines_sent++; } void ircmode(int fd,char *channel,char *mode,char *nick) { int sz=5+strlen(channel)+1+strlen(mode)+1+strlen(nick)+3;//"MODE ", " ", " ","\r\n\0" char *hrm; if(!(hrm=malloc(sz+1))) { mywrite(fd,"QUIT :malloc error 1! holy shit!\r\n"); return; } snprintf(hrm,sz,"MODE %s %s %s\r\n",channel,mode,nick); write(fd,hrm,strlen(hrm)); free(hrm); } void privmsg(int fd,char *who,char *msg) { int i=0; char *chunk,*hrm; int sz; int cs; if(!who) return; if(!msg) return; for(i=0;i LINES_SENT_LIMIT) {//if it is still the same second, skip this function. return; } else { lines_sent=0; } if(redirect_to_fd != -1) { fd=redirect_to_fd; } for(i=0;i_> tailf[i].lines++; mmerp=0; if(strchr(tmp,'\r')) *strchr(tmp,'\r')=0; if(strchr(tmp,'\n')) *strchr(tmp,'\n')=0; if(tailf[i].opt & TAILO_EVAL) {//eval if(tailf[i].args) { //only format magic evaled file lines if they have args. tmp2=format_magic(fd,tailf[i].to,segnick,tmp,tailf[i].args); } else { tmp2=strdup(tmp); if(!tmp2) { perror("ZOMG malloc error in eval section!!!"); return; } } message_handler(fd,tailf[i].to,segnick,tmp2,0); free(tmp2); } if(tailf[i].opt & TAILO_RAW) {//raw tmp2=malloc(strlen(tmp)+3); snprintf(tmp2,strlen(tmp)+3,"%s\r\n",tmp); mywrite(fd,tmp2); free(tmp2); } if(tailf[i].opt & TAILO_MSG) {//just msg the lines. privmsg(fd,tailf[i].to,tmp); } if(tailf[i].lines >= LINE_LIMIT && (tailf[i].opt & TAILO_SPAM)) { tailf[i].lines=-1; //lock it. privmsg(fd,tailf[i].to,"--more--"); } } else { if(tailf[i].lines != 0 && (tailf[i].opt & TAILO_ENDMSG)) { privmsg(fd,tailf[i].to,"---------- TAILO_ENDMSG border ----------"); } tailf[i].lines=0; } } else { //don't PM in here. shows a LOT of shit. } } } } void file_tail(int fd,char *from,char *file,char *args,char opt) { int i,j; int fdd; char tmp[256]; struct stat st; for(i=0;ikey=key; hashtable[h]->value=value;//strdup this return; } //filled bucket... oh shit. if(!strcmp(hashtable[h]->key,key)) { //same key. just change the value. hashtable[h]->value=value;//free previous and strdup. return; } else { //collision! add to the linked list. for } } #endif ///////////////////////// HASH TABLE SHIT ///////////////////////////////// void debug_time(int fd,char *from,char *msg) { char tmp[100]; // struct itimerspec whattime; if(debug) { // timer_gettime(timer,&whattime); // snprintf(tmp,511,"%d.%d %d.%d", // whattime.it_interval.tv_sec, // whattime.it_value.tv_nsec, // whattime.it_value.tv_sec, // whattime.it_value.tv_nsec); snprintf(tmp,99,"%lu %s",time(0),msg?msg:"(no message)");//time() returns time_t which on BSD is a long. privmsg(fd,from,tmp); } } //CONVERT void c_aliases(int fd,char *from,char *line) { struct alias *m; char tmp[512]; int i=0,j=0; // debug_time(fd,from); if(!line){ privmsg(fd,from,"usage: !aliases [search-term]"); return; } for(m=a_start;m;m=m->next) { if(strcasestr(m->target,line) || strcasestr(m->original,line)) { snprintf(tmp,sizeof(tmp)-1,"%s -> %s",m->original,m->target); privmsg(fd,from,tmp); i++; } j++; } snprintf(tmp,sizeof(tmp)-1,"found %d of %d aliases",i,j); privmsg(fd,from,tmp); // debug_time(fd,from); } //CONVERT void c_rmalias(int fd,char *from,char *line) { struct alias *m; for(m=a_start;m;m=m->next) { if(!strcmp(line,m->original)) { if(m->next == 0) { a_end=m->prev; } if(m->prev == 0) { a_start=m->next; free(m->target); free(m->original); free(m); } else { m->prev->next=m->next; } privmsg(fd,from,"alias deleted"); return; } } privmsg(fd,from,"alias not found"); return; } void c_kill(int fd,char *from,char *line) { char *csig=line; char *cpid=strchr(line,' '); int sig,pid; if(cpid) { *cpid=0; cpid++; } else { privmsg(fd,from,"usage: !kill signum pid"); return; } sig=atoi(csig); pid=atoi(cpid); if(sig && pid) { if(kill(pid,sig)) privmsg(fd,from,"kill error"); else privmsg(fd,from,"signal sent"); } else { privmsg(fd,from,"pid or sig is 0. something is odd."); } } //CONVERT void c_alias(int fd,char *from,char *line) { char *derp=strchr(line,' '); struct alias *tmp,*tmp2; char tmps[256]; if(!derp) { for(tmp=a_start;tmp;tmp=tmp->next) { if(!strcmp(line,tmp->original)) { privmsg(fd,from,tmp->target); return; } } privmsg(fd,from,"not an alias."); return; } if(!line) return; *derp=0; for(tmp=a_start;tmp;tmp=tmp->next) { if(!strcmp(line,tmp->original)) { snprintf(tmps,sizeof(tmps)-1,"duplicate alias: %s",line); privmsg(fd,from,tmps); return; } } tmp=a_end; tmp2=malloc(sizeof(struct alias)+20); if(!tmp2) { mywrite(fd,"QUIT :malloc error 7!!!\r\n"); return; } if(a_end == 0) { a_end=tmp2; a_start=tmp2; a_end->prev=0; } else { a_end->next=tmp2; a_end=tmp2; a_end->prev=tmp; } a_end->target=strdup(derp+1); a_end->original=strdup(line); a_end->next=0; } void c_id(int fd,char *from) { char tmp[512]; snprintf(tmp,sizeof(tmp)-1,"u:%d g:%d eu:%d eg:%d",getuid(),getgid(),geteuid(),getegid()); privmsg(fd,from,tmp); } void c_leetuntail(int fd,char *from,char *line) { char *frm=line; char *file=0; int frmN=0; int i; char tmp[512]; if((file=strchr(line,' '))) { *file=0; file++; } if(file) { if(*frm == '*') { for(i=0;i IRCdestination"); for(i=0;i %s",tailf[i].file,tailf[i].inode,ftell(tailf[i].fp),tailf[i].lines,tailmode_to_txt(tailf[i].opt),tailf[i].opt,tailf[i].to); privmsg(fd,from,tmp); free(tmp); } } } char recording,recording_raw; void c_record(int fd,char *from,char *line) { if(*line == '0') { privmsg(fd,from,"no longer recording IRC."); recording=0; return; } if(*line == '1') { recording=1; unlink(LOG); privmsg(fd,from,"recording IRC."); return; } privmsg(fd,from,recording?"1":"0"); } void c_rawrecord(int fd,char *from,char *line) { if(*line == '0') { privmsg(fd,from,"no longer recording raw IRC."); recording_raw=0; return; } if(*line == '1') { recording_raw=1; unlink(RAWLOG); privmsg(fd,from,"recording raw IRC."); return; } privmsg(fd,from,recording_raw?"1":"0"); } void c_leetsetout(int fd,char *from,char *msg) { if(redirect_to_fd != -1) close(redirect_to_fd); redirect_to_fd=open(msg+3,((msg[0]-'0')*100) + ((msg[1]-'0')*10) + (msg[2]-'0'),022); if(redirect_to_fd == -1) { privmsg(fd,from,"failed to open file as redirect:"); privmsg(fd,from,msg+3); } } void c_linelimit(int fd,char *from,char *msg) { char tmp[256]; //struct itimerspec settime; //settime.it_interval.tv_sec=0; //settime.it_interval.tv_nsec=10; //settime.it_value.tv_nsec=0; //settime.it_value.tv_nsec=10; if(!msg) { snprintf(tmp,255,"current spam line limit: %d (debug: %d)",line_limit,debug); privmsg(fd,from,tmp); } else { if(atoi(msg) > 1) { line_limit=atoi(msg); snprintf(tmp,255,"spam line limit set to: %d",line_limit); privmsg(fd,from,tmp); } else { privmsg(fd,from,"please set the limit to > 1... oi. (btw, debug has been flipped)"); debug^=1; // if(debug) { // if(timer_create(CLOCK_REALTIME,SIGEV_NONE,&timer) == -1) { // privmsg(fd,from,(debug=0,"error making debug timer. shit.")); // } // if(timer_settime(timer,0,&settime,&settime) == -1) { // privmsg(fd,from,(debug=0,"error setting debug timer. shit.")); // } // } // else if(timer_delete(timer) == -1) { // privmsg(fd,from,"error deleting timer. shit."); // } } } } void c_resetout(int fd,char *from) { redirect_to_fd=-1; privmsg(fd,from,"output reset"); } void message_handler(int fd,char *from,char *nick,char *msg,int redones) { struct alias *m; char *tmp2; char tmp[512]; int sz; //debug_time(fd,from); if(redirect_to_fd != -1) { fd=redirect_to_fd; } if(recording) { debug_time(fd,from,"writing to log..."); append_file(fd,"raw",LOG,msg,'\n'); debug_time(fd,from,"finished writing to log."); } if(*msg != '!') { return; } //this section could probably be made a LOT shorter with //an array of structs that contain command and function pointer. //... meh. it'd just be a LITTLE bit shorter. // this still seems horrible though. all those constants in there that are just strlen()s... if(!strncmp(msg,"!leetsetout ",12)) { c_leetsetout(fd,from,msg+12); } else if(!strncmp(msg,"!whoareyou",10) && !msg[10]) { privmsg(fd,from,segnick); } else if(!strncmp(msg,"!whoami",7) && !msg[7]) { privmsg(fd,from,nick); } else if(!strncmp(msg,"!whereami",9) && !msg[9]) { privmsg(fd,from,from); } else if(!strncmp(msg,"!resetout",9) && !msg[9]) { c_resetout(fd,from); } else if(!strncmp(msg,"!botup",6) && !msg[6]) { c_botup(fd,from); } else if(!strncmp(msg,"!linelimit",10) && (!msg[10] || msg[10] == ' ')) { c_linelimit(fd,from,*(msg+10)?msg+11:0); } else if(!strncmp(msg,"!tailunlock ",12)) { c_tailunlock(fd,from,msg+12); } else if(!strncmp(msg,"!changetail ",12)) { c_changetail(fd,from,msg+12); } else if(!strncmp(msg,"!tails",6) && !msg[6]) { c_tails(fd,from); } else if(!strncmp(msg,"!record ",8)) { c_record(fd,from,msg+8); } else if(!strncmp(msg,"!rawrecord ",11)) { c_rawrecord(fd,from,msg+11); } else if(!strncmp(msg,"!leettail ",10)) { c_leettail(fd,from,msg+10); } else if(!strncmp(msg,"!leetuntail ",12)) { c_leetuntail(fd,from,msg+12); } else if(!strncmp(msg,"!leetappend ",12)) { c_leetappend(fd,from,msg+12); } else if(!strncmp(msg,"!untail ",8)) { c_untail(fd,from,msg+8); } else if(!strncmp(msg,"!raw ",5)) { tmp2=malloc(strlen(msg)-5+3); snprintf(tmp2,strlen(msg)-5+2,"%s\r\n",msg+5); mywrite(fd,tmp2); free(tmp2); //write(fd,msg+5,strlen(msg)-5); //write(fd,"\r\n",2); } else if(!strncmp(msg,"!say ",5)) { privmsg(fd,from,msg+5); } else if(!strncmp(msg,"!id",3) && !msg[3]) { c_id(fd,from); } else if(!strncmp(msg,"!kill ",6)) { c_kill(fd,from,msg+6); } else if(!strncmp(msg,"!alias ",7)) { c_alias(fd,from,msg+7); } else if(!strncmp(msg,"!rmalias ",9)) { c_rmalias(fd,from,msg+9); } else if(!strncmp(msg,"!aliases",8) && (!msg[8] || msg[8] == ' ')) { c_aliases(fd,from,*(msg+8)?msg+9:0); } else if(redones < 5) { debug_time(fd,from,"checking aliases..."); //CONVERT for(m=a_start;m;m=m->next) {//optimize this. hash table? if(!strncmp(msg,m->original,strlen(m->original)) && (msg[strlen(m->original)]==' ' || msg[strlen(m->original)] == 0)) {//this allows !c to get called when using !c2 if !c2 is defined after !c. >_> sz=(strlen(msg)-strlen(m->original)+strlen(m->target)+1); //redo=malloc(sz); //if(!redo) (void *)mywrite(fd,"QUIT :malloc error 9!!!\r\n"); //this is where the format string is used... //generate an array based on the format string containing %N stuff in the right order. // %u = user, %f = from (user/channel), %s = argument // handling it here would be a bitch. maybe // redo=apply_alias(fd,from,sz,m->target) ??? new function would probably be good. redo=format_magic(fd,from,nick,m->target,*(msg+strlen(m->original)+1)=='\n'?"":(msg+strlen(m->original)+1)); //snprintf(redo,sz,m->target,*(msg+strlen(m->original)+1)=='\n'?"":(msg+strlen(m->original)+1) ); message_handler(fd,from,nick,redo,redones+1); free(redo); redo=0; return; } } debug_time(fd,from,"finished checking aliases. not found."); //privmsg(fd,from,msg); // '!c ' + (msg - '!'); // sz=(strlen(msg)+4); // redo=malloc(sz); // if(!redo) (void *)mywrite(fd,"QUIT :malloc error 10!!!\r\n"); // snprintf(redo,sz-1,"!c %s",msg+1); // privmsg(fd,from,redo); // message_handler(fd,from,nick,redo,redones+1); // free(redo); redo=0; snprintf(tmp,sizeof(tmp),"unknown command: %s",msg); privmsg(fd,from,tmp); // privmsg(fd,from,"I don't know what you're talking about, man."); } if(redones >=5) { privmsg(fd,from,"too much recursion."); } //debug_time(fd,from); } void line_handler(int fd,char *line) {//this should be built into the libary? //static int flag=0; //char *a=":hacking.allowed.org 366 "; char *s=line; char *nick=0,*name=0,*host=0; char *t=0,*u=0; if(strchr(line,'\r')) *strchr(line,'\r')=0; if(strchr(line,'\n')) *strchr(line,'\n')=0; //:nick!name@host MERP DERP :message //strchr doesn't like null pointers. :/ why not just take them and return null? //check that I haven't gone past the end of the string? nah. it should take care of itself. if(recording_raw) { append_file(fd,"epoch",RAWLOG,line,'\n'); } if((nick=strchr(line,':'))) { *nick=0; nick++; if((name=strchr(nick,'!'))) { *name=0; name++; if((host=strchr(name,'@'))) { *host=0; host++; if((s=strchr(host,' '))) { *s=0; s++; if((t=strchr(s,' '))) { *t=0; t++; if((u=strchr(t,' '))) {//: *u=0; u++; } } } } } } //printf("<%s!%s@%s> '%s' '%s' '%s'\n",nick,name,host,s,t,u); //if(to_file!=0) { // fd= //} if(s && t && u) { if(!strcmp(s,"PRIVMSG")) { u++; if(*t == '#')//channel. message_handler(fd,t,nick,u,0); else message_handler(fd,nick,nick,u,0); } } if(s && nick && t) { if(!strcmp(s,"JOIN")) { ircmode(fd,t+1,"+v",nick);//why t+1? it starts with :? } if(!strcmp(s,"MODE")) { if(u) { if(*u == '-') {//auto-give modes back that are removed in front of segfault. *u='+'; ircmode(fd,t,u,"");//u contains the nick the mode is being removed from. } } } //:Ishikawa-!~epoch@localhost NICK :checking if(!strcmp(s,"NICK")) { if(!strcmp(nick,segnick)) { free(segnick); segnick=strdup(t+1); } } } } int main(int argc,char *argv[]) { int fd; int c; redirect_to_fd=-1; debug=0; lines_sent=0; line_limit=25; recording=0; recording_raw=0; start_time=time(0); a_start=0; a_end=0; redo=0; segnick=strdup(NICK); printf("starting segfault...\n"); for(c=0;c1?argv[1]:"SegFault","segfault segfault segfault :segfault"); startup_stuff(fd); return runit(fd,line_handler,extra_handler); }