#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>

#ifdef DMALLOC
#include <dmalloc.h>
#endif

#ifdef SOCKS
#include <socks.h>
#endif

#include "serverdcc.h"
#include "window.h"
#include "support.h"
#include "echo.h"
#include "config.h"
#include "tcltk.h"
#include "quirc.h"
#include "format.h"

// Port 59 is what mIRC listens on for incoming connections
// Add error checking on close
// Add no connect warnings to sendata et all.

tdcc::~tdcc() {
  free(nick);
  Tcl_DeleteFileHandler(sockfd);
}

void tdcc::perror(char *message) {
  int number=errno;
  char num[100];
  if(type==DCCFILEGET||type==DCCFILESEND) {
    sprintf(num,"%d",index);
    fdisplay("DCC_FILE_ERROR",server->index,3,num,message,strerror(number));
  } else {
    fdisplay("DCC_ERROR",server->index,3,pathname,message,strerror(number));
  }
}

void tdcc::perror(char *message, int number) {
  char num[100];
  if(type==DCCFILEGET||type==DCCFILESEND) {
    sprintf(num,"%d",index);
    fdisplay("DCC_FILE_ERROR",server->index,3,num,message,strerror(number));
  } else {
    fdisplay("DCC_ERROR",server->index,3,pathname,message,strerror(number));
  }
}

void tdcc::filemessage(char *message) {
  char temp[1000];
  sprintf(temp," [%d] \0030,14 DCC \003 %s",index,message);
  echo(temp,pathname,1);
}

// Generic Network Handling Function for DCC's.  If something happens on a
// socket, this gets called.
void tdcc::handle(int mask) {
  int numbytes;
  char temp[TEMPLEN];
  //if(networklock) return;
  //networklock=1;
  //printf("Hi, here I am\n");
  switch(type) {
  case  DCCCHAT:
    if(listening) {
      if(mask & TCL_READABLE) {
	do_accept();
      }
    } else if(connected) {
      if(mask & TCL_READABLE) {
	if((numbytes=recv(sockfd,temp,TEMPLEN-1,0))==-1) {
	  perror("Receiving");
	  disconnect();
	} else {
	  if(numbytes) {
	    temp[numbytes]=0;
	    if((strlen(buffer)+strlen(temp))<BUFLEN) {
	      strcat(buffer,temp);
	      usedata();
	    } else {
	      echo("Buffer Overrun in DCCCHAT (Please contact author at quirc@patearl.net)",".main",1);
	    }
	  } else {
	    disconnect();
	  }
	}
      }
    } else if (connecting) {
      if(mask & TCL_WRITABLE) {
	complete_connect();
      }
    }
    break;
  case DCCFILEGET:
    if(connected) {
      if(mask & TCL_READABLE) {
	handle_incoming();
      }
    } else if (connecting) {
      if(mask & TCL_WRITABLE) {
	complete_connect();
      }
    }
    break;
  case DCCFILESEND:
    if(listening) {
      if(mask & TCL_READABLE) {
	do_accept();
      }
    } else if(connected) {
      int incoming_result=0;
      if(mask & TCL_READABLE) {
	//printf("1 - %d\n",time());
	incoming_result=handle_incoming();
	//printf("2\n");
      }
      if(!incoming_result && (mask & TCL_WRITABLE)) {
	//printf("3\n");
	handle_outgoing();
	//printf("4\n");
      }
    } else {
      fprintf(stderr,"DCC file sends shouldn't be connecting, they should be accepting.\n");
      exit(1);
    }
    // else if (connecting) {
    //if(mask & TCL_WRITABLE) {
    //complete_connect();
    //}
    //}
    break;
  default:
    fprintf(stderr,"Something bad has happened, there wasn't a valid type for the dcc.\n");
    exit(1);
  }
  //networklock=0;
}

int tdcc::getflags() {
  return flags;
}

int tdcc::getsockfd() {
  return sockfd;
}

