#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/file.h>
#include <time.h>
#include <ctype.h>
#include <netdb.h>
#include <stdarg.h>
#include "config.h"
#include <string>
#ifdef USING_STD_STRING
using std::string;
#endif
#ifdef DMALLOC
#include <dmalloc.h>
#endif
#ifdef SOCKS
#include <socks.h>
#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;i<num;i++) {
if(i) {
stuff+=" ";
}
stuff+="\"";
for(n=0;n<(signed)strlen(charpp[i]);n++) {
// $, ", \, [, {, and }
switch (charpp[i][n]) {
case '{':
stuff+="\\{";
break;
case '}':
stuff+="\\}";
break;
case '\\':
stuff+="\\\\";
break;
case '$':
stuff+="\\$";
break;
case '"':
stuff+="\\\"";
break;
case '[':
stuff+="\\[";
break;
default:
tiny[0]=charpp[i][n];
tiny[1]=0;
stuff+=tiny;
}
}
stuff+="\"";
}
assemble+=stuff;
break;
default:
tiny[0]=*p;
tiny[1]=0;
assemble+=tiny;
}
}
va_end(ap);
scriptcount=(++scriptcount)%100;
scriptcountnow=scriptcount;
TT_EvalF(TT_ARGS,"\
set ::internal::complete%d 0\n\
foreach script $::%d::scripts {\n\
if { [info commands ::%d::${script}::%s]!=\"\" } {\n\
set ::internal::tempcomplete%d [::%d::${script}::%s %s]\n\
if { [set ::internal::tempcomplete%d]==\"\" } { set ::internal::tempcomplete%d 0 }\n\
set ::internal::complete%d [expr [set ::internal::complete%d]|[set ::internal::tempcomplete%d]]\n\
if { [expr [set ::internal::tempcomplete%d]&1] } { break }\n\
}\n\
}\n\
",scriptcountnow,index,index,name,scriptcountnow,index,name,assemble.c_str(),scriptcountnow,scriptcountnow,scriptcountnow,scriptcountnow,scriptcountnow,scriptcountnow);
return TT_IntF(TT_ARGS,"set ::internal::complete%d",scriptcountnow);
}
void tserver::ial_prefix(char *prefix) {
char *position;
if((position=strstr(prefix,"!"))) {
*position=0;
ial_add(prefix,position+1);
*position='!';
}
}
void tserver::ial_add(char *nick, char *userhost) {
char *tempnick = mystrdup(nick);
lc(tempnick);
hash_set(&ial,tempnick,userhost);
free(tempnick);
}
void tserver::perror(char *message) {
int number=errno;
char temp[1000];
sprintf(temp," \0030,5 ERROR \003 %s: %s",message,strerror(number));
echo(temp,pathname,1);
}
void tserver::perror(char *message, int number) {
char temp[1000];
// DEBUG
//printf("Message is %x, Number is %d, strerror(number) is %x\n",message,number,strerror(number));
sprintf(temp," \0030,5 ERROR \003 %s: %s",message,strerror(number));
echo(temp,pathname,1);
}
tserver::~tserver() {
free(mynick);
hash_destroy(&ial);
if(name) free(name);
if(desc) free(desc);
Tcl_DeleteFileHandler(sockfd);
}
void tserver::createchannel(const char *channelname) {
twindow window;
int n;
tchan chan(channelname);
if(!chanlist.find(chan)) {
strcpy(window.name,channelname);
sprintf(window.pathname,".channel%d",channum);
window.title="QuIRC - ";
window.title+=channelname;
window.server=this;
n=windows.insert_ascending(window);
strcpy(chan.pathname,window.pathname);
TT_EvalF(TT_ARGS,"Init_Window .channel%d %q %d channel %d",channum,channelname,n,index);
channum++;
chanlist.insert_end(chan);
TT_EvalF(TT_ARGS,"if { $::dynamic::auto_raise_channels } { totop %d }",n);
TT_Eval(TT_ARGS,"if { !$::dynamic::auto_raise_channels } { raise [currentwindow] }");
}
}
void tserver::createquery(const char *queryname) {
twindow window;
int n;
tquery query(queryname);
if(!querylist.find(query)) {
strcpy(window.name,queryname);
sprintf(window.pathname,".query%d",querynum);
window.server=this;
n=windows.insert_ascending(window);
strcpy(query.pathname,window.pathname);
TT_EvalF(TT_ARGS,"Init_Window .query%d %q %d query %d",querynum,queryname,n,index);
querynum++;
querylist.insert_end(query);
TT_EvalF(TT_ARGS,"if { $::dynamic::auto_raise_queries } { totop %d }",n);
TT_Eval(TT_ARGS,"if { !$::dynamic::auto_raise_queries } { raise [currentwindow] }");
}
}
tserver::tserver(const char *address, int theport, const char *password) {
twindow window;
int n;
mynick = mystrdup("");
if(!strlen(address)) {
strcpy(ip,TT_Str(TT_ARGS,"set ::dynamic::default_server"));
} else {
strcpy(ip,address);
}
name=0;
desc=0;
sockfd=0;
connected=0;
connecting=0;
// Set up hash table for IAL
hash_create(&ial);
// Set up the window for the window list
sprintf(pathname,".status%d",statusnum);
strcpy(window.pathname,pathname);
index=statusnum;
window.server=this;
// FIXME - I don't think we need these two lines.
//window.title="QuIRC - ";
//window.title+=ip;
n=windows.insert_ascending(window);
TT_EvalF(TT_ARGS,"Init_Window .status%d %q %d status %d",index,ip,n,index);
statusnum++;
// Set up the server namespace and scripts
TT_EvalF(TT_ARGS,"namespace eval ::%d {}",index);
TT_EvalF(TT_ARGS,"namespace eval ::%d::internal { proc index { args } { callproc ::template::index $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { set scripts {} }",index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc condis { args } { callproc ::template::condis $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc connect { args } { callproc connect $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc disconnect { args } { callproc disconnect $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc index { args } { callproc ::template::index $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc popup { args } { callproc ::template::popup $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc fdisplay { args } { callproc ::template::fdisplay $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc fgetpathnames { args } { callproc ::template::fgetpathnames $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { proc fparse { args } { callproc ::template::fparse $args %d } }",index,index);
TT_EvalF(TT_ARGS,"namespace eval ::%d { namespace export -clear * }",index);
TT_EvalF(TT_ARGS,"set ::%d::ctcp_cloak $::dynamic::default_ctcp_cloak",index);
TT_EvalF(TT_ARGS,"foreach script $::dynamic::default_scripts { ::template::script %d $script }",index);
// Hack to set current window before server starts going.
strcpy(currentwindow,pathname);
// Perform the actual server connection
server(address,theport,password);
TT_EvalF(TT_ARGS,"totop %d",n);
}
void tserver::server(const char *address, int theport, const char *password) {
twindow window;
twindow *windowp;
// Destroy old server data
//hash_destroy(&ial);
if(name) free(name);
if(desc) free(desc);
if(sockfd) {
disconnect(1);
Tcl_DeleteFileHandler(sockfd);
}
if(!strlen(address)) {
strcpy(ip,TT_Str(TT_ARGS,"set ::dynamic::default_server"));
} else {
strcpy(ip,address);
}
if(!theport) {
port=TT_Int(TT_ARGS,"set ::dynamic::default_port");
} else {
port=theport;
}
sockfd=0;
connected=0;
connecting=0;
localip[0]=0;
userlist_nocase=1;
// Set up hash table for IAL
//hash_create(&ial);
// Do rest of stuff
setmynick(TT_Str(TT_ARGS,"set ::dynamic::default_nick"));
name=mystrdup(TT_Str(TT_ARGS,"set ::dynamic::default_user"));
desc=mystrdup(TT_Str(TT_ARGS,"set ::dynamic::default_desc"));
pass=password;
// Get old window entry for this particular server
strcpy(window.pathname,pathname);
windowp=windows.find(window);
// Set the title then rename the windowlist entry
windowp->title="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;n<ctcps-ctcps%2;n++) {
p=::index(splitpos,1);
*p=0;
if(n%2) {
loc=strstr(splitpos," ");
pair[0]=splitpos;
if(!loc) pair[1]=""; else {
pair[1]=loc+1;
*loc=0;
}
for(i=0;i<(int)strlen(pair[0]);i++) pair[0][i]=toupper(pair[0][i]);
if(!(argv[place++]=Tcl_Merge(2,pair))) {
for(n=0;n<place-1;n++) {
Tcl_Free(p);
}
fprintf(stderr,"Error in Tcl_Merge\n");
exit(1);
}
} else {
strcat(assemble,splitpos);
}
splitpos=p+1;
}
strcat(assemble,splitpos);
if(strlen(assemble)) {
complete=script("event_noticetext","q q q",prefix,params0,assemble);
parsed=parsed|((complete&2)/2);
otherevents=otherevents|((complete&4)/4);
}
if(place) {
p=Tcl_Merge(place,argv);
if(!p) {
for(n=0;n<place;n++) {
Tcl_Free(p);
}
fprintf(stderr,"Error in Tcl_Merge\n");
exit(1);
}
complete=script("event_ctcpreply","q q q",prefix,params0,p);
parsed=parsed|((complete&2)/2);
otherevents=otherevents|((complete&4)/4);
Tcl_Free(p);
}
}
}
if(!strcasecmp(command,"PART")) {
strcpy(chan.name,params[0]);
if((chanp=chanlist.find(chan))) {
strcpy(temp,prefix);
strtok(temp,"!");
if(!strcasecmp(temp,mynick)) {
chanp->ison=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;n<ctcps-ctcps%2;n++) {
p=::index(splitpos,1);
*p=0;
if(n%2) {
loc=strstr(splitpos," ");
pair[0]=splitpos;
if(!loc) pair[1]=""; else {
pair[1]=loc+1;
*loc=0;
}
for(i=0;i<(int)strlen(pair[0]);i++) pair[0][i]=toupper(pair[0][i]);
if(!(argv[place++]=Tcl_Merge(2,pair))) {
for(n=0;n<place-1;n++) {
Tcl_Free(p);
}
fprintf(stderr,"Error in Tcl_Merge\n");
exit(1);
}
} else {
strcat(assemble,splitpos);
}
splitpos=p+1;
}
strcat(assemble,splitpos);
if(strlen(assemble)) {
complete=script("event_text","q q q",prefix,params[0],assemble);
parsed=parsed|((complete&2)/2);
otherevents=otherevents|((complete&4)/4);
}
if(place) {
p=Tcl_Merge(place,argv);
if(!p) {
for(n=0;n<place;n++) {
Tcl_Free(p);
}
fprintf(stderr,"Error in Tcl_Merge\n");
exit(1);
}
complete=script("event_ctcp","q q q",prefix,params[0],p);
parsed=parsed|((complete&2)/2);
otherevents=otherevents|((complete&4)/4);
Tcl_Free(p);
}
}
}
if(!strcasecmp(command,"QUIT")) {
strcpy(temp,prefix);
strtok(temp,"!");
tnick nick(temp);
chanlist.init_trav();
while((chanp=chanlist.trav())) {
if(chanp->nicklist.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<numparams;n++) {
displayparams[n+1]=params[n];
}
fdisplayv(assemble,index,numparams+1,displayparams);
parsed=1;
free(displayparams);
}
}
/* Deal with unprocessed numeric commands */
if((command[0]>='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;n<numparams;n++) {
displayparams[n+2]=params[n];
}
fdisplayv("UNPARSED_NUMERIC",index,numparams+2,displayparams);
parsed=1;
free(displayparams);
}
}
}
/* If it's not a command we know how to handle, just print it in the
status window and assume that the user will know how to how to
deal with it */
if(!parsed) {
complete=script("event_unparsed","q q p",prefix,command,params,numparams);
parsed=parsed|((complete&2)/2);
}
if(!parsed) {
if(fexists("UNPARSED")) {
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;n<numparams;n++) {
displayparams[n+2]=params[n];
}
fdisplayv("UNPARSED",index,numparams+2,displayparams);
parsed=1;
free(displayparams);
}
}
if(!parsed) {
sprintf(assemble,"%s %s ",prefix,command);
for(n=0;n<numparams;n++) {
strcat(assemble,params[n]);
if(n!=numparams-1) strcat(assemble," ");
}
echo(assemble,pathname,1);
}
}
}
/*** Network Handling Functions ***/
// Generic network handling Function for servers. If something happens on a
// socket, this gets called with the socket structure and the flags that
// were activated.
void tserver::handle(int mask) {
socklen_t optlen;
int optval;
int numbytes;
//printf("The handler got called!\n");
//if(networklock) printf("Something odd is going on.\n");
//networklock=1;
//Tcl_DeleteFileHandler(sockfd);
//flags will be zero sometimes. Handler should not be installed.
//Tcl_CreateFileHandler(sockfd,0,handle_networking,this);
if(connecting) {
if(mask & TCL_WRITABLE) {
optlen=sizeof(optval);
// < 0 return from getsockopt may indicate failure as well.
getsockopt(sockfd,SOL_SOCKET,SO_ERROR,(void *)&optval,&optlen);
if(optval) {
perror("Connecting",optval);
disconnect(0);
} else {
handle_connect();
// CHANGE
flags=TCL_READABLE;
Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
}
}
} else if(connected) {
if(mask & TCL_READABLE) {
if((numbytes=recv(sockfd,temp,TEMPLEN-1,0))==-1) {
perror("Receiving");
disconnect(0);
} else {
if(numbytes) {
temp[numbytes]=0;
if((strlen(buffer)+strlen(temp))<BUFLEN) {
strcat(buffer,temp);
usedata();
} else {
echo("Buffer Overrun in SERVER (Please contact author at quirc@patearl.net)",".main",1);
}
} else {
disconnect(0);
}
}
}
}
//if(flags) Tcl_CreateFileHandler(sockfd,flags,handle_networking,this);
//networklock=0;
}
// Generic portion of connecting
void tserver::handle_connect(void) {
tchan *channelp;
struct sockaddr_in addr;
socklen_t addrlength;
char message[IRCMAXLEN];
connected=1;
connecting=0;
memset(buffer,0,BUFLEN);
script("event_connect","");
addrlength=sizeof(struct sockaddr);
if(getsockname(sockfd,(struct sockaddr*)&addr,&addrlength)) {
perror("Getting Local IP");
return;
}
strcpy(localip,inet_ntoa(addr.sin_addr));
if(pass.length()) {
sprintf(message,"PASS %s",pass.c_str());
senddata(message);
}
sprintf(message,"NICK %s",mynick);
// DEBUG
//TT_EvalF(TT_ARGS,".raw.text insert end \"Command (server.cc:839): %q\\n\"",message);
senddata(message);
sprintf(message,"USER %s %s %s :%s",name,localip,ip,desc);
// DEBUG
//TT_EvalF(TT_ARGS,".raw.text insert end \"Command (server.cc:843): %q\\n\"",message);
senddata(message);
chanlist.init_trav();
while((channelp=chanlist.trav())) {
if(channelp->topic) 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;
}
syntax highlighted by Code2HTML, v. 0.9.1