/* 
   dcc.c -- handles:
     activity on a dcc socket
     disconnect on a dcc socket
   ...and that's it!  (but it's a LOT)

   dprintf'ized, 27oct95
*/

#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <netinet/in.h>     /* hpux needs for ntohl */
#include "eggdrop.h"
#include "chan.h"
#include "proto.h"


/* dcc list */
struct dcc_t dcc[MAXDCC];
/* total dcc's */
int dcc_total=0;
/* root dcc directory */
char dccdir[121]="";
/* directory to put incoming dcc's into */
char dccin[121]="";
/* temporary directory (default: current dir) */
char tempdir[121]="";
/* if non-zero, accept telnets at this port */
int telnet_port=0;
/* require 'p' access to get on the party line? */
int require_p=0;
/* let all uploads go to the user's current directory? */
int upload_to_cd=0;
/* allow people to introduce themselves via telnet */
int allow_new_telnets=0;
/* how many bytes should we send at once? */
int dcc_block=512;

extern int serv;
extern int cx_line;
extern char cx_file[];
extern char ver[];
extern char version[];
extern char botname[];
extern char realbotname[];
extern char origbotname[];
extern char notify_new[];
extern int conmask;
extern int isolate;
extern int require_x;
extern int default_flags;
extern int online;
extern struct userrec *userlist;
extern struct chanset_t *chanset;