tdcc::tdcc(tserver *serverp, const char *thenick, char *theip, int theport) {
  // DCC CHAT ACCEPT
  twindow window;
  twindow *windowp;
  int n;
  struct sockaddr_in addr;
  int windowexists=0;
  char assemble[TEMPLEN];
  
  nick = mystrdup("");

  strcpy(window.name,"=");
  strcat(window.name,thenick);
  windows.init_trav();
  while((windowp=windows.trav())) {
    if(!strcasecmp(windowp->name,window.name)&&windowp->server==serverp) {
      if(windowp->dcc->sockfd&&(windowp->dcc->connected||windowp->dcc->listening||windowp->dcc->connecting)) {
	close(windowp->dcc->sockfd);
	echo(" \0030,14 DCC \003 Disconnected.",windowp->dcc->pathname,1);
      }
      windowexists=1;
      windowp->dcc->connecting=0;
      windowp->dcc->connected=0;
      windowp->dcc->listening=0;
      errortest=0;
      
      strcpy(windowp->dcc->ip,theip);
      windowp->dcc->port=theport;
      
      if((windowp->dcc->sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
	windowp->dcc->perror("Getting Socket File Descriptor for DCC");
	errortest=1;
	return;
      }
      
      fcntl(windowp->dcc->sockfd, F_SETFL, O_NONBLOCK);
      
      addr.sin_family=AF_INET;
      addr.sin_port=htons(windowp->dcc->port);
      addr.sin_addr.s_addr=inet_addr(windowp->dcc->ip);
      bzero(&(addr.sin_zero),8);
      
      sprintf(assemble," \0030,14 DCC \003 Attempting connection to %s",windowp->dcc->ip);
      echo(assemble,windowp->dcc->pathname,1);
      windowp->dcc->connecting=1;
      if(connect(windowp->dcc->sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr))) {
	// We got an error.  If it's just EINPROGRESS keep going, otherwise error.
	if(errno!=EINPROGRESS) {
	  windowp->dcc->perror("Connecting [Immediate]");
	  windowp->dcc->disconnect();
	  errortest=1;
	  return;
	}
	Tcl_CreateFileHandler(windowp->dcc->sockfd,TCL_WRITABLE,handle_networking,windowp->dcc);
      } else {
	windowp->dcc->connected=1;
	windowp->dcc->connecting=0;
	echo(" \0030,14 DCC \003 Connected.",windowp->dcc->pathname,1);
	Tcl_CreateFileHandler(windowp->dcc->sockfd,TCL_READABLE,handle_networking,windowp->dcc);
      }
    }
  }
  
  if(!windowexists) {
    connecting=0;
    connected=0;
    listening=0;
    ip[0]=0;
    errortest=0;
    type=DCCCHAT;
    filefd=0;

    setnick(thenick);
    strcpy(ip,theip);
    port=theport;
    server=serverp;
    
    sprintf(pathname,".chat%d",chatnum);
    strcpy(window.pathname,pathname);
    window.server=serverp;
    window.dcc=this;
    window.title="QuIRC - ";
    window.title+=window.name;
    n=windows.insert_ascending(window);
    TT_EvalF(TT_ARGS,"Init_Window .chat%d %q %d chat %d",chatnum,window.name,n,serverp->index);
    TT_EvalF(TT_ARGS,"totop %d",n);
    chatnum++;

    if(connected) {
      echo(" \0030,5 ERROR \003 Already Connected",pathname,1);
      errortest=1;
      return;
    }
    
    if(connecting) {
      echo(" \0030,5 ERROR \003 We're in the middle of connecting, hold your horses!\n",pathname,1);
      errortest=1;
      return;
    }
    
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
      perror("Getting Socket File Descriptor for DCC");
      errortest=1;
      return;
    }
    
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
    
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=inet_addr(ip);
    bzero(&(addr.sin_zero),8);
    
    sprintf(assemble," \0030,14 DCC \003 Attempting connection to %s",ip);
    echo(assemble,pathname,1);
    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();
	errortest=1;
	return;
      }
      Tcl_CreateFileHandler(sockfd,TCL_WRITABLE,handle_networking,this);
    } else {
      connected=1;
      connecting=0;
      echo(" \0030,14 DCC \003 Connected.",pathname,1);
      Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
    }
    
    memset(buffer,0,BUFLEN);
  }
}

tdcc::tdcc(tserver *serverp, const char *thenick, char *theip, int theport, char *thefile, int thesize, int dccflags, const char *renamedfile) {
  // DCC SEND ACCEPT
  twindow window;
  struct sockaddr_in addr;
  char assemble[TEMPLEN];
  tdccentry dccentry;
  int n;
  
  nick = mystrdup(thenick);

  sprintf(window.pathname,".files%d",serverp->index);
  if(!windows.find(window)) {
    strcat(window.name,"DCC Files");
    window.server=serverp;
    window.dcc=0;
    window.title="QuIRC - DCC Files";
    n=windows.insert_ascending(window);
    TT_EvalF(TT_ARGS,"Init_Window %q %q %d files %d",window.pathname,window.name,n,serverp->index);
    TT_EvalF(TT_ARGS,"pack forget %s.text %s.entry %s.text_vscroll",window.pathname,window.pathname,window.pathname);
    TT_EvalF(TT_ARGS,"grid columnconfigure %s 0 -weight 1",window.pathname);
    //TT_EvalF(TT_ARGS,"grid rowconfigure %s 0 -weight 1",window.pathname);
    TT_EvalF(TT_ARGS,"grid rowconfigure %s 1 -weight 1",window.pathname);
    TT_EvalF(TT_ARGS,"grid [listbox %s.list -yscroll \"%s.list_vscroll set\" -height 11 -exportselection no] -column 0 -row 0 -sticky new",window.pathname,window.pathname,window.pathname);
    TT_EvalF(TT_ARGS,"grid [scrollbar %s.list_vscroll -command \"%s.list yview\"] -column 1 -row 0 -sticky ns",window.pathname,window.pathname,window.pathname,window.pathname);
    TT_EvalF(TT_ARGS,"grid %s.text -column 0 -row 1 -sticky nsew",window.pathname);
    TT_EvalF(TT_ARGS,"grid %s.text_vscroll -column 1 -row 1 -sticky ns",window.pathname);
    TT_EvalF(TT_ARGS,"grid %s.entry -column 0 -row 2 -columnspan 2 -sticky ew",window.pathname);
    TT_EvalF(TT_ARGS,"%s.list configure -foreground $::dynamic::theme_filelist_foreground -background $::dynamic::theme_filelist_background -font $::dynamic::theme_filelist_font",window.pathname);
    TT_EvalF(TT_ARGS,"bind %s.list <Button-3> {::template::killdcc [currentindex] [%s.list nearest %y]}",window.pathname,window.pathname);
    echo(" \0030,14 DCC \003 Right click on files in the list to remove them.",window.pathname,0);
    TT_EvalF(TT_ARGS,"totop %d",n);
    //serverp->dcclist.insert_end(dccentry); 
    //TT_EvalF(TT_ARGS,"%s.list insert 0 [::format_dcc_status Index Status]",window.pathname);
  }
  
  index=filenum++;
  
  strcpy(pathname,window.pathname);
  
  written=0;
  connecting=0;
  connected=0;
  listening=0;
  ip[0]=0;
  errortest=0;
  type=DCCFILEGET;
  filefd=0;
  size=thesize;
  checkflags=dccflags;
  
  struct timeval tv;
  struct timezone tz;
  tz.tz_minuteswest=0;
  tz.tz_dsttime=0;
  if(gettimeofday(&tv,&tz)) {
    perror("Call to gettimeofday()");
    errortest=1;
    return;
  }
  dccseconds=tv.tv_sec;
  dccmilliseconds=tv.tv_usec;
  
  dccentry.dcc=this;
  entryindex=serverp->dcclist.insert_end(dccentry);
  //TT_EvalF(TT_ARGS,"%s.list insert %d [::format_dcc_status %d \"\"]",window.pathname,entryindex,index);
  TT_EvalF(TT_ARGS,"%s.list insert %d \" \\[%d]\"",window.pathname,entryindex,index);
  
  strcpy(destfile,TT_Str(TT_ARGS,"set ::dynamic::dcc_directory"));
  strcat(destfile,"/");
  strcat(destfile,renamedfile);
  
  strcpy(file,thefile);
  strcpy(ip,theip);
  port=theport;
  server=serverp;
  
  if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
    perror("Getting Socket File Descriptor for DCC");
    return;
  }
  
  fcntl(sockfd, F_SETFL, O_NONBLOCK);
  
  addr.sin_family=AF_INET;
  addr.sin_port=htons(port);
  addr.sin_addr.s_addr=inet_addr(ip);
  bzero(&(addr.sin_zero),8);
  
  sprintf(assemble," [%d] \0030,14 DCC \003 Attempting connection to %s",index,ip);
  echo(assemble,pathname,1);
  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();
      errortest=1;
      return;
    }
    Tcl_CreateFileHandler(sockfd,TCL_WRITABLE,handle_networking,this);
  } else {
    connected=1;
    connecting=0;
    sprintf(assemble," [%d] \0030,14 DCC \003 Connected.",index);
    echo(assemble,pathname,1);
    Tcl_CreateFileHandler(sockfd,TCL_READABLE|TCL_WRITABLE,handle_networking,this);
  }
  memset(buffer,0,BUFLEN);
}

