#include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include #ifdef USING_STD_STRING using std::string; #endif #ifdef DMALLOC #include #endif #ifdef SOCKS #include #endif #include "defines.h" #include "serverdcc.h" #include "window.h" #include "echo.h" #include "tcltk.h" #include "quirc.h" #include "format.h" #include "messages.h" int tserver::script(char *name, char *format, ...) { // Format: // d corresponds to an integer. // q corresponds to a char* which will be escaped. // s corresponds to a char* which will not be escaped // p corresponds to a char**, int which is an argument list that will be // assembled into a list of arguments, each argument being escaped // properly along the way. static int scriptcount=0; int scriptcountnow; va_list ap; char *p; char *charp; char **charpp; int num; char tiny[3]={0}; string stuff; string assemble; int n,i; char tempnum[21]; assemble=""; va_start(ap,format); for(p=format;*p;p++) { switch(*p) { case 'q': charp=va_arg(ap,char*); stuff="\""; for(n=0;n<(signed)strlen(charp);n++) { // $, ", \, [, {, and } switch (charp[n]) { case '{': stuff+="\\{"; break; case '}': stuff+="\\}"; break; case '\\': stuff+="\\\\"; break; case '$': stuff+="\\$"; break; case '"': stuff+="\\\""; break; case '[': stuff+="\\["; break; default: tiny[0]=charp[n]; tiny[1]=0; stuff+=tiny; } } stuff+="\""; assemble+=stuff; break; case 's': charp=va_arg(ap,char*); assemble+=charp; break; case 'd': num=va_arg(ap,int); sprintf(tempnum,"%d",num); assemble+=tempnum; break; case 'p': charpp=va_arg(ap,char**); num=va_arg(ap,int); stuff=""; for(i=0;ititle="QuIRC - "; windowp->title+=ip; TT_EvalF(TT_ARGS,"renamewindow %s %s",pathname,ip); // FIXME - What is this for? TT_EvalF(TT_ARGS,"set ::%d::auto_reconnecting 0",index); TT_EvalF(TT_ARGS,"::template::condis %d Connect connect",index); if(TT_Int(TT_ARGS,"set ::dynamic::auto_connect")) do_connect(); } void tserver::usedata() { char *nothingness=""; char *prefix; char *command; char *params[IRCMAXLEN/2]={0}; char **displayparams; int numparams=0; char thisline[IRCMAXLEN]; char assemble[IRCMAXLEN]; char temp[TEMPLEN]; char *position; char *splitpos; char *p; int parsed, complete, otherevents; int modetype=0; int currentparam; int n,i; tchan chan; tchan *chanp; while((position=strstr(buffer,"\xA"))) { bzero(thisline,IRCMAXLEN); command=nothingness; prefix=nothingness; strncpy(thisline,buffer,position-buffer); if(strlen(thisline)) if(thisline[strlen(thisline)-1]=='\xD') thisline[strlen(thisline)-1]=0; strcpy(buffer,position+1); strcpy(temp,thisline); complete=script("event_raw_unparsed","q",thisline); parsed=(complete&2)/2; otherevents=(complete&4)/4; parse(thisline,&prefix,&command,params,&numparams); /* Set our nick for use in the various events. */ if((command[0]>='0')&&(command[0]<='9')&& (command[1]>='0')&&(command[0]<='9')&& (command[2]>='0')&&(command[0]<='9')&& (command[3]==0)) { /* End of SILENCE reply */ if(strcmp(command,"272") && /* Offline WATCH Message */ strcmp(command,"601")) { setmynick(params[0]); } } complete=script("event_raw","q q p",prefix,command,params,numparams); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); /* Maybe produce a series of events based on the parsed setting. Like it would call event_bleh if it was parsed or not and event_unparsed_bleh only if it was unparsed by that point. Or maybe redefine the event system so each event gets a set of flags passed in. Another possibility is to check for recursive events, and if not too many then just set the parsed variable before entering the proc. Calling update within the event could cause nested events. Could just set the variable and tell the user if they want to use it they should set a local variable immediately. */ /* The *** compare is a kludge for a broken Undernet server. */ if(!otherevents && strncmp(command,"***",3)) { sprintf(assemble,"event_%s",command); for(p=assemble;*p;p++) *p=tolower(*p); complete=script(assemble,"q p",prefix,params,numparams); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); } /* Get any ial info out of the prefix */ ial_prefix(prefix); /*** COMMANDS START HERE ***/ /* The commands are organized as follows: The named commands all come first and are followed by the numbered commands. They are in the appropriate alphabetical or numerical order. The way the command parsing works is that if a command is known, it will be handled in a "default" manner, otherwise the data will just be passed to the scripting system to handle if it wishes to. The default handling can be partially or fully overriden. Or have events like event_join_parse which pass in the parse variable and then all the regular arguments. script() could accept another argument called parsed and could implement that automagically. */ if(!strcasecmp(command,"JOIN")) { strcpy(temp,prefix); strtok(temp,"!"); if(!strcasecmp(temp,mynick)) { createchannel(params[0]); } strcpy(chan.name,params[0]); if((chanp=chanlist.find(chan))) { strcpy(temp,prefix); strtok(temp,"!"); if(!strcasecmp(temp,mynick)) { hide.n353=1; hide.n366=1; chanp->ison=1; chanp->clearnicklist(); chanp->clearmodes(); } else { chanp->addnick(temp); } } } if(!strcasecmp(command,"KICK")) { strcpy(chan.name,params[0]); if((chanp=chanlist.find(chan))) { strcpy(temp,params[1]); strtok(temp,"!"); if(!strcasecmp(temp,mynick)) { chanp->ison=0; chanp->clearnicklist(); } else { chanp->delnick(temp); } } } if(!strcasecmp(command,"MODE")) { if(!ischannel(params[0])) { setmynick(params[0]); if(!otherevents) { for(i=0;i<(signed)strlen(params[1]);i++) { switch(params[1][i]) { case '+': modetype=1; break; case '-': modetype=0; break; default: sprintf(assemble,"event_mymode%c%c",modetype?'+':'-',params[1][i]); complete=script(assemble,"q p",prefix,params,numparams); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); break; } } } } strcpy(chan.name,params[0]); if((chanp=chanlist.find(chan))) { currentparam=2; for(i=0;i<(signed)strlen(params[1]);i++) { switch(params[1][i]) { case '+': modetype=1; break; case '-': modetype=0; break; case 'v': if (currentparam>=numparams) { fdisplay("NO_MODE_PARAMETER",index,2,modetype?"+":"-","v"); sprintf(assemble,"MODE %s",chanp->name); senddata(assemble); break; } sprintf(assemble,"event_mode%cv",modetype?'+':'-'); complete=script(assemble,"q q q",prefix,params[0],params[currentparam++]); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); if(modetype==1) chanp->voice(params[currentparam-1]); if(modetype==0) chanp->devoice(params[currentparam-1]); break; case 'b': if (currentparam>=numparams) { fdisplay("NO_MODE_PARAMETER",index,2,modetype?"+":"-","b"); sprintf(assemble,"MODE %s",chanp->name); senddata(assemble); break; } sprintf(assemble,"event_mode%cb",modetype?'+':'-'); complete=script(assemble,"q q q",prefix,params[0],params[currentparam++]); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); break; case 'l': if (modetype&¤tparam>=numparams) { fdisplay("NO_MODE_PARAMETER",index,2,modetype?"+":"-","l"); sprintf(assemble,"MODE %s",chanp->name); senddata(assemble); break; } sprintf(assemble,"event_mode%cl",modetype?'+':'-'); if(modetype) { complete=script(assemble,"q q q",prefix,params[0],params[currentparam++]); } else { complete=script(assemble,"q q",prefix,params[0]); } parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); chanp->setmode('l',modetype); if(modetype==1) chanp->limit=atoi(params[currentparam-1]); if(modetype==0) chanp->limit=0; break; case 'k': if (currentparam>=numparams) { fdisplay("NO_MODE_PARAMETER",index,2,modetype?"+":"-","k"); sprintf(assemble,"MODE %s",chanp->name); senddata(assemble); break; } // Hack for broken IRCd //if(modetype || chanp->getmode('k')) { sprintf(assemble,"event_mode%ck",modetype?'+':'-'); complete=script(assemble,"q q q",prefix,params[0],params[currentparam++]); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); chanp->setmode('k',modetype); if(modetype==1) strcpy(chanp->keyword,params[currentparam-1]); if(modetype==0) chanp->keyword[0]=0; //} break; case 'o': if (currentparam>=numparams) { fdisplay("NO_MODE_PARAMETER",index,2,modetype?"+":"-","o"); sprintf(assemble,"MODE %s",chanp->name); senddata(assemble); break; } sprintf(assemble,"event_mode%co",modetype?'+':'-'); complete=script(assemble,"q q q",prefix,params[0],params[currentparam++]); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); if(modetype==1) chanp->op(params[currentparam-1]); if(modetype==0) chanp->deop(params[currentparam-1]); break; default: sprintf(assemble,"event_mode%c%c",modetype?'+':'-',params[1][i]); complete=script(assemble,"q q",prefix,params[0]); parsed=parsed|((complete&2)/2); otherevents=otherevents|((complete&4)/4); chanp->setmode(params[1][i],modetype); break; } } } } if(!strcasecmp(command,"NICK")) { strcpy(temp,prefix); strtok(temp,"!"); { char *tempnick = mystrdup(temp); char *tempnick2 = mystrdup(params[0]); lc(tempnick); lc(tempnick2); hash_set(&ial,tempnick2,hash_get(&ial,tempnick)); free(tempnick); free(tempnick2); } if(!strcmp(temp,mynick)) { setmynick(params[0]); } tnick nick(temp); chanlist.init_trav(); while((chanp=chanlist.trav())) { if(chanp->nicklist.find(nick)) { chanp->changenick(temp,params[0]); } } tquery query(temp); tquery *queryp; if((queryp=querylist.find(query))) { query.setname(params[0]); if(!querylist.find(query)) { queryp->setname(params[0]); TT_EvalF(TT_ARGS,"renamewindow %q %q",queryp->pathname,params[0]); } } } if(!strcasecmp(command,"NOTICE")) { if(!otherevents) { char *pair[2]; char *loc; int place=0; int ctcps=0; // Stupid kludge because certain servers don't always return nicks for // notices, just the text itself. Would be good to warn user and // have a more generic way of handling it. char *params0; char *params1; if(numparams==1) { params0=""; params1=params[0]; } else { params0=params[0]; params1=params[1]; } strcpy(assemble,""); strcpy(temp,params1); for(n=0;n<(int)strlen(params1);n++) { if(params1[n]==1) ctcps++; } char *argv[ctcps/2]; splitpos=temp; for(n=0;nison=0; chanp->clearnicklist(); } else { chanp->delnick(temp); } } } if(!strcasecmp(command,"PING")) { parsed=1; sprintf(assemble,"PONG :%s",params[0]); // DEBUG //TT_EvalF(TT_ARGS,".raw.text insert end \"Command (server.cc:582): %q\\n\"",assemble); senddata(assemble); } if(!strcasecmp(command,"PRIVMSG")) { if(!otherevents) { char *pair[2]; char *loc; int place=0; int ctcps=0; strcpy(assemble,""); strcpy(temp,params[1]); for(n=0;n<(int)strlen(params[1]);n++) { if(params[1][n]==1) ctcps++; } char *argv[ctcps/2]; splitpos=temp; for(n=0;nnicklist.find(nick)) { chanp->delnick(temp); } } } if(!strcasecmp(command,"TOPIC")) { strcpy(chan.name,params[0]); if((chanp=chanlist.find(chan))) { if(chanp->topic) free(chanp->topic); chanp->topic=mystrdup(params[1]); } } /* Replay to USERHOST */ if(!strcmp(command,"302")) { strcpy(temp,params[1]); while((position=strstr(temp," "))) { *position=0; if((splitpos=strstr(temp,"=+"))) { *splitpos=0; ial_add(temp,splitpos+2); } strcpy(temp,position+1); } if((splitpos=strstr(temp,"=+"))) { *splitpos=0; ial_add(temp,splitpos+2); } } /* Reply to WHOIS (First line) */ if(!strcmp(command,"311")) { setmynick(params[0]); sprintf(assemble,"%s@%s",params[2],params[3]); ial_add(params[1],assemble); } /* Reply to WHOWAS (First line) */ if(!strcmp(command,"314")) { setmynick(params[0]); sprintf(assemble,"%s@%s",params[2],params[3]); ial_add(params[1],assemble); } /* Mode settings for channel */ //:raptor.ab.ca.dal.net 324 Hynato_C #asdfkjhaf +tnlk 45 346 if(!strcasecmp(command,"324")) { setmynick(params[0]); strcpy(chan.name,params[1]); if((chanp=chanlist.find(chan))&&chanp->ison) { chanp->clearmodes(); currentparam=3; for(i=0;i<(signed)strlen(params[2]);i++) { switch(params[2][i]) { case '+': modetype=1; break; case '-': modetype=0; break; case 'l': if (currentparam>=numparams) { fdisplay("NO_324_MODE_PARAMETER",index,2,modetype?"+":"-","l"); break; } chanp->setmode(params[2][i],modetype); if(modetype==1) chanp->limit=atoi(params[currentparam++]); if(modetype==0) chanp->limit=0; break; case 'k': if (currentparam>=numparams) { fdisplay("NO_324_MODE_PARAMETER",index,2,modetype?"+":"-","k"); break; } chanp->setmode(params[2][i],modetype); if(modetype==1) strcpy(chanp->keyword,params[currentparam++]); if(modetype==0) { chanp->keyword[0]=0; } break; default: chanp->setmode(params[2][i],modetype); break; } } } } /* Reply to TOPIC check */ if(!strcmp(command,"332")) { setmynick(params[0]); strcpy(chan.name,params[1]); if((chanp=chanlist.find(chan))) { if(chanp->topic) free(chanp->topic); chanp->topic=mystrdup(params[2]); } } /* Replay to WHO */ if(!strcmp(command,"352")) { sprintf(assemble,"%s@%s",params[2],params[3]); ial_add(params[5],assemble); } /* Reply to NAMES */ if(!strcmp(command,"353")) { setmynick(params[0]); if(hide.n353) { parsed=1; if(!strcmp(params[1],"=")) { strcpy(chan.name,params[2]); if((chanp=chanlist.find(chan))) { chanp->addnicks(params[3]); } } /* Channel is set +s */ if(!strcmp(params[1],"@")) { strcpy(chan.name,params[2]); if((chanp=chanlist.find(chan))) { chanp->addnicks(params[3]); } } /* Channel is set +p */ if(!strcmp(params[1],"*")) { strcpy(chan.name,params[2]); if((chanp=chanlist.find(chan))) { chanp->addnicks(params[3]); } } } } /* End of NAMES Reply */ if(!strcmp(command,"366")) { setmynick(params[0]); if(hide.n366) { parsed=1; strcpy(chan.name,params[1]); if((chanp=chanlist.find(chan))) { chanp->updatenicklist(); } hide.n353=0; hide.n366=0; } } if(!parsed) { sprintf(assemble,"IRC_%s",command); for(n=0;assemble[n];n++) assemble[n]=toupper(assemble[n]); if(fexists(assemble)) { if(!(displayparams=(char **)malloc((numparams+1)*sizeof(char*)))) { fprintf(stderr,M_OUT_OF_MEMORY); exit(1); } displayparams[0]=prefix; for(n=0;n='0')&&(command[0]<='9')&& (command[1]>='0')&&(command[0]<='9')&& (command[2]>='0')&&(command[0]<='9')&& (command[3]==0)) { // Took out the nick setting from here, moved it up top, before the event // handling. if(!parsed) { complete=script("event_unparsednumeric","q q p",prefix,command,params,numparams); parsed=parsed|((complete&2)/2); } if(!parsed) { if(fexists("UNPARSED_NUMERIC")) { if(!(displayparams=(char **)malloc((numparams+2)*sizeof(char*)))) { fprintf(stderr,M_OUT_OF_MEMORY); exit(1); } displayparams[0]=prefix; displayparams[1]=command; for(n=0;ntopic) free(channelp->topic); channelp->topic=0; channelp->ison=0; } } int tserver::senddata(const char *message) { char temp[IRCMAXLEN]; if(strlen(message)>=(IRCMAXLEN-2)) { echo(" \0030,5 ERROR \003 Message too long, please shorten and resend.",currentwindow,1); return 1; } if(connected) { sprintf(temp,"%s\xD\xA",message); if(send(sockfd,temp,(signed)strlen(temp),0)!=(signed)strlen(temp)) { perror("Sending"); disconnect(0); return 1; } TT_EvalF(TT_ARGS,"if { $::dynamic::do_rawview } \" .raw.text insert end \\\"Command: [escape %q]\\n\\\"\"",message); return 0; } return 1; } int tserver::getflags() { return flags; } int tserver::getsockfd() { return sockfd; } int tserver::do_connect(void) { twindow window; struct sockaddr_in addr; struct hostent *h; char assemble[TEMPLEN]; if(connected) { echo(" \0030,5 ERROR \003 Connecting twice will get you nowhere. Try disconnecting first.",pathname,1); return 1; } if(connecting) { echo(" \0030,5 ERROR \003 We're in the middle of connecting, hold your horses!\n",pathname,1); return 1; } TT_EvalF(TT_ARGS,"::template::condis %d Disconnect disconnect",index); sprintf(assemble," \0030,14 STATUS \003 Looking up %s",ip); echo(assemble,pathname,1); if(!(h=gethostbyname(ip))) { switch(h_errno) { case HOST_NOT_FOUND: echo(" \0030,6 ERROR \003 The specified host is unknown.",pathname,1); TT_EvalF(TT_ARGS,"::template::condis %d Connect connect",index); return 1; case NO_ADDRESS: echo(" \0030,6 ERROR \003 The requested name is valid but does not have an IP address.",pathname,1); TT_EvalF(TT_ARGS,"::template::condis %d Connect connect",index); return 1; case NO_RECOVERY: echo(" \0030,6 ERROR \003 A non-recoverable name server error occured.",pathname,1); TT_EvalF(TT_ARGS,"::template::condis %d Connect connect",index); return 1; case TRY_AGAIN: echo(" \0030,6 ERROR \003 A temporary error occured on an authoritative name server. Try again later.",pathname,1); TT_EvalF(TT_ARGS,"::template::condis %d Connect connect",index); return 1; } } addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(inet_ntoa(*((struct in_addr *)h->h_addr))); //inet_addr(ip); bzero((char *)&(addr.sin_zero), 8); sockfd = socket(AF_INET, SOCK_STREAM, 0); /* struct linger lin; getsockopt(sockfd,SOL_SOCKET,SO_LINGER,&lin,sizeof(lin)); printf("%d and %d\n",lin.l_onoff,lin.l_linger); lin.l_onoff=1; lin.l_linger=120; setsockopt(sockfd,SOL_SOCKET,SO_LINGER,&lin,sizeof(lin)); getsockopt(sockfd,SOL_SOCKET,SO_LINGER,&lin,sizeof(lin)); printf("%d and %d\n",lin.l_onoff,lin.l_linger); */ if(sockfd==-1) { perror("Getting Socket File Descriptor"); return 1; } fcntl(sockfd, F_SETFL, O_NONBLOCK); setmynick(TT_Str(TT_ARGS,"set ::dynamic::default_nick")); fdisplay("CONNECT_ATTEMPT",index,1,inet_ntoa(*((struct in_addr *)h->h_addr))); connecting=1; if(connect(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr))) { // We got an error. If it's just EINPROGRESS keep going, otherwise error. if(errno!=EINPROGRESS) { perror("Connecting [Immediate]"); disconnect(0); return 1; } flags=TCL_WRITABLE; //printf("Here!\n"); Tcl_CreateFileHandler(sockfd,TCL_WRITABLE,handle_networking,this); } else { handle_connect(); flags=TCL_READABLE; //printf("Now here!\n"); Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this); } return 0; } int tserver::disconnect(int sendquit) { char assemble[IRCMAXLEN+1]; if(sendquit&&connected) { sprintf(assemble,"QUIT :%s",TT_Str(TT_ARGS,"set ::dynamic::default_quit")); senddata(assemble); shutdown(sockfd,1); } if(connected||connecting) if(close(sockfd)) { echo(" \0030,5 ERROR \003 Problem closing socket.",pathname,1); return 1; } Tcl_DeleteFileHandler(sockfd); connected=0; connecting=0; flags=0; script("event_disconnect",""); if((!TT_IntF(TT_ARGS,"info exists ::%d::intentional_disconnect",index) || !TT_IntF(TT_ARGS,"set ::%d::intentional_disconnect",index))&& !sendquit) { if(TT_Int(TT_ARGS,"set ::dynamic::auto_reconnect")) { TT_EvalF(TT_ARGS,"set ::%d::auto_reconnecting 1",index); TT_EvalF(TT_ARGS,"::template::condis %d Disconnect \"after cancel [after 100 {connect %d}]; ::template::condis %d Connect connect\"",index,index,index); } } TT_EvalF(TT_ARGS,"set ::%d::intentional_disconnect 0",index); return 0; }