/* main loop calls here when activity is found on a dcc socket */
void dcc_activity(z,buf,len)
int z,len; char *buf;
{
  int idx; char s[121],s1[81],*bf; unsigned long netint,l,cmp;
  context;
  for (idx=0; ((dcc[idx].sock!=z) || (dcc[idx].type==DCC_FORK)) && 
       (idx<dcc_total); idx++);
  context;
  if (idx>=dcc_total) return;
  if ((dcc[idx].type!=DCC_SEND) && (dcc[idx].type!=DCC_GET) &&
      (dcc[idx].type!=DCC_GET_PENDING) && (dcc[idx].type!=DCC_TELNET) &&
      (dcc[idx].type!=DCC_RELAY) && (dcc[idx].type!=DCC_RELAYING)) {
    /* interpret embedded telnet codes */
    strip_telnet(z,buf,&len);
  }
  context;
  if (dcc[idx].type==DCC_CHAT_PASS) {
    if (pass_match_by_handle(buf,dcc[idx].nick)) {
      if (get_attr_handle(dcc[idx].nick) & USER_BOT) {
	nfree(dcc[idx].u.chat);
	dcc[idx].type=DCC_TANDEM;
	set_tand(idx);
	dcc[idx].u.tand->status=STAT_CALLED;
	dcc[idx].u.tand->timer=time(NULL);
	dcc[idx].u.tand->version[0]=0;
	if (get_attr_handle(dcc[idx].nick) & BOT_LEAF)
	  dcc[idx].u.tand->status|=STAT_LEAF;
	tprintf(z,"*hello!\n");
	tprintf(z,"version %s\n",ver);
	tprintf(z,"thisbot %s\n",origbotname);
	stop_auto(dcc[idx].nick);
	putlog(LOG_TAND,"*","Linked to %s",dcc[idx].nick);
	chatout("*** Linked to %s\n",dcc[idx].nick);
	tandout_but(idx,"nlinked %s %s\n",dcc[idx].nick,origbotname);
	tandout_but(idx,"chat %s Linked to %s\n",origbotname,dcc[idx].nick);
	dump_links(z); addbot(dcc[idx].nick,dcc[idx].nick);
	check_tcl_link(dcc[idx].nick,origbotname);
      }
      else {
	dcc[idx].type=DCC_CHAT;
	dcc[idx].u.chat->status&=~STAT_CHAT;
	if (dcc[idx].u.chat->status&STAT_MASTER)
	  dcc[idx].u.chat->con_flags=conmask;
	if (dcc[idx].u.chat->status&STAT_TELNET)
	  tprintf(dcc[idx].sock,"\377\374\001\n");  /* turn echo back on */
	dcc_chatter(idx);
      }
    }
    else {
      if (get_attr_handle(dcc[idx].nick) & USER_BOT)
	tprintf(z,"badpass\n");
      else dprintf(idx,"Authentification failed.\n");
      putlog(LOG_MISC,"*","Bad password: DCC chat [%s]%s",dcc[idx].nick,
	  dcc[idx].host);
      close(z); lostdcc(idx);
    }
  }
  else if (dcc[idx].type==DCC_FILES_PASS) {
    if (pass_match_by_handle(buf,dcc[idx].nick)) {
      if (too_many_filers()) {
	dprintf(idx,"Too many people are in the file system right now.\n");
	dprintf(idx,"Please try again later.\n");
	putlog(LOG_MISC,"*","File area full: DCC chat [%s]%s",dcc[idx].nick,
	    dcc[idx].host);
	close(z); lostdcc(idx);
      }
      else {
	dcc[idx].type=DCC_FILES;
	if (dcc[idx].u.file->chat->status&STAT_TELNET)
	  tprintf(dcc[idx].sock,"\377\374\001\n");  /* turn echo back on */
	putlog(LOG_FILES,"*","File system: [%s]%s/%d",dcc[idx].nick,
	       dcc[idx].host,dcc[idx].port);
	if (!welcome_to_files(idx,z)) {
	  putlog(LOG_FILES,"*","File system broken.");
	  close(z); lostdcc(idx);
	}
      }
    }
    else {
      dprintf(idx,"Authentification failed.\n");
      putlog(LOG_MISC,"*","Bad password: DCC chat [%s]%s",dcc[idx].nick,
	     dcc[idx].host);
      close(z); lostdcc(idx);
    }
  }
  else if (dcc[idx].type==DCC_TANDEM_NEW) {
    if (strcasecmp(buf,"*hello!")==0) {
      dcc[idx].type=DCC_TANDEM; stop_auto(dcc[idx].nick);
      putlog(LOG_TAND,"*","Linked to %s",dcc[idx].nick);
      chatout("*** Linked to %s\n",dcc[idx].nick);
      tprintf(dcc[idx].sock,"version %s\n",ver);
      tprintf(dcc[idx].sock,"thisbot %s\n",origbotname);
      tandout_but(idx,"nlinked %s %s\n",dcc[idx].nick,origbotname);
      tandout_but(idx,"chat %s Linked to %s\n",origbotname,dcc[idx].nick);
      dump_links(dcc[idx].sock); addbot(dcc[idx].nick,dcc[idx].nick);
      check_tcl_link(dcc[idx].nick,origbotname);
    }
    if (strcasecmp(buf,"badpass")==0) {
      /* we entered the wrong password */
      putlog(LOG_TAND,"*","Bad password on connect attempt to %s.",
	     dcc[idx].nick);
    }
    if (strcasecmp(buf,"passreq")==0) {
      if (pass_match_by_handle("nopass",dcc[idx].nick)) {
	putlog(LOG_TAND,"*","Password required for connection to %s.",
	       dcc[idx].nick);
	tprintf(z,"nopass\n");
      }
    }
    if (strncmp(buf,"error",5)==0) {
      split(NULL,buf);
      putlog(LOG_TAND,"*","ERROR linking %s: %s",dcc[idx].nick,buf);
    }
    /* ignore otherwise */
  }
  else if (dcc[idx].type==DCC_TANDEM) {
    got_tandem(idx,buf);
  }
  else if (dcc[idx].type==DCC_CHAT) {
    int i;
    if ((!detect_dcc_flood(dcc[idx].u.chat,idx)) &&
	(!check_tcl_filt(idx,buf))) {
      int nathan=0; char *v=buf;
      /* check for beeps and cancel annoying ones */
      while (*v != 0) switch(*v) {
      case 7:  /* beep - no more than 3 */
	nathan++;
	if (nathan>3) strcpy(v,v+1);
	else v++;
	break;
      case 27:  /* ESC - ansi code? */
      case '\r':   /* weird pseudo-linefeed */
	strcpy(v,v+1); break;
      default:
	v++;
      }
      if ((buf[0]=='.') || (dcc[idx].u.chat->channel<0)) {
	if (buf[0]=='.') strcpy(buf,&buf[1]);
	if (got_dcc_cmd(idx,buf)) {
	  check_tcl_chof(dcc[idx].nick,dcc[idx].sock);
	  dprintf(idx,"*** Ja mata!\n");
	  putlog(LOG_MISC,"*","DCC connection closed (%s!%s)",dcc[idx].nick,
		 dcc[idx].host);
	  if (dcc[idx].u.chat->channel>=0)
	    chanout2(dcc[idx].u.chat->channel,"%s left the party line.\n",
		     dcc[idx].nick);
	  if (dcc[idx].sock!=STDOUT) {
	    close(dcc[idx].sock); lostdcc(idx);
	  }
	  else {
	    tprintf(STDOUT,"\n### SIMULATION RESET\n\n");
	    dcc_chatter(idx);
	  }
	}
      }
      else if (buf[0]==',') {
	for (i=0; i<dcc_total; i++) {
	  if ((dcc[i].type==DCC_CHAT) && (dcc[i].u.chat->status&STAT_MASTER)
	      && (dcc[i].u.chat->channel>=0) &&
	      ((i!=idx) || (dcc[idx].u.chat->status&STAT_ECHO)))
	    dprintf(i,"-%s- %s\n",dcc[idx].nick,&buf[1]);
	  if ((dcc[i].type==DCC_FILES) && 
	      (dcc[i].u.file->chat->status&STAT_MASTER) &&
	      ((i!=idx) || (dcc[idx].u.chat->status&STAT_ECHO)))
	    dprintf(i,"-%s- %s\n",dcc[idx].nick,&buf[1]);
	}
      }
      else {
	if (dcc[idx].u.chat->away!=NULL) not_away(idx);
	if (dcc[idx].u.chat->status&STAT_ECHO)
	  chanout(dcc[idx].u.chat->channel,"<%s> %s\n",dcc[idx].nick,buf);
	else
	  chanout_but(idx,dcc[idx].u.chat->channel,"<%s> %s\n",dcc[idx].nick,
		      buf);
	if (!isolate)
	  tandout("chan %s@%s %d %s\n",dcc[idx].nick,origbotname,
		  dcc[idx].u.chat->channel,buf);
	check_tcl_chat(dcc[idx].nick,dcc[idx].u.chat->channel,buf);
      }
    }
  }
  else if (dcc[idx].type==DCC_FILES) {
    int i;
    if (!detect_dcc_flood(dcc[idx].u.file->chat,idx)) {
      if (buf[0]==',') {
	for (i=0; i<dcc_total; i++) {
	  if ((dcc[i].type==DCC_CHAT) && (dcc[i].u.chat->status&STAT_MASTER)
	      && (dcc[i].u.chat->channel>=0) &&
	      ((i!=idx) || (dcc[idx].u.chat->status&STAT_ECHO)))
	    dprintf(i,"-%s- %s\n",dcc[idx].nick,&buf[1]);
	  if ((dcc[i].type==DCC_FILES) &&
	      (dcc[i].u.file->chat->status&STAT_MASTER) &&
	      ((i!=idx) || (dcc[idx].u.chat->status&STAT_ECHO)))
	    dprintf(i,"-%s- %s\n",dcc[idx].nick,&buf[1]);
	}
      }
      else if (got_files_cmd(idx,buf)) {
	dprintf(idx,"*** Ja mata!\n");
	putlog(LOG_FILES,"*","DCC user [%s]%s left file system",dcc[idx].nick,
	       dcc[idx].host);
	set_handle_dccdir(userlist,dcc[idx].nick,dcc[idx].u.file->dir);
	if (dcc[idx].u.file->chat->status&STAT_CHAT) {
	  struct chat_info *ci;
	  dprintf(idx,"Returning you to command mode...\n");
	  ci=dcc[idx].u.file->chat; nfree(dcc[idx].u.file);
	  dcc[idx].u.chat=ci;
	  if (dcc[idx].u.chat->channel>=0)
	    chanout2(dcc[idx].u.chat->channel,"%s joined the party line\n",
		     dcc[idx].nick);
	  dcc[idx].u.chat->status&=(~STAT_CHAT); dcc[idx].type=DCC_CHAT;
	}
	else {
	  dprintf(idx,"Dropping connection now.\n");
	  putlog(LOG_FILES,"*","Left files: [%s]%s/%d",dcc[idx].nick,
		 dcc[idx].host,dcc[idx].port);
	  close(dcc[idx].sock); lostdcc(idx);
	}
      }
    }
  }
  else if (dcc[idx].type==DCC_SEND) {
    fwrite(buf,len,1,dcc[idx].u.xfer->f);
    dcc[idx].u.xfer->sent+=len;
    netint=htonl(dcc[idx].u.xfer->sent);
    write(dcc[idx].sock,&netint,4);
    dcc[idx].u.xfer->pending=time(NULL);
    if ((dcc[idx].u.xfer->sent > dcc[idx].u.xfer->length) &&
	(dcc[idx].u.xfer->length > 0)) {
      mprintf(serv,"NOTICE %s :Bogus file length.\n",dcc[idx].nick);
      putlog(LOG_FILES,"*","File too long: dropping dcc send %s from %s!%s",
	     dcc[idx].u.xfer->filename,dcc[idx].nick,dcc[idx].host);
      fclose(dcc[idx].u.xfer->f);
      sprintf(s,"%s%s",tempdir,dcc[idx].u.xfer->filename);
      unlink(s); close(dcc[idx].sock);
      lostdcc(idx);
    }
  }
  else if (dcc[idx].type==DCC_GET_PENDING) {
    unsigned long ip; int i;
    i=answer(dcc[idx].sock,s,&ip); close(dcc[idx].sock); 
    dcc[idx].sock=i; dcc[idx].addr=ip;
    if (dcc[idx].sock==-1) {
      neterror(s);
      mprintf(serv,"NOTICE %s :Bad connection (%s)\n",dcc[idx].nick,
	      s);
      putlog(LOG_FILES,"*","DCC bad connection: GET %s (%s!%s)",
	     dcc[idx].u.xfer->filename,dcc[idx].nick,dcc[idx].host);
      lostdcc(idx);
    }
    else {
      /* file was already opened */
      if (dcc[idx].u.xfer->length < dcc_block) 
	dcc[idx].u.xfer->sent=dcc[idx].u.xfer->length;
      else dcc[idx].u.xfer->sent = dcc_block;
      dcc[idx].type=DCC_GET;
      bf=(char *)nmalloc(dcc[idx].u.xfer->sent+1);
      fread(bf,dcc[idx].u.xfer->sent,1,dcc[idx].u.xfer->f);
      write(dcc[idx].sock,bf,dcc[idx].u.xfer->sent);
      nfree(bf); dcc[idx].u.xfer->pending=time(NULL);
      /* leave f open until file transfer is complete */
    }
  }
  else if (dcc[idx].type==DCC_GET) {
    unsigned char bbuf[41];
    /* prepend any previous partial-acks: */
    my_memcpy(bbuf,buf,len);
    if (dcc[idx].u.xfer->sofar) {
      my_memcpy(&dcc[idx].u.xfer->buf[dcc[idx].u.xfer->sofar],bbuf,len);
      my_memcpy(bbuf,dcc[idx].u.xfer->buf,dcc[idx].u.xfer->sofar+len);
      len+=dcc[idx].u.xfer->sofar; dcc[idx].u.xfer->sofar=0;
    }
    /* toss previous pent-up acks */
    while (len>4) { len-=4; my_memcpy(bbuf,&bbuf[4],len); }
    if (len<4) {
      /* not a full answer */
      dcc[idx].u.xfer->sofar=len;
      my_memcpy(dcc[idx].u.xfer->buf,bbuf,len);
    }
    else {
      /* this is more compatable than ntohl for machines where an int */
      /* is more than 4 bytes: */
      cmp=((unsigned int)(bbuf[0])<<24)+((unsigned int)(bbuf[1])<<16)+
	((unsigned int)(bbuf[2])<<8)+bbuf[3];
      if ((cmp>dcc[idx].u.xfer->sent) && (cmp<=dcc[idx].u.xfer->length)) {
	/* attempt to resume I guess */
	fseek(dcc[idx].u.xfer->f,cmp,SEEK_SET);
	dcc[idx].u.xfer->sent=cmp;
	putlog(LOG_FILES,"*","Resuming file transfer at %dk for %s to %s",
	       (int)(cmp/1024),dcc[idx].u.xfer->filename,dcc[idx].nick);
      }
      if (cmp==dcc[idx].u.xfer->sent) {
	if (dcc[idx].u.xfer->sent==dcc[idx].u.xfer->length) {
	  /* successful send, we're done */
	  char xnick[10];
	  close(dcc[idx].sock);
	  fclose(dcc[idx].u.xfer->f);
	  if (strcmp(dcc[idx].nick,"*users")==0) {
	    int x,y=0;
	    putlog(LOG_FILES,"*","Completed userfile transfer.");
	    for (x=0; x<dcc_total; x++)
	      if ((strcasecmp(dcc[x].nick,dcc[idx].host)==0) &&
		  (dcc[x].type==DCC_TANDEM))
		y=x;
	    if (y!=0) dcc[y].u.tand->status&=~STAT_SENDING;
	    unlink(dcc[idx].u.xfer->filename);
	    xnick[0]=0;
	  }
	  else {
	    check_tcl_sent(dcc[idx].u.xfer->from,dcc[idx].nick,
			   dcc[idx].u.xfer->dir);
	    incr_file_gots(dcc[idx].u.xfer->dir);
	    putlog(LOG_FILES,"*","Finished dcc send %s to %s",
		   dcc[idx].u.xfer->filename,dcc[idx].nick);
	    wipe_tmp_file(idx); strcpy(xnick,dcc[idx].nick);
	  }
	  lostdcc(idx);
	  /* any to dequeue? */
	  if (!at_limit(xnick)) send_next_file(xnick);
	}
	else {
	  fseek(dcc[idx].u.xfer->f,dcc[idx].u.xfer->sent,0);
	  l=dcc_block;
	  if (dcc[idx].u.xfer->sent+dcc_block > dcc[idx].u.xfer->length)
	    l=dcc[idx].u.xfer->length - dcc[idx].u.xfer->sent;
	  bf=(char *)nmalloc(l+1); fread(bf,l,1,dcc[idx].u.xfer->f);
	  write(dcc[idx].sock,bf,l); nfree(bf);
	  dcc[idx].u.xfer->sent+=l;
	  dcc[idx].u.xfer->pending=time(NULL);
	}
      }
    }
  }
  else if (dcc[idx].type==DCC_TELNET) {
    unsigned long ip; int i,j;
    i=dcc_total;
    if (i+1>MAXDCC) {
      j=answer(dcc[idx].sock,s,&ip);
      if (j!=-1) {
	tprintf(j,"Sorry, too many connections already.\r\n");
	close(j);
      }
    }
    else {
      dcc[i].sock=answer(dcc[idx].sock,s,&ip);
      while ((dcc[i].sock==(-1)) && (errno==EAGAIN))
	dcc[i].sock=answer(dcc[idx].sock,s,&ip);
      if (dcc[i].sock!=-1) {
	sprintf(dcc[i].host,"telnet!telnet@%s",s);
	if (match_ignore(dcc[i].host)) {
	  tprintf(dcc[i].sock,"\r\nSorry, your site is being ignored.\r\n\n");
	  close(dcc[i].sock);
	}
	else {
	  dcc[i].addr=ip;
	  dcc[i].port=dcc[idx].port;
	  sprintf(dcc[i].host,"telnet from %s",s);
	  dcc[i].type=DCC_TELNET_ID;
	  strcpy(dcc[i].nick,"???");
	  set_chat(i);
	  dcc[i].u.chat->away=NULL;
	  dcc[i].u.chat->status=STAT_TELNET|STAT_ECHO;
	  dcc[i].u.chat->timer=time(NULL);
	  dcc[i].u.chat->msgs_per_sec=0;
	  dcc[i].u.chat->con_flags=0;
	  strcpy(dcc[i].u.chat->con_chan,chanset->name);
	  dcc[i].u.chat->channel=0;   /* party line */
	  tprintf(dcc[i].sock,"\r\n\r\n");
	  telltext(i,"banner",0,0);
	  tprintf(dcc[i].sock,"\r\nPlease enter your nickname.\r\n");
	  if (allow_new_telnets)
	    tprintf(dcc[i].sock,"(If you are new, enter 'NEW' here.)\r\n");
	  dcc_total++;
	  putlog(LOG_MISC,"*","Telnet connection: %s",s);
	}
      }
      else {
	neterror(s1);
	putlog(LOG_MISC,"*","Failed TELNET incoming (%s)",s1);
      }
    }
  }
  else if (dcc[idx].type==DCC_TELNET_ID) {
    int ok=0,atr;
    buf[10]=0;
    atr=get_attr_handle(buf);
    if (atr & USER_OP) {
      if (!require_p) ok=1;
      if ((!require_x) && (dccdir[0])) ok=1;
    }
    if (atr & (USER_MASTER|USER_BOT|USER_PARTY)) ok=1;
    if ((atr&USER_XFER) && (dccdir[0])) ok=1;
    if ((strcasecmp(buf,"NEW")==0) && (allow_new_telnets)) {
      dcc[idx].type=DCC_TELNET_NEW;
      dcc[idx].u.chat->timer=time(NULL);
      tprintf(dcc[idx].sock,"\r\n");
      telltext(idx,"newuser",0,0);
      tprintf(dcc[idx].sock,"\r\nEnter the nickname you would like to use.\r\n");
    }
    else if (!ok) {
      tprintf(dcc[idx].sock,"You don't have access.\r\n");
      putlog(LOG_TAND,"*","Refused %s (invalid handle: %s)",
	  dcc[idx].host,buf);
      close(dcc[idx].sock); lostdcc(idx);
    }
    else if ((atr & USER_BOT) && (in_chain(buf))) {
      tprintf(dcc[idx].sock,"error Already connected.\n");
      putlog(LOG_TAND,"*","Refused telnet connection from %s (duplicate)",buf);
      close(dcc[idx].sock); lostdcc(idx);
    }
    else if ((atr & USER_BOT) && (!online)) {
      tprintf(dcc[idx].sock,"error Not accepting links yet.\n");
      putlog(LOG_TAND,"*","Refused telnet connection from %s (premature)",buf);
      close(dcc[idx].sock); lostdcc(idx);
    }
    else if (pass_match_by_handle("nopass",buf)) {
      if (atr & USER_BOT) {
	char ps[20];
	makepass(ps); change_pass_by_handle(buf,ps);
	correct_handle(buf);
	strcpy(dcc[idx].nick,buf);
	nfree(dcc[idx].u.chat);
	set_tand(idx);
	dcc[idx].u.tand->status=STAT_CALLED;
	dcc[idx].u.tand->timer=time(NULL);
	dcc[idx].u.tand->version[0]=0;
	if (atr & BOT_LEAF) dcc[idx].u.tand->status|=BOT_LEAF;
	dcc[idx].type=DCC_TANDEM;
	tprintf(dcc[idx].sock,"*hello!\n");
	tprintf(dcc[idx].sock,"thisbot %s\n",origbotname);
	tprintf(dcc[idx].sock,"version %s\n",ver);
	tprintf(dcc[idx].sock,"handshake %s\n",ps);
	stop_auto(dcc[idx].nick);
	putlog(LOG_TAND,"*","Linked to %s",dcc[idx].nick);
	chatout("*** Linked to %s\n",dcc[idx].nick);
	tandout_but(idx,"nlinked %s %s\n",dcc[idx].nick,origbotname);
	tandout_but(idx,"chat %s Linked to %s\n",origbotname,dcc[idx].nick);
	dump_links(dcc[idx].sock); addbot(dcc[idx].nick,dcc[idx].nick);
	dcc[idx].type=DCC_TANDEM;
	check_tcl_link(dcc[idx].nick,dcc[idx].nick);
      }
      else {
	tprintf(dcc[idx].sock,"Can't telnet until you have a password set.\r\n");
	putlog(LOG_MISC,"*","Refused [%s]%s (no password)",buf,
	    dcc[idx].host);
	close(dcc[idx].sock); lostdcc(idx);
      }
    }
    else {
      int ok=0;
      dcc[idx].type=DCC_CHAT_PASS;
      dcc[idx].u.chat->timer=time(NULL);
      if (atr & USER_XFER) dcc[idx].u.chat->status|=STAT_XFER;
      if (atr & USER_MASTER) {
	dcc[idx].u.chat->status|=STAT_MASTER; ok=1;
      }
      else if (atr & USER_OP) {
	if (!require_p) ok=1;
	else if (atr & USER_PARTY) ok=1;
      }
      else if (atr & USER_PARTY) {
	ok=1; dcc[idx].u.chat->status|=STAT_PARTY;
      }
      if (atr & USER_BOT) ok=1;
      if (!ok) {
	set_files(idx);
	dcc[idx].type=DCC_FILES_PASS;
      }
      correct_handle(buf);
      strcpy(dcc[idx].nick,buf);
      if (atr & USER_BOT)
	tprintf(dcc[idx].sock,"passreq\n");
      else {
	dprintf(idx,"\nEnter your password.\377\373\001\n");
	/* turn off remote telnet echo: IAC WILL ECHO */
      }
    }
  }
  else if (dcc[idx].type==DCC_RELAY) {
    int j;
    for (j=0; (dcc[j].sock!=dcc[idx].u.relay->sock) ||
	 (dcc[j].type!=DCC_RELAYING); j++);
    /* if redirecting to a non-telnet user, swallow telnet codes */
    if (!(dcc[j].u.relay->chat->status&STAT_TELNET)) {
      swallow_telnet_codes(buf);
      if (!buf[0]) tprintf(dcc[idx].u.relay->sock," \n");
      else tprintf(dcc[idx].u.relay->sock,"%s\n",buf);
    }
    else {   /* telnet user */
      if (!buf[0]) tprintf(dcc[idx].u.relay->sock," \r\n");
      else tprintf(dcc[idx].u.relay->sock,"%s\r\n",buf);
    }
  }
  else if (dcc[idx].type==DCC_RELAYING) {
    int j;
    if (strcasecmp(buf,"*BYE*")==0) {
      struct chat_info *ci;
      for (j=0; (dcc[j].sock!=dcc[idx].u.relay->sock) ||
	   (dcc[j].type!=DCC_RELAY); j++);
      /* in case echo was off, turn it back on: */
      if (dcc[idx].u.relay->chat->status&STAT_TELNET)
	tprintf(dcc[idx].sock,"\377\374\001\n");
      dprintf(idx,"\n(Breaking connection to %s.)\n",dcc[j].nick);
      dprintf(idx,"You are now back on %s.\n\n",origbotname);
      putlog(LOG_MISC,"*","Relay broken: %s -> %s",dcc[idx].nick,dcc[j].nick);
      if (dcc[idx].u.relay->chat->channel >= 0)
	chanout2(dcc[idx].u.relay->chat->channel,
		 "%s joined the party line.\n",dcc[idx].nick);
      ci=dcc[idx].u.relay->chat; nfree(dcc[idx].u.relay);
      dcc[idx].u.chat=ci;
      dcc[idx].type=DCC_CHAT;
      notes_read(dcc[idx].nick,"",-1,idx);
      close(dcc[j].sock); lostdcc(j);
    }
    else tprintf(dcc[idx].u.relay->sock,"%s\n",buf);
  }
  else if (dcc[idx].type==DCC_TELNET_NEW) {
    int x,ok=1;
    buf[9]=0;
    strcpy(dcc[idx].nick,buf);
    dcc[idx].u.chat->timer=time(NULL);
    for (x=0; x<strlen(buf); x++)
      if ((buf[x]<=32) || (buf[x]>=127)) ok=0;
    if (!ok) {
      dprintf(idx,"\nYou can't use weird symbols in your nick.\n");
      dprintf(idx,"Try another one please:\n");
    }
    else if (is_user(buf)) {
      dprintf(idx,"\nSorry, that nickname is taken already.\n");
      dprintf(idx,"Try another one please:\n");
    }
    else {
      userlist=adduser(userlist,buf,"none","nopass",USER_PARTY|default_flags);
      dcc[idx].u.chat->status=STAT_ECHO;
      dcc[idx].type=DCC_CHAT;   /* just so next line will work */
      check_dcc_attrs(buf,USER_PARTY|default_flags,USER_PARTY|default_flags);
      dcc[idx].type=DCC_TELNET_PW;
      dprintf(idx,"\nOkay, now choose and enter a password:\n");
      dprintf(idx,"(Only the first 9 letters are significant.)\n");
    }
  }
  else if (dcc[idx].type==DCC_TELNET_PW) {
    buf[10]=0;
    putlog(LOG_MISC,"*","New user via telnet: [%s]%s/%d",dcc[idx].nick,
	   dcc[idx].host,dcc[idx].port);
    if (notify_new[0]) {
      char s[121],s1[121],*p1;
      sprintf(s,"Introduced to %s, %s",dcc[idx].nick,dcc[idx].host);
      strcpy(s1,notify_new); while (s1[0]) {
	p1=strchr(s1,','); if (p1!=NULL) { *p1=0; p1++; rmspace(p1); }
	rmspace(s1); add_note(s1,origbotname,s,-1,0);
	if (p1==NULL) s1[0]=0; else strcpy(s1,p1);
      }
    }
    change_pass_by_handle(dcc[idx].nick,buf);
    dprintf(idx,"\nRemember that!  You'll need it next time you log in.\n");
    dprintf(idx,"You now have an account on %s...\n\n\n",origbotname);
    dcc[idx].type=DCC_CHAT;
    dcc_chatter(idx);
  }
  else if (dcc[idx].type==DCC_SCRIPT) {
    if (buf[0]) {
      set_tcl_vars();
      if (call_tcl_func(dcc[idx].u.script->command,dcc[idx].sock,buf)) {
	void *old;
	old=dcc[idx].u.script->u.other;
	dcc[idx].type=dcc[idx].u.script->type;
	nfree(dcc[idx].u.script);
	dcc[idx].u.other=old;
	notes_read(dcc[idx].nick,"",-1,idx);
	if ((dcc[idx].type==DCC_CHAT) && (dcc[idx].u.chat->channel >= 0))
	  chanout2(dcc[idx].u.chat->channel,"%s has joined the party line.\n",
		   dcc[idx].nick);
      }
    }
  }
}