tdcc::tdcc(tserver *serverp, const char *thenick) {
  // DCC CHAT SEND
  char temp[TEMPLEN];
  twindow window;
  twindow *windowp;
  int n;
  struct sockaddr_in my_addr;
  socklen_t my_addr_len=sizeof(struct sockaddr);
  
  nick = mystrdup(thenick);

  if(!strcasecmp(thenick,serverp->getmynick())) {
    echo(" \0030,5 ERROR \003 You cannot DCC Chat yourself.",serverp->pathname,1);
    return;
  }

  strcpy(window.name,"=");
  strcat(window.name,nick);
  windows.init_trav();
  while((windowp=windows.trav())) {
    if(!strcasecmp(windowp->name,window.name)&&(windowp->server==serverp)) {
      if(windowp->dcc->sockfd&&(windowp->dcc->connected||windowp->dcc->listening||windowp->dcc->connecting)) {
	close(windowp->dcc->sockfd);
	echo(" \0030,14 DCC \003 Disconnected.",windowp->dcc->pathname,1);
      }
      windowp->dcc->connecting=0;
      windowp->dcc->connected=0;
      windowp->dcc->listening=1;
      errortest=0;
      
      if((windowp->dcc->sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
	windowp->dcc->perror("Getting Socket File Descriptor for DCC");
	errortest=1;
	return;
      }
      
      my_addr.sin_family=AF_INET;
      my_addr.sin_port=0;
      my_addr.sin_addr.s_addr=INADDR_ANY;
      bzero(&(my_addr.sin_zero),8);
      
      if(bind(windowp->dcc->sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1) {
	windowp->dcc->perror("Binding DCC Socket");
	errortest=1;
	return;
      }
      
      if(getsockname(windowp->dcc->sockfd,(struct sockaddr *)&my_addr,&my_addr_len)==-1) {
	windowp->dcc->perror("Getting DCC Socket Name");
	errortest=1;
	return;
      }
      
      windowp->dcc->port=ntohs(my_addr.sin_port);
      
      if(listen(windowp->dcc->sockfd,1)==-1) {
	windowp->dcc->perror("Listening for DCC Connection");
	errortest=1;
	return;
      }
      
      Tcl_CreateFileHandler(windowp->dcc->sockfd,TCL_READABLE,handle_networking,windowp->dcc);

      if(strlen(TT_Str(TT_ARGS,"set ::dynamic::dcc_localip"))) {
	sprintf(temp,"PRIVMSG %s :\001DCC CHAT chat %u %d\001",thenick,htonl(inet_addr(TT_Str(TT_ARGS,"set ::dynamic::dcc_localip"))),windowp->dcc->port);
      } else {
	sprintf(temp,"PRIVMSG %s :\001DCC CHAT chat %u %d\001",thenick,htonl(inet_addr(serverp->localip)),windowp->dcc->port);
      }
      
      // DEBUG
      //TT_EvalF(TT_ARGS,".raw.text insert end \"Command (dcc.cc:465): %q\\n\"",temp);
      windowp->dcc->server->senddata(temp);
      
      return;
    }
  }
  
  connecting=0;
  connected=0;
  listening=1;
  ip[0]=0;
  errortest=0;
  type=DCCCHAT;
  filefd=0;
  
  if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
    serverp->perror("Getting Socket File Descriptor for DCC");
    errortest=1;
    return;
  }
  
  my_addr.sin_family=AF_INET;
  my_addr.sin_port=0;
  my_addr.sin_addr.s_addr=INADDR_ANY;
  bzero(&(my_addr.sin_zero),8);
  
  if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1) {
    serverp->perror("Binding DCC Socket");
    errortest=1;
    return;
  }
  
  if(getsockname(sockfd,(struct sockaddr *)&my_addr,&my_addr_len)==-1) {
    serverp->perror("Getting DCC Socket Name");
    errortest=1;
    return;
  }
  
  port=ntohs(my_addr.sin_port);
  
  // Change to 0?
  if(listen(sockfd,1)==-1) {
    serverp->perror("Listening for DCC Connection");
    errortest=1;
    return;
  }
  
  memset(buffer,0,BUFLEN);

  Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
  
  sprintf(temp,"PRIVMSG %s :\001DCC CHAT chat %u %d\001",thenick,htonl(inet_addr(serverp->localip)),port);
  
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"Command (dcc.cc:526): %q\\n\"",temp);
  serverp->senddata(temp);
  
  server=serverp;
  
  sprintf(pathname,".chat%d",chatnum);
  strcpy(window.pathname,pathname);
  window.server=serverp;
  window.dcc=this;
  window.title="QuIRC - ";
  window.title+=window.name;
  n=windows.insert_ascending(window);
  TT_EvalF(TT_ARGS,"Init_Window .chat%d %q %d chat %d",chatnum,window.name,n,serverp->index);
  TT_EvalF(TT_ARGS,"totop %d",n);
  chatnum++;
}
  
tdcc::tdcc(tserver *serverp, const char *thenick, const char *thefile) {
  // DCC SEND SEND
  char temp[TEMPLEN];
  twindow window;
  //twindow *windowp;
  int n;
  struct sockaddr_in my_addr;
  socklen_t my_addr_len=sizeof(struct sockaddr);
  //int windowexists=0;
  char *p;  
  FILE *myfile;
  tdccentry dccentry;

  nick = mystrdup(thenick);

  strcpy(file,thefile);

  if((p=rindex(file,'/'))) {
    strcpy(destfile,++p);
  } else {
    strcpy(destfile,thefile);
  }

  while((p=strstr(destfile," "))) {
    *p='_';
  }

  sprintf(window.pathname,".files%d",serverp->index);
  if(!windows.find(window)) {
    strcat(window.name,"DCC Files");
    window.server=serverp;
    window.dcc=0;
    window.title="QuIRC - DCC Files";
    n=windows.insert_ascending(window);
    TT_EvalF(TT_ARGS,"Init_Window %q %q %d files %d",window.pathname,window.name,n,serverp->index);
    TT_EvalF(TT_ARGS,"pack forget %s.text %s.entry %s.text_vscroll",window.pathname,window.pathname,window.pathname);
    TT_EvalF(TT_ARGS,"grid columnconfigure %s 0 -weight 1",window.pathname);
    //TT_EvalF(TT_ARGS,"grid rowconfigure %s 0 -weight 1",window.pathname);
    TT_EvalF(TT_ARGS,"grid rowconfigure %s 1 -weight 1",window.pathname);
    TT_EvalF(TT_ARGS,"grid [listbox %s.list -yscroll \"%s.list_vscroll set\" -height 11 -exportselection no] -column 0 -row 0 -sticky new",window.pathname,window.pathname,window.pathname);
    TT_EvalF(TT_ARGS,"grid [scrollbar %s.list_vscroll -command \"%s.list yview\"] -column 1 -row 0 -sticky ns",window.pathname,window.pathname,window.pathname,window.pathname);
    TT_EvalF(TT_ARGS,"grid %s.text -column 0 -row 1 -sticky nsew",window.pathname);
    TT_EvalF(TT_ARGS,"grid %s.text_vscroll -column 1 -row 1 -sticky ns",window.pathname);
    TT_EvalF(TT_ARGS,"grid %s.entry -column 0 -row 2 -columnspan 2 -sticky ew",window.pathname);
    TT_EvalF(TT_ARGS,"%s.list configure -foreground $::dynamic::theme_filelist_foreground -background $::dynamic::theme_filelist_background -font $::dynamic::theme_filelist_font",window.pathname);
    TT_EvalF(TT_ARGS,"bind %s.list <Button-3> {::template::killdcc [currentindex] [%s.list nearest %y]}",window.pathname,window.pathname);
    echo(" \0030,14 DCC \003 Right click on files in the list to remove them.",window.pathname,0);
    TT_EvalF(TT_ARGS,"totop %d",n);
    //serverp->dcclist.insert_end(dccentry); 
    //TT_EvalF(TT_ARGS,"%s.list insert 0 [::format_dcc_status Index Status]",window.pathname);
  }

  index=filenum++;

  strcpy(pathname,window.pathname);

  connecting=0;
  connected=0;
  listening=1;
  ip[0]=0;
  errortest=0;
  transmitted=0;
  filefd=0;

  server=serverp;

  struct timeval tv;
  struct timezone tz;
  tz.tz_minuteswest=0;
  tz.tz_dsttime=0;
  if(gettimeofday(&tv,&tz)) {
    perror("Call to gettimeofday()");
    errortest=1;
    return;
  }
  dccseconds=tv.tv_sec;
  dccmilliseconds=tv.tv_usec;
  
  if((myfile=fopen(file,"rb"))) {
    fseek(myfile,0,SEEK_END);
    size=ftell(myfile);
    fclose(myfile);
  }
  
  
  type=DCCFILESEND;
  
  if((filefd=open(file,O_RDONLY))==-1) {
    perror("Opening Given File");
    errortest=1;
    return;
  }
  
  if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
    perror("Getting Socket File Descriptor");
    errortest=1;
    return;
  }

  fcntl(sockfd, F_SETFL, O_NONBLOCK);
  
  my_addr.sin_family=AF_INET;
  my_addr.sin_port=0;
  my_addr.sin_addr.s_addr=INADDR_ANY;
  bzero(&(my_addr.sin_zero),8);
  
  if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1) {
    perror("Binding Socket");
    errortest=1;
    return;
  }
  
  if(getsockname(sockfd,(struct sockaddr *)&my_addr,&my_addr_len)==-1) {
    perror("Getting Socket Name");
    errortest=1;
    return;
  }
  
  port=ntohs(my_addr.sin_port);
  
  // Change to 0?
  if(listen(sockfd,1)==-1) {
    perror("Listening for Connection");
    errortest=1;
    return;
  }
  
  dccentry.dcc=this;
  entryindex=serverp->dcclist.insert_end(dccentry);
  //TT_EvalF(TT_ARGS,"%s.list insert %d [::format_dcc_status %d \"\"]",window.pathname,entryindex,index);
  TT_EvalF(TT_ARGS,"%s.list insert %d \" \\[%d]\"",window.pathname,entryindex,index);

  memset(buffer,0,BUFLEN);

  Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
  
  if(strlen(TT_Str(TT_ARGS,"set ::dynamic::dcc_localip"))) {
    sprintf(temp,"PRIVMSG %s :\001DCC SEND %s %u %d %ld\001",thenick,destfile,htonl(inet_addr(TT_Str(TT_ARGS,"set ::dynamic::dcc_localip"))),port,size);
  } else {
    sprintf(temp,"PRIVMSG %s :\001DCC SEND %s %u %d %ld\001",thenick,destfile,htonl(inet_addr(serverp->localip)),port,size);
  }
	
  // DEBUG
  //TT_EvalF(TT_ARGS,".raw.text insert end \"Command (dcc.cc:686): %q\\n\"",temp);
  serverp->senddata(temp);
  
  if(!errortest) {
    sprintf(temp,"Attempting to Send %s to %s",file,nick);
    filemessage(temp);
  }
}
  