/* eof from dcc goes here from I/O... */
void eof_dcc(z)
int z;
{
  int idx; char s[121];
  context;
  for (idx=0; (dcc[idx].sock!=z) || (dcc[idx].type==DCC_FORK) ||
       (dcc[idx].type==DCC_LOST); idx++);
  if (idx>=dcc_total) {
    putlog(LOG_MISC,"*","(@) EOF socket %d, not a dcc socket, not anything.",
	   z);
    return;
  }
  if (dcc[idx].type==DCC_SCRIPT) {
    void *old;
    /* tell the script they're gone: */
    call_tcl_func(dcc[idx].u.script->command,dcc[idx].sock,"");
    old=dcc[idx].u.script->u.other;
    dcc[idx].type=dcc[idx].u.script->type;
    nfree(dcc[idx].u.script);
    dcc[idx].u.other=old;
    /* then let it fall thru to the real one */
  }
  if ((dcc[idx].type==DCC_CHAT) || (dcc[idx].type==DCC_CHAT_PASS) ||
      (dcc[idx].type==DCC_FILES) || (dcc[idx].type==DCC_FILES_PASS)) {
    if (dcc[idx].type==DCC_CHAT) dcc[idx].u.chat->con_flags=0;
    putlog(LOG_MISC,"*","Lost dcc connection to %s!%s/%d",dcc[idx].nick,
	   dcc[idx].host,dcc[idx].port);
    if (dcc[idx].type==DCC_CHAT) {
      if (dcc[idx].u.chat->channel>=0)
	chanout2_but(idx,dcc[idx].u.chat->channel,"%s lost dcc link.\n",
		     dcc[idx].nick);
      check_tcl_chof(dcc[idx].nick,dcc[idx].sock);
    }
    close(z); lostdcc(idx);
  }
  else if (dcc[idx].type==DCC_TANDEM) {
    putlog(LOG_TAND,"*","Lost bot: %s",dcc[idx].nick,dcc[idx].port);
    tandout_but(idx,"chat %s Lost bot: %s",origbotname,dcc[idx].nick);
    tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
    rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
    cancel_user_xfer(idx);
    close(z); lostdcc(idx);
  }
  else if (dcc[idx].type==DCC_TANDEM_NEW) {
    putlog(LOG_TAND,"*","Lost bot: %s",dcc[idx].nick,dcc[idx].port);
    close(z); lostdcc(idx);
  }
  else if (dcc[idx].type==DCC_TELNET_ID) {
    putlog(LOG_MISC,"*","Lost telnet connection to %s/%d",dcc[idx].host,
	dcc[idx].port);
    close(z); lostdcc(idx);
  }
  else if (dcc[idx].type==DCC_SEND) {
    if (dcc[idx].u.xfer->length==dcc[idx].u.xfer->sent) {
      /* success */
      int ok=0,j; char ofn[121],nfn[121],hand[41],s[161];
      fclose(dcc[idx].u.xfer->f);
      if (strcmp(dcc[idx].nick,"*users")==0) {
	finish_share(idx);
      }
      else {
	putlog(LOG_FILES,"*","Completed dcc send %s from %s!%s",
	       dcc[idx].u.xfer->filename,dcc[idx].nick,dcc[idx].host);
	sprintf(s,"%s!%s",dcc[idx].nick,dcc[idx].host);
	get_handle_by_host(hand,s);
	/* move the file from /tmp */
	sprintf(ofn,"%s%s",tempdir,dcc[idx].u.xfer->filename);
	sprintf(nfn,"%s%s",dcc[idx].u.xfer->dir,dcc[idx].u.xfer->filename);
	if (movefile(ofn,nfn))
	  putlog(LOG_MISC|LOG_FILES,"*","FAILED move %s from %s ! File lost!",
		 dcc[idx].u.xfer->filename,tempdir);
	else {
	  /* add to file database */
	  add_file(dcc[idx].u.xfer->dir,dcc[idx].u.xfer->filename,hand);
	  check_tcl_rcvd(hand,dcc[idx].nick,nfn);
	}
	for (j=0; j<dcc_total; j++)
	  if ((!ok) && ((dcc[j].type==DCC_CHAT) || (dcc[j].type==DCC_FILES)) &&
	      (strcasecmp(dcc[j].nick,hand)==0)) {
	    ok=1;
	    dprintf(j,"Thanks for the file!\n");
	  }
	if (!ok) mprintf(serv,"NOTICE %s :Thanks for the file!\n",
			 dcc[idx].nick);
      }
      close(z); lostdcc(idx);
    }
    else {
      /* failure */
      fclose(dcc[idx].u.xfer->f);
      if (strcmp(dcc[idx].nick,"*users")==0) {
	int x,y=0;
	for (x=0; x<dcc_total; x++)
	  if ((strcasecmp(dcc[x].nick,dcc[idx].host)==0) &&
	      (dcc[x].type==DCC_TANDEM))
	    y=x;
	if (y!=0) {
	  dcc[y].u.tand->status&=~STAT_GETTING;
	  dcc[y].u.tand->status&=~STAT_SHARE;
	}
	putlog(LOG_MISC,"*","Lost userfile transfer; aborting.");
	unlink(dcc[idx].u.xfer->filename);
      }
      else {
	putlog(LOG_FILES,"*","Lost dcc send %s from %s!%s (%lu/%lu)",
	       dcc[idx].u.xfer->filename,dcc[idx].nick,dcc[idx].host,
	       dcc[idx].u.xfer->sent,dcc[idx].u.xfer->length);
	sprintf(s,"%s%s",tempdir,dcc[idx].u.xfer->filename); unlink(s);
      }
      close(z); lostdcc(idx);
    }
  }
  else if ((dcc[idx].type==DCC_GET_PENDING) || (dcc[idx].type==DCC_GET)) {
    char xnick[10];
    fclose(dcc[idx].u.xfer->f);
    if (strcmp(dcc[idx].nick,"*users")==0) {
      int x,y=0;
      for (x=0; x<dcc_total; x++)
	if ((strcasecmp(dcc[x].nick,dcc[idx].host)==0) &&
	    (dcc[x].type==DCC_TANDEM))
	  y=x;
      if (y!=0) {
	dcc[y].u.tand->status&=~STAT_SENDING;
	dcc[y].u.tand->status&=~STAT_SHARE;
      }
      putlog(LOG_MISC,"*","Lost userfile transfer; aborting.");
      unlink(dcc[idx].u.xfer->filename); xnick[0]=0;
    } 
    else {
      putlog(LOG_FILES,"*","Lost dcc get %s from %s!%s",
	     dcc[idx].u.xfer->filename,dcc[idx].nick,dcc[idx].host);
      wipe_tmp_file(idx); strcpy(xnick,dcc[idx].nick);
    }
    close(z); lostdcc(idx);
    /* send next queued file if there is one */
    if (!at_limit(xnick)) send_next_file(xnick);
  }
  else if (dcc[idx].type==DCC_RELAY) {
    int j; struct chat_info *ci;
    for (j=0; dcc[j].sock!=dcc[idx].u.relay->sock; j++);
    /* in case echo was off, turn it back on: */
    if (dcc[idx].u.relay->chat->status&STAT_TELNET)
      tprintf(dcc[idx].sock,"\377\374\001\n");
    putlog(LOG_MISC,"*","Ended relay link: %s -> %s",dcc[j].nick,
	   dcc[idx].nick);
    dprintf(j,"\n\n*** RELAY CONNECTION DROPPED.\n");
    dprintf(j,"You are now back on %s.\n",origbotname);
    if (dcc[j].u.chat->channel>=0)
      chanout2(dcc[j].u.relay->chat->channel,"%s rejoined the party line.\n",
	       dcc[j].nick);
    ci=dcc[j].u.relay->chat; nfree(dcc[j].u.relay);
    dcc[j].u.chat=ci; dcc[j].type=DCC_CHAT;
    notes_read(dcc[j].nick,"",-1,j);
    close(dcc[idx].sock); lostdcc(idx);
  }
  else if (dcc[idx].type==DCC_RELAYING) {
    int j,x=dcc[idx].u.relay->sock;
    putlog(LOG_MISC,"*","Lost dcc connection to [%s]%s/%d",dcc[idx].nick,
	   dcc[idx].host,dcc[idx].port);
    close(dcc[idx].sock); lostdcc(idx);
    for (j=0; (dcc[j].sock!=x) || (dcc[j].type==DCC_FORK) ||
	 (dcc[j].type==DCC_LOST); j++);
    putlog(LOG_MISC,"*","(Dropping relay link to %s)",dcc[j].nick);
    close(dcc[j].sock);
    lostdcc(j);   /* drop connection to the bot */
  }
  else if (dcc[idx].type==DCC_TELNET_NEW) {
    putlog(LOG_MISC,"*","Lost new telnet user (%s/%d)",dcc[idx].host,
	   dcc[idx].port);
    close(dcc[idx].sock); lostdcc(idx);
  }
  else if (dcc[idx].type==DCC_TELNET_PW) {
    putlog(LOG_MISC,"*","Lost new telnet user %s (%s/%d)",dcc[idx].nick,
	   dcc[idx].host,dcc[idx].port);
    deluser(dcc[idx].nick);
    close(dcc[idx].sock); lostdcc(idx);
  }
  else {
    putlog(LOG_MISC,"*","*** ATTENTION: DEAD SOCKET (%d) OF TYPE %d UNTRAPPED",
	   z,dcc[idx].type);
    close(z); lostdcc(idx);
  }
}