int tdcc::senddata(const char *message) {
  char *temp=strmem(message,1);
  if(connected) {
    strcat(temp,"\n");
    if(send(sockfd,temp,(signed)strlen(temp),0)!=(signed)strlen(temp)) {
      perror("Sending");
      disconnect();
      return 1;
    }
    TT_EvalF(TT_ARGS,"if { $::dynamic::do_rawview } \" .raw.text insert end \\\"DCC Command: [escape %q]\\n\\\"\"",message);
    return 0;
  }
  free(temp);
  return 1;
}

void tdcc::sendchunk() {
  Tcl_CreateFileHandler(sockfd,TCL_READABLE|TCL_WRITABLE,handle_networking,this);
}

int tdcc::handle_incoming() {
  // Returns 1 if disconnect was performed.
  if(type==DCCFILESEND) {
    ssize_t numbytes;
    char assemble[TEMPLEN];
    unsigned int buffer;
    numbytes=read(sockfd,&buffer,4);
    acknowledged=ntohl(buffer);
    if(numbytes) {
      if(numbytes>0) {
	struct timeval tv;
	struct timezone tz;
	long checktime;
	tz.tz_minuteswest=0;
	tz.tz_dsttime=0;
	if(gettimeofday(&tv,&tz)) {
	  perror("Call to gettimeofday()");
	  return 1;
	}
	checktime=(tv.tv_sec-dccseconds)*1000+(tv.tv_usec-dccmilliseconds)/1000;
	if(checktime>=TT_Int(TT_ARGS,"set ::dynamic::dcc_statuswait")) {
	  dccmilliseconds=tv.tv_usec;
	  dccseconds=tv.tv_sec;
	  tdccentry dccentry;
	  dccentry.dcc=this;
	  entryindex=server->dcclist.find(dccentry)->index;
	  if(!(server->script("event_dcc_sendstatus","d q q q d d d d",entryindex,nick,file,destfile,acknowledged,transmitted,size,index)&6)) {
	    sprintf(assemble,"%3d%%",(int)(acknowledged/(float)size*100));
	    echo(assemble,pathname,1);
	  }
	}
	if(acknowledged>=transmitted) sendchunk();
      } else {
	perror("Getting Data");
	disconnect();
	return 1;
      }
    } else {
      disconnect();
      return 1;
    }
    if(acknowledged==size) {
      disconnect();
      return 1;
    }
  }
  if(type==DCCFILEGET) {
    ssize_t numbytes;
    char buffer[TEMPLEN];
    ssize_t checkbytes;
    numbytes=read(sockfd,buffer,TEMPLEN);
    if(numbytes) {
      if(numbytes>0) {
	if(write(filefd,buffer,numbytes)!=numbytes) {
	  perror("Writing to File");
	  disconnect();
	  return 1;
	} else {
	  written+=numbytes;
	  checkbytes=htonl(written);
	  if(write(sockfd,(char *)&checkbytes,4)!=4) {
	    perror("Sending Confirmation");
	    disconnect();
	    return 1;
	  }
	  else {
	    struct timeval tv;
	    struct timezone tz;
            long checktime;
	    tz.tz_minuteswest=0;
	    tz.tz_dsttime=0;
            if(gettimeofday(&tv,&tz)) {
               perror("Call to gettimeofday()");
	       return 1;
	    }
	    checktime=(tv.tv_sec-dccseconds)*1000+(tv.tv_usec-dccmilliseconds)/1000;
	    if(checktime>=TT_Int(TT_ARGS,"set ::dynamic::dcc_statuswait")) {
	      dccmilliseconds=tv.tv_usec;
	      dccseconds=tv.tv_sec;
	      tdccentry dccentry;
	      dccentry.dcc=this;
	      entryindex=server->dcclist.find(dccentry)->index;
	      if(!(server->script("event_dcc_getstatus","d q q q d d d",entryindex,nick,destfile,file,written,size,index)&6)) {
		char assemble[TEMPLEN];
		sprintf(assemble," %d",written);
		echo(assemble,pathname,1);
	      }
	    }
	  }
	}
      } else {
	perror("Getting Data");
	disconnect();
	return 1;
      }
    } else {
      disconnect();
      return 1;
    }
  }
  return 0;
}

void tdcc::usedata() {
  char *thisline;
  char *position;
  char temp[TEMPLEN];

  while((position=strstr(buffer,"\n"))) {
    *position=0;
    thisline=strmem(buffer,0);
    if(thisline[strlen(thisline)-1]=='\r') thisline[strlen(thisline)-1]=0;
    strcpy(buffer,position+1);
    
    if(!(server->script("event_dcc_text","q q",nick,thisline)&2)) {
      sprintf(temp,"<%s> %s",nick,thisline);
      echo(temp,pathname,1);
    }
    
    free(thisline);
  }  
}

void tdcc::complete_connect() {
  socklen_t optlen;
  int optval;
  optlen=sizeof(optval);
  getsockopt(sockfd,SOL_SOCKET,SO_ERROR,(void *)&optval,&optlen);
  if(optval) {
    perror("Connecting",optval);
    disconnect();
  } else {
    connected=1;
    connecting=0;
    if(type==DCCFILESEND) {
      tdccentry dccentry;
      dccentry.dcc=this;
      entryindex=server->dcclist.find(dccentry)->index;
      if(!(server->script("event_dcc_connect","q q q d q q q d d d",pathname,nick,ip,port,"filesend",file,destfile,size,index,entryindex))&6) {
	echo(" \0030,14 DCC \003 Connected.",pathname,1);
      }
      // CHANGE
      Tcl_CreateFileHandler(sockfd,TCL_READABLE|TCL_WRITABLE,handle_networking,this);
    }
    if(type==DCCFILEGET) {
      tdccentry dccentry;
      dccentry.dcc=this;
      entryindex=server->dcclist.find(dccentry)->index;
      if(!(server->script("event_dcc_connect","q q q d q q q d d d",pathname,nick,ip,port,"fileget",destfile,file,size,index,entryindex))&6) {
	echo(" \0030,14 DCC \003 Connected.",pathname,1);
      }
      if(checkflags & DCC_OVERWRITE) {
	if((filefd=open(destfile,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))==-1) {
	  perror("Opening for Writing");
	  disconnect();
	  return;
	}
      } else {
	if((filefd=open(destfile,O_WRONLY|O_CREAT|O_EXCL,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))==-1) {
	  perror("Opening for Writing");
	  disconnect();
	  return;
	}
      }
      timestarted=time(0); 
      // CHANGE
      Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
    }
    if(type==DCCCHAT) {
      if(!(server->script("event_dcc_connect","q q q d q",pathname,nick,ip,port,"chat"))&6) {
	echo(" \0030,14 DCC \003 Connected.",pathname,1);
      }
      // CHANGE
      Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
    }
  }
}

void tdcc::do_accept() {
  struct sockaddr_in addr;
  socklen_t addrlength;
  int tempfd;
  addrlength=sizeof(struct sockaddr);
  listening=0;
  if((tempfd=accept(sockfd,(struct sockaddr *)&addr,&addrlength))==-1) {
    perror("Accepting DCC Connection");
    Tcl_DeleteFileHandler(sockfd);
    close(sockfd);
  } else {
    connected=1;
    if(type==DCCFILESEND) {
      tdccentry dccentry;
      dccentry.dcc=this;
      entryindex=server->dcclist.find(dccentry)->index;
      if(!(server->script("event_dcc_connect","q q q d q q q d d d",pathname,nick,ip,port,"filesend",file,destfile,size,index,entryindex))&6) {
	echo(" \0030,14 DCC \003 Connected.",pathname,1);
      }
      // CHANGE
      Tcl_CreateFileHandler(tempfd,TCL_READABLE|TCL_WRITABLE,handle_networking,this);
    }
    if(type==DCCCHAT) {
      if(!(server->script("event_dcc_connect","q q q d q",pathname,nick,ip,port,"chat"))&6) {
	echo(" \0030,14 DCC \003 Connected.",pathname,1);
      }
      // CHANGE
      Tcl_CreateFileHandler(tempfd,TCL_READABLE,handle_networking,this);
    }
    Tcl_DeleteFileHandler(sockfd);
    close(sockfd);
    sockfd=tempfd;
    if(type==DCCFILESEND) {
      timestarted=time(0);
      sendchunk();
    }
  }
}

void tdcc::handle_outgoing(void) {
  int packetsize=TT_Int(TT_ARGS,"set ::dynamic::dcc_packetsize");
  char readbuffer[packetsize];
  ssize_t numbytes;
  
  numbytes=read(filefd,readbuffer,packetsize);
  
  if(numbytes) {
    if(numbytes>0) {
      if(send(sockfd,readbuffer,numbytes,0)!=numbytes) {
	perror("Sending");
	disconnect();
      }
      transmitted+=numbytes;
    } else {
      perror("Reading Data File");
    }
  }
  Tcl_CreateFileHandler(sockfd,TCL_READABLE,handle_networking,this);
}

void killdcc(ClientData clientData) {
  delete (class tdcc *)clientData;
}

int tdcc::disconnect(void) {
  char temp[100];
  if(listening||connected||connecting) {
    // Error check close
    if(sockfd) close(sockfd);
    if(filefd) close(filefd);
    if(type==DCCFILESEND||type==DCCFILEGET) {
      sprintf(temp," [%d] \0030,14 DCC \003 Disconnected.",index);
      echo(temp,pathname,1);
    } else {
      echo(" \0030,14 DCC \003 Disconnected.",pathname,1);
    }
    listening=0;
    connected=0;
    connecting=0;
  }
  if(type==DCCFILESEND||type==DCCFILEGET) {
    tdccentry dccentry;
    dccentry.dcc=this;
    entryindex=server->dcclist.deleteitem(dccentry);
    TT_EvalF(TT_ARGS,"%s.list delete %d %d",pathname,entryindex,entryindex);
    sprintf(temp," [%d] \0030,14 DCC \003 Destroyed.",index);
    echo(temp,pathname,1);
    Tcl_CreateTimerHandler(300,killdcc,this);
  }
  memset(buffer,0,BUFLEN);
  Tcl_DeleteFileHandler(sockfd);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1