/*
	D C C  & C T C P
*/


/* ctcp.c	- CTCP and some DCC protocol
		see also: dcc.c

    Author: Tano Fotang, 1997
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License , or
    (at your option) any later version.
    see the file COPYING for more info.
    -------------------------------------------------------
    ctcp_quote_it(),
    ctcp_unquote_it(),
    decrypt() and encrypt() are from the IrcII source code ,
    Written By Michael Sandrof and Copyright(c) 1990. GNU GPL.
    slighty modified.
    ----------------------------------------------------------

*/

#include <forms.h>
#if 1
#include <sys/utsname.h>  /* #define's UT_NAMESIZE etc for linux */
#else
#include <sys/types.h> 	/* others use this... */
#endif
#include <pwd.h>
#include <utmp.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "config.h"
#include "aux.h"
#include "sula.h"
#include "chanwin.h"
#include "script.h"
#include "global.h"
#include "parser.h"
#include "dcc.h"
#include "setting.h"
#include "ignore.h"
#include "cmd.h"

extern Winstruct *Win;/* parser window */

/************************************************/
/*	c t c p					*/
/************************************************/


/* taken f r o m   ircII  for compatibility */
#define CTCP_QUOTE_CHAR '\\'
#define CTCP_DELIM_CHAR '\001'
char *ctcp_unquote_it(char *,int *len);
static	void decrypt(char *,int,char *);
/*-----------------*/


static void addkey(Winstruct *win, Key **s, char *nick, char *key){
	Key *p=*s;
	char *buf=NULL;
	while(p)
		if(!strcasecmp(p->who, nick)){
			free(p->key);
			p->key=strdup(key);
			p->flag=1;
			buf=malloc(sizeof(char)*(strlen(p->key)+strlen(p->who)+30));
			sprintf(buf,"@ikey for %s changed to \"%s\"", p->who, p->key);
			break;
		}else p=p->next;
	if(p) return;
	else{
	
	p=*s;
	*s=(Key *)malloc(sizeof(Key));
	(*s)->next=p;
	(*s)->who=strdup(nick);
	(*s)->key=strdup(key);
	(*s)->flag=1;
	buf=malloc(sizeof(char)*(strlen((*s)->key)+strlen((*s)->who)+40));
	sprintf(buf,"@i%s added to crypt list with key \"%s\"", (*s)->who, (*s)->key);
	}
	if(buf) fl_addto_browser(win->chanwin->brow2,buf);
	free(buf);
}
static void del_key_node(Key **head, Key *trail, Key *node){
	if(trail) trail->next=node->next;
	else{
	#warning uncomment and  test this now!!!!
		/*free((*head)->nick);free((*head)->key);*/
		*head=(*head)->next;
		free(node);
	}
} 
static void remove_key(Winstruct *win, char *who){
  Key *p=keys;
  if(p){
	char *buf=NULL, found=0;
	if(!strcasecmp(p->who,who)){
		keys=keys->next;
		free(p);
		found++;
	}else{
	Key *q=p->next;
	while(q)
		if(!strcasecmp(q->who,who)){
			del_key_node(&(keys), p, q);
			found++;
			break;
		}else{ p=q; q=q->next; }
	}
	if(!found) return;
	buf=malloc(sizeof(char)*(strlen(who)+32));
	sprintf(buf,"@i%s removed from crypt list", who);
	fl_addto_browser(win->chanwin->brow2,buf);
	free(buf);
  }
}
static void toggle_sed_flag(Winstruct *win, char *who){
	Key *p=keys;
	char *buf=NULL;
	while(p)
		if(!strcasecmp(p->who, who)){
			p->flag=p->flag?0:1;
			buf=malloc(sizeof(char)*(strlen(p->who)+35));
			sprintf(buf,"@iencryption to %s now set %s",
			 p->who, p->flag?"on":"off");
			fl_addto_browser(win->chanwin->brow2,buf);
			free(buf);
			break;
		}
		else p=p->next;

}

		
char *have_key(char *who, int action){
	Key *p=keys;
	while(p)
		if(!strcasecmp(p->who, who)){
			if(action==ENCRYPT && p->flag==0) return NULL;
			else return p->key;
		}
		else p=p->next;
	return NULL;
}

extern void do_encrypt(int argc, char **argv, Winstruct * win)
{
/*
kludge!!
*/
	
	if(argc==1){
		char buf[256];
		Key *p=keys;
		if(p){
		sprintf(buf,"@b@f  %-20s%-20s  %s","User/Channel","Key","Status");
		 fl_addto_browser(win->chanwin->brow2,buf);
		 }
		while(p){
			sprintf(buf,"@f  %-20s%-20s  %s", p->who, p->key,
			p->flag?"on":"off");
			p=p->next;
			fl_addto_browser(win->chanwin->brow2,buf);
		}
		return;
	}
	if (!strncasecmp(argv[1], "-help", 2)) {
		show_std_help(uppercase(argv[0]), NULL, win->chanwin);
		return;
	}
	if(argc>1)
		if(!strncasecmp(argv[1], "-del", 4) && argv[2] )
			remove_key(win,argv[2]);
		else if(argc==2)
			toggle_sed_flag(win, argv[1]);
		else if(argc>2)
			addkey(win, &keys, argv[1], argv[2]);
}


static void do_sed(void *junk){

      	char *buf=NULL;
	char *nick=NULL, *dest=NULL, *login=NULL, *host=NULL,*msg=NULL;
	char *sender=NULL;
	size_t ll, n;
	char *key=0;
	char *ptr=0;

	 if(check_msg(SED,Msg)) return;
	 ll=strlen(Msg);

	 sender=(char*)malloc(sizeof(char)*ll);
	 if(!sender) return;
	 sscanf(Msg,"%s", sender);
	 msg=(char*)malloc(sizeof(char)*ll);
	 dest= (char*)malloc(sizeof(char)*ll);
	if(sscanf(Msg,"%*s PRIVMSG %s :\x1%[^\x1\n]\x1\n",dest, msg)<2){
		if(gflags & VERBOSE_CTCP)
			err_msg("invalid CTCP sed");
		return;
	}
	ptr=msg;
	ptr+=4; /* skip "SED " */
	/*while(*ptr && *ptr!=0x05) ptr++;*/
	n=strlen(ptr);
	buf=ctcp_unquote_it(ptr,&n);
	
	ll=strlen(sender);
	nick=(char*)malloc(sizeof(char)*ll);
	login=(char*)malloc(sizeof(char)*ll);
	host=(char*)malloc(sizeof(char)*ll);
	if(sscanf(sender,"%[^!]!%[^@]@%s", nick, login,host)<3)
		goto done;
	key=have_key( (*dest=='#' || *dest=='&') ? dest:nick, DECRYPT);
	if(key){
		decrypt(buf, n, key);
		free(msg);
		msg=strdup(buf);
		ptr=(*dest=='#' || *dest=='&')?fmt_sed_pub:fmt_sed_priv;
	}
	else
		ptr=fmt_sed_nokey;
	fmt_string="%n%l%h%c%p%z%Z$";
	
	buf=format_msg( 
		ptr, nick, login,host,dest,key? msg:msg+4,
		 strlen(Msg), Win->server->read);
	if(buf){
		 fl_addto_browser(Win->chanwin->browser, buf);
		 free(buf);
	}
	done:
	free(msg); free(dest); free(login); free(host);
	if(sflags&S_SED) addto_sender(sender, SS_SED);
	 free(nick);
	 free(sender);
}

static void do_action(void *data){

      	char *buf;
	char *nick, *dest, *login, *host,*msg;
	char *sender;
	size_t ll;
	 int i;

	 if(check_msg(ACTION,Msg)) return;
	 ll=strlen(Msg);

	 sender=(char*)malloc(sizeof(char)*ll);
	 if(!sender) return;
	 sscanf(Msg,"%s", sender);
	 if(!(*sender)) return;
	 msg=(char*)malloc(sizeof(char)*ll);
	 dest= (char*)malloc(sizeof(char)*ll);
	 ll=strlen(sender);
	 nick=(char*)malloc(sizeof(char)*ll);
	login=(char*)malloc(sizeof(char)*ll);
	host=(char*)malloc(sizeof(char)*ll);
	if(!nick  || !login || !host || !msg || !dest) return;
	if(sscanf(Msg,"%*s PRIVMSG %s :\x1%*s %[^\x1\r\n]\x1\r\n",dest, msg)<2){
		if(gflags & VERBOSE_CTCP)
			berr_msg("invalid CTCP action str: %s",Msg);
		return;
	}

	if(!clean(msg)) return;
	i=sscanf(sender,"%[^!]!%[^@]@%s", nick, login,host);
	if(i<2){
		free(login);login=strdup("<unknown>"); nick=sender;
	}else
	if(i<3) {free(host);host=strdup("<unknown>");}
	fmt_string="%n%l%h%c%p%z%Z$";
	buf=format_msg( 
		(*dest=='#' || *dest=='&')?fmt_pub_action:fmt_priv_action, 
		nick, login,host,dest, msg, strlen(Msg), Win->server->read);
	if(buf) fl_addto_browser(Win->chanwin->browser, buf);
	free(buf);free(msg); free(dest); free(login); free(host);
	if(sflags&S_ACTION) addto_sender(sender, SS_ACTION);
	 free(sender);free(nick);

}

static void do_clientinfo(void *data){
	char *s, buf[512];
	char *nick;
	if(check_msg(CLIENTINFO,Msg)) return;
	nick=(char*)malloc(sizeof(char)*strlen(Msg));
	if(!nick) return;
	if( sscanf(Msg,"%[^!]!", nick)<1) return;
	s=(char *)malloc(sizeof(char)*(strlen(sula_GETTING)+
	strlen(sula_INFOSITE)+strlen(sula_EMAIL)+200));
	if(!s) return;
	sprintf(s,"CLIENTINFO sula - a multiserver X client \n"
	"Latest versions: "sula_GETTING" \n"
	"Infoterial: "sula_INFOSITE" \n"
	"Email: "sula_EMAIL);
	quote_str(&s);
	sprintf(buf,"NOTICE %s :%c%s%c\n", nick, 0x01, s, 0x01);
	sendto_server(Win,buf);
	free(s);
	/*if(sflags&S_CTCP_OTHER) addto_sender(sender, SS_CTCP_OTHER);*/
	 free(nick);
}
#define DCC_CHAT	0
#define DCC_SEND	1
#define DCC_ACCEPT	2
#define DCC_RESUME	3
#define DCC_RENAME	4
#define DCC_REJECT	5

static void parse_dcc_request(int which){

	  char *nick, *pemail, *email,*msg;
	  int ll;
	  unsigned short port=0;

	ll=strlen(Msg);
	msg=(char*)malloc(sizeof(char)*ll);
	sscanf(Msg,"%*s PRIVMSG %*s :\x1%*s %*s %[^\x1\r\n]\x1\r\n", msg);
	
	pemail=(char*)malloc(sizeof(char)*ll);
	nick=(char*)malloc(sizeof(char)*ll);
	if(sscanf(Msg, "%[^!]!%s",nick, pemail)<2){
		free(nick);free(pemail);
		nick=strdup("#unknown#");pemail=strdup("#unknown#");}
	email=pemail;
	if(*email=='~') email++;
  switch(which){
	case DCC_SEND:
		if(!check_msg(DCC_SEND_REQUEST,Msg))
	{
       		char *fname, *ext_block;
		/*CheckSum*/ u_long	cksum=0;
		u_long inaddr=0;
		u_long fsize=0;

		ext_block=(char*)malloc(sizeof(char)*strlen(msg));
		*ext_block=0;
		fname= (char*)malloc(sizeof(char)*strlen(msg));
		*fname=0;
		sscanf(msg, "%s %lu %hu %lu", 
			fname, &inaddr, &port, &fsize);
		if(*fname==0 || port==0 ){ 
			if(gflags & VERBOSE_CTCP)
				err_msg("invalid DCC SEND Req. %s from %s\n",msg,nick);
			break;
		}
		/* read extension block*/
		sscanf(Msg,"%*[^\x1]\x1%*[^\x1]\x1\x2%[^\x2]\x2", ext_block);
		fprintf(stderr, "DCC SEND: extension block:%s (%d bytes)\n",
			ext_block,strlen(ext_block));
		if(*ext_block) sscanf(ext_block,"%*s %lu", &cksum);
		if(sflags&S_FILESEND) addto_sender(nextstr(msg,1),SS_FILESEND);
		receive_send_req(nick, email, fname, inaddr, port, fsize,cksum);
		free(fname);
		free(ext_block);
		break;
	  }
	  case DCC_ACCEPT:
	  {
		off_t offset=0;
		/* offset is redundant: open in O_APPEND mode */
		sscanf(msg, "%*s %hu %lu", &port, &offset);
		if(port==0){
		   if(gflags & VERBOSE_CTCP)
			err_msg("incomplete req. from %s:%s\n",msg,nick);
		}else
		accept_request(nick,email,port,offset);
		break;
	  }
	  case DCC_RESUME:
	  {
	  	off_t offset=0;
		u_long checksum=0;
		#if 1
		char *pp=malloc(sizeof(char)*ll), *ss=0;
		sscanf(msg,"%s %hu %lu", pp, &port, &offset);
		checksum=strtoul(pp,&ss,0);
		if(*ss) checksum=0; 
		free(pp);
		#else
		sscanf(msg,"%lu %hu %lu", &checksum, &port, &offset);
		#endif
		if(port) resume_request(nick,email,port,offset,checksum);
		else if(gflags & VERBOSE_CTCP)
			err_msg("incomplete req. from %s:%s\n",msg,nick);
		break;
	  }
	  case DCC_RENAME:
	  {
		sscanf(msg, "%*s %hu", &port);
		if(port==0){
		   if(gflags & VERBOSE_CTCP)
			err_msg("incomplete req. from %s:%s\n",msg,nick);
		}else
		rename_request(nick,email,port);
		break;
	  }
	  case DCC_REJECT:
	  {
	  	sscanf(msg, "%*s %hu", &port);
		reject_request(nick,email,port);
		break;
	  }
	  case DCC_CHAT:
	  	if(!check_msg(DCC_CHAT_REQUEST,Msg))
	  {
		u_long inaddr;
		
		if(sscanf(msg, "%*s %lu %hu", &inaddr, &port)<2){
			if(gflags & VERBOSE_CTCP)
				err_msg("bad DCC CHAT Req. %s\n",msg);
			break;
		}
		if(sflags&S_FILEGET) addto_sender(nextstr(msg,1), SS_FILEGET);
		receive_chat_req(nick, email, inaddr, port);
		break;
	   }
	   default:
	   	if(gflags & VERBOSE_CTCP)
	   	err_msg("unimplemented DCC type %d\n",which);
		break;
	}/* swicth(which)*/
 	free(msg); free(nick); free(pemail);
}

static int lookup_DCC_type(const char *name)
{
   typedef struct TDCC_request{
	short nr;
	char *name;
   }DCC_request;
   DCC_request request_type[]={
   /* binary search, keep sorted by name!! */
	{DCC_ACCEPT, "ACCEPT"},
	{DCC_CHAT, "CHAT"},
	{DCC_REJECT,"REJECT"},
	{DCC_RENAME,"RENAME"},
	{DCC_RESUME,"RESUME"},
	{DCC_SEND, "SEND"},
	{-1,NULL}
   }, *p=request_type;
	 register int low, high, mid, cond;
	 low = 0; high = sizeof(request_type)/sizeof(DCC_request) - 1;
    while (p->name && low <= high) {
	 mid = (low + high) / 2;
	 if ((cond = strcasecmp(name,(p + mid)->name)) < 0)
	 	high = mid - 1;
	 else
	 if (cond > 0)
	 	low = mid + 1;
	 else{
	 	parse_dcc_request((p + mid)->nr);
		return mid;
	 }
   }
	 return -1;
}
 



static void do_dcc(void *data){

	char *cmd,*msg;
	  int ll;

	ll=strlen(Msg);
	msg=(char*)malloc(sizeof(char)*ll);
	cmd= (char*)malloc(sizeof(char)*ll);
	ll=sscanf(Msg,"%*s PRIVMSG %*s :\x1%*s %s %[^\x1\r\n]\x1\r\n",
	cmd, msg);
	free(msg);
	if(ll<2)
	{
		if(gflags & VERBOSE_CTCP)
			err_msg("invalid DCC: %s",Msg);
	}
	else
	if(lookup_DCC_type(cmd)<0)
	   	if(gflags & VERBOSE_CTCP)
	   	err_msg("unknown DCC cmd %s (%s)\n",cmd,Msg);
 	free(cmd);
}

static void do_ping(void *data){
	
	if(check_msg(CTCP_PING,Msg)) return;
	else{
	char s[256];
	char *nick=(char*)malloc(sizeof(char)*strlen(Msg));
	char *buf=(char*)malloc(sizeof(char)*strlen(Msg));
	if(sscanf(Msg,"%[^!]!%*s %*s %*s :\x1%*s %[^\x1]\x1",nick,buf)<2)
		return;
	sprintf(s,"NOTICE %s :%cPING %s%c\r\n", nick,0x01,buf,0x01);
	
	sendto_server(Win,s);
	if(sflags&S_PING) addto_sender(nextstr(Msg,1), SS_PING);
	free(buf); free(nick);
	}

}

static void do_time(void *data){
	char s[256];
	char *nick;
	time_t p;
	char *t;
	if(check_msg(CTCP_TIME,Msg)) return;
	nick=(char*)malloc(sizeof(char)*strlen(Msg));
	if(!nick) return;
	p= time(NULL);
	t=ctime (&p);
	if( sscanf(Msg,"%[^!]!", nick)<1) return;
	*(t+strlen(t)-1)='\0';
	sprintf(s,"NOTICE %s :%cTIME %s%c\r\n", nick,0x01,t,0x01);
	if(sflags&S_CTCP_OTHER) addto_sender(nextstr(Msg,1), SS_CTCP_OTHER);
	sendto_server(Win,s);
	free(nick);
}

static void do_version(void *data){
	char *s=NULL, buf[512];
	char *nick=NULL;
	struct utsname ut;

	if(check_msg(VERSION,Msg)) return;
	nick=(char*)malloc(sizeof(char)*strlen(Msg));
	if(!nick) return;
	if( sscanf(Msg,"%[^!]!", nick)<1) return;
	if(uname(&ut)<0){
		s=(char *)malloc(sizeof(char)*(strlen(sula_DESCR)+strlen(sula_VERSION)+
		strlen(sula_RELEASE)+strlen(Win->server->nick)+100));
		sprintf(s,"VERSION %s, version %s (Release %s) \nDo"
		" \"CTCP %s HELP\" for more info. \n", 
		sula_DESCR, sula_VERSION, sula_RELEASE,Win->server->nick);
	}
	else{
		s=(char *)malloc(sizeof(char)*(strlen(sula_DESCR)+strlen(sula_VERSION)+
		strlen(Win->server->nick) +strlen(ut.sysname)+strlen(ut.release)+100));
		if(!s) return;
		sprintf(s,"VERSION %s, version %s on %s %s. \nDo"
		" \"CTCP %s HELP\" for more info. \n",
		sula_DESCR, sula_VERSION, ut.sysname, ut.release, Win->server->nick);
	}
	quote_str(&s);
	sprintf(buf,"NOTICE %s :%c%s%c\n",nick,0x01,s,0x01);
	if(sflags&S_CTCP_OTHER) addto_sender(nextstr(Msg,1),SS_CTCP_OTHER);
	sendto_server(Win,buf);
	free(s); free(nick);

}

static void do_ctcp_help(void *data){
	if(!check_msg(CTCP_HELP,Msg)){

	char s[MAXLINE], *buf;
	static void listcmds(Cmd *p, char **buf, int flag);
	char *nick=(char*)malloc(sizeof(char)*strlen(Msg));
	char *cmd=(char*)malloc(sizeof(char)*strlen(Msg));

	if(!nick) return;
	s[0]='\0';
	if( sscanf(Msg,"%[^!]!", nick)<1) return;
	if( sscanf(Msg, "%*s %*s %*s %*s %[^\x1]\x1",cmd)==1){
		Cmd *p;
		
		cmd=strtok(cmd, " \r\n");
		p=find_cmd(cmd, CMD_CTCP);
		if(!p)
		sprintf(s,"NOTICE %s :%cHELP %s: No such command%c\n", nick,0x01,cmd,0x01);
			
		else if(p->help)
		sprintf(s,"NOTICE %s :%cHELP %s%c\n", nick,0x01,p->help,0x01);
		
		else
		sprintf(s,"NOTICE %s :%cHELP %s: No help for command%c\n", nick,0x01,cmd,0x01);
			
		sendto_server(Win,s);
		return;
	} 
	buf= (char*)malloc(sizeof(char)*MAXLINE);
	if(!buf) {err_ret("%s (%d)", __FILE__, __LINE__);return;}
	buf[0]='\0';
	listcmds(cmdTree, &buf, CMD_CTCP);
	if(buf[0])
		sprintf(s,"NOTICE %s :%cHELP Help topics: %s%c\r\n", nick,0x01,buf,0x01);
	else
	     sprintf(s,"NOTICE %s :%cHELP %s%c\r\n", nick,0x01,"No help found for cmd",0x01);
	if(sflags&S_CTCP_OTHER) addto_sender(nextstr(Msg,1),SS_CTCP_OTHER);
	sendto_server(Win,s);
	free(nick); free(buf); free(cmd);
}
}
static void listcmds(Cmd *p, char **s,int flag){
	if(p){
		listcmds(p->left, s, flag);
		if(p->type & flag) {
#warning realloc
			strcat(*s,p->name);
			strcat(*s, " ");
		}
		listcmds(p->right, s, flag);
	}
}


static void do_relay(void *data){
 fputs("(relay not implemented)", stderr);
}
static void do_userinfo(void *data){
	if(!check_msg(USERINFO,Msg)){
	char *s;
	char *nick;

	nick=(char*)malloc(sizeof(char)*strlen(Msg));

	if(!nick) return;
	
	if( sscanf(Msg,"%[^!]!", nick)<1) goto ddone;
	if(sflags&S_CTCP_OTHER) addto_sender(nextstr(Msg,1),SS_CTCP_OTHER);
	if(!(*userinfo)) goto ddone;
	s=(char*)malloc(sizeof(char)*(strlen(userinfo)+ strlen(nick)+30));
	if(!s) goto ddone;
	sprintf(s,"NOTICE %s :%cUSERINFO %s%c\n", nick,0x01,userinfo,0x01);
	sendto_server(Win,s);

	free(s);
 ddone:
	free(nick);

 }
}

static void do_finger(void *data){
if(!check_msg(CTCP_FINGER,Msg)){
	
	char *s=NULL;
	char *nick;

	if(sflags&S_FINGER) addto_sender(nextstr(Msg,1),SS_FINGER);
	if(!(gflags&FINGER)) return;
	nick=(char*)malloc(sizeof(char)*strlen(Msg));
	if(!nick) return;
	if( sscanf(Msg,"%[^!]!", nick)<1) return;
	/*if(sflags&S_FINGER) addto_sender(Win->server, nick, S_FINGER);*/
if(*finger_reply){
	s= (char *)malloc(sizeof(char)*(strlen(finger_reply)+64)+strlen(nick));
	if(!s) return;
	sprintf(s,"NOTICE %s :%cFINGER %s%c\n",	nick, 0x01,finger_reply, 0x01);
}
else
{
	struct passwd *pwd;
	FILE *fp;

	pwd=getpwuid(getuid());
	if(!pwd) return;
#ifdef UTMP
	if(!(gflags&FINGER_UTMP)) goto no_utmp;
	if( (fp=fopen(UTMP, "r"))!=NULL){
		struct utmp ut;
		char name[UT_NAMESIZE+1];

		int found=0;
		while(fread( (char *)&ut, sizeof(ut), 1, fp)==1){
			if(ut.ut_user[0]=='\0') continue;
			strncpy(name, ut.ut_user,UT_NAMESIZE);
			if(!strcmp(pwd->pw_name, name)){
				found=1; break;
			}
		}
		fclose(fp);
		if(found){
		    	char *t;
			char ut_host[UT_HOSTSIZE+1];
			char ut_line[UT_LINESIZE+1];
			
			t=ctime( &ut.ut_time);
			*(t+strlen(t)-1)='\0';
			s=(char*)malloc(sizeof(char)*(strlen(pwd->pw_gecos)+ strlen(t)+
			strlen(pwd->pw_name)+ UT_HOSTSIZE+ UT_NAMESIZE+128+strlen(host)
			+strlen(nick)));
			if(!s) return;
			strncpy(ut_host,ut.ut_host, UT_HOSTSIZE);
			strncpy(ut_line,ut.ut_line, UT_LINESIZE);
		sprintf(s,"NOTICE %s :%c%c%cFINGER %c\x2Login:%s\x2Name:%s\x2Host:"
		" %s%cFrom: %s%cDevice: %s.\x2On since %s%c\n",
			 nick, 0x01,0x2,0x2,0x2,pwd->pw_name, pwd->pw_gecos, 
			 host,0x2, ut_host,0x2,ut_line, t,0x01);
		}
	}
	else if(gflags&VERBOSE_CTCP) berr_msg(UTMP": %s", strerror(errno));
no_utmp:
#endif
	if(!s){
	   	s=(char*)malloc(sizeof(char)*(strlen(pwd->pw_gecos)+ strlen(pwd->pw_name)+ 
		strlen(host)+64+strlen(nick)));
		if(!s) return;
		sprintf(s,"NOTICE %s :%cFINGER %s (%s) on host %s%c\n",
			nick, 0x01,pwd->pw_name, pwd->pw_gecos, host, 0x01);
	}
}/* if(*finger_reply)*/
	if(s){
		sendto_server(Win,s);
		free(s);
	}
	free(nick);
}
}


extern int load_ctcp_cmds(void){
typedef struct TCtcpCmd{
	char *name;
	char *help;
	void (*func)(void *);
} CtcpCmd;
CtcpCmd	ctcp_cmds[]={
	{"ACTION",	"ACTION describes sender's actions or feelings",& do_action},
	{"CLIENTINFO",	"CLIENTINFO  returns info about the client",&do_clientinfo},
	{"DCC",		"DCC request direct client to client connection",& do_dcc},
	{"FINGER",	"FINGER gets system info about the recipient", &do_finger},
	{"HELP",	"HELP gets info about a client's CTCP commands",&do_ctcp_help},
	{"PING",	"PING sends something and expects it to be echoed", &do_ping},
	{"RELAY",	"RELAY forwards a DCC request", &do_relay},
	{"SED",		"SED marks simple(?) encryted data",&do_sed},
	{"TIME",	"TIME requests for the time on recipient host",&do_time},
	{"USERINFO",	"USERINFO gets any info that user set for this purpose", &do_userinfo},
	{"VERSION",	"VERSION requests for client version",&do_version},

	};	

	Cmd cmd;
	int nvars=sizeof(ctcp_cmds)/sizeof(CtcpCmd);
       int i;

       for(i=0;i<nvars;i++){
		cmd.name=ctcp_cmds[i].name;
		cmd.help=ctcp_cmds[i].help;
		cmd.type=CMD_IN|CMD_CTCP;
		cmd.cmd.in=ctcp_cmds[i].func;
		insert_cmd(&cmdTree, &cmd, CMD_CTCP);
	}
	return 0;

}


extern int ctcp_cmd(void){
	char *str=NULL;
	char *buf=NULL, *sender=NULL;
	size_t ll=strlen(Msg);
	Cmd *cmd;


	 buf=(char*)malloc(sizeof(char)*ll);
	if(!buf) return 0;
	if(sscanf(Msg,"%*s %*s %*s :\x1%[^\x1]\x1",buf)<1)
	{free(buf); return 0;  /* msg*/}
	/*fprintf(stderr,"%s\n",Msg);*/
	sender=(char*)malloc(sizeof(char)*ll);
	if(sscanf(Msg,"%s",sender)<1) return 1; /* sender */
        str=nextstr(buf,1);	/* ctcp command */
	if( (!strcasecmp(str, "DCC") && ignored(sender, IG_DCC)) 
		|| (strcasecmp(str, "CTCP") && ignored(sender, IG_CTCP))
		){
		if(gflags & INFORM_IGNORED){
		    	char *m, *nick, *email;

		    	ll=strlen(sender);
			nick=(char*)malloc(sizeof(char)*ll);
			email=(char*)malloc(sizeof(char)*ll);
			if(sscanf(sender,"%[^!]!%s", nick, email)<2)
				return 1;
		    fmt_string="%n%h%p$";
    		    m=format_msg(fmt_ignored,nick, email,str);
		    if(!m) return 1;
		    else{
		    	char *s=(char*)malloc(sizeof(char)*(strlen(m)+strlen(nick)+16));
		    	if(!s) return (-1);
			sprintf(s,"NOTICE %s :%s\n",nick, m);
		    	sendto_server(Win, s);
			free(s); free(m);
		    }
		    free(email);free(nick);
		}
		return 1;
	}
	if(check_msg(CTCP_ALL,Msg)) return 1;
	cmd=find_cmd(str,CMD_CTCP);
	if(!cmd){
		if(!check_msg(CTCP_UNKNOWN,Msg)) 
		if(gflags & VERBOSE_CTCP)
			err_msg("unknown ctcp %s received from %s: %s",
			str, sender,buf);
       		return 1;
	}
       if(gflags & VERBOSE_CTCP)
		berr_msg(PROMPT"CTCP %s %s received from %s", 
		cmd->name, nextstr(buf,2), sender);
       if(cmd->type & CMD_IN){
       		free(str);free(sender); 
       		(cmd->cmd.in)( (void*)0);
	}
       	 else if(cmd->type & CMD_FD){
			char   *buff;
			if(cmd->cmd.sp>sp_count || s_pipe[cmd->cmd.sp].fd<0){
				berr_msg("%s %d: CTCP script bug",__FILE__, __LINE__);
				return (-1);
			}
			buff = (char *) malloc(sizeof(char) * (
			ll+16+strlen(Win->server->nick+
			strlen(Win->server->servname)+16)));
			rmstr(&buf,1);	/* remove cmd name*/
			/*
			<id> __CTCP__ <yournick> <sender> <args>
			*/
			sprintf(buff,"%d __CTCP__ %s,%d %s %s %s %s\n", 
			(int)Win->chanwin->bar->u_ldata, Win->server->servname,
			Win->server->port, Win->server->nick,
			sender,cmd->name, buf);
			write_to_sp(cmd->cmd.sp, buff);
			free(buff);
		}
	else if(gflags & VERBOSE_CTCP)	
		berr_msg("unknown ctcp cmd type %d", cmd->type&CMD_CTCP);
	return 1;
}

/* -- from ircII */
/*
 * quote_it: This quotes the given string making it sendable via irc.  A
 * pointer to the length of the data is required and the data need not be
 * null terminated (it can contain nulls).  Returned is a malloced, null
 * terminated string.   
 */
char	*
ctcp_quote_it(str, len)
	char	*str;
	int	len;
{
	char	buffer[MAXLINE + 1];
	char	*ptr;
	int	i;

	ptr = buffer;
	for (i = 0; i < len; i++)
	{
		switch (str[i])
		{
		case CTCP_DELIM_CHAR:
			*(ptr++) = CTCP_QUOTE_CHAR;
			*(ptr++) = 'a';
			break;
		case '\n':
			*(ptr++) = CTCP_QUOTE_CHAR;
			*(ptr++) = 'n';
			break;
		case '\r':
			*(ptr++) = CTCP_QUOTE_CHAR;
			*(ptr++) = 'r';
			break;
		case CTCP_QUOTE_CHAR:
			*(ptr++) = CTCP_QUOTE_CHAR;
			*(ptr++) = CTCP_QUOTE_CHAR;
			break;
		case '\0':
			*(ptr++) = CTCP_QUOTE_CHAR;
			*(ptr++) = '0';
			break;
		default:
			*(ptr++) = str[i];
			break;
		}
	}
	*ptr = '\0';
	return strdup(buffer); /* fotang */
	/*str = (char *) 0;
	malloc_strcpy(&str, buffer);
	return (str);*/
}
/* -- from ircII */
/*
 * ctcp_unquote_it: This takes a null terminated string that had previously
 * been quoted using ctcp_quote_it and unquotes it.  Returned is a malloced
 * space pointing to the unquoted string.  NOTE: a trailing null is added for
 * convenied, but the returned data may contain nulls!.  The len is modified
 * to contain the size of the data returned. 
 */
char	*
ctcp_unquote_it(str, len)
	char	*str;
	int	*len;
{
	char	*buffer;
	char	*ptr;
	char	c;
	int	i,
		new_size = 0;

	buffer = (char *) malloc(sizeof(char) * *len);
	if(!buffer) return NULL;
	ptr = buffer;
	i = 0;
	while (i < *len)
	{
		if ((c = str[i++]) == CTCP_QUOTE_CHAR)
		{
			switch (c = str[i++])
			{
			case CTCP_QUOTE_CHAR:
				*(ptr++) = CTCP_QUOTE_CHAR;
				break;
			case 'a':
				*(ptr++) = CTCP_DELIM_CHAR;
				break;
			case 'n':
				*(ptr++) = '\n';
				break;
			case 'r':
				*(ptr++) = '\r';
				break;
			case '0':
				*(ptr++) = '\0';
				break;
			default:
				*(ptr++) = c;
				break;
			}
		}
		else
			*(ptr++) = c;
		new_size++;
	}
	*ptr = '\0';
	*len = new_size;
	return (buffer);
}
/* -- from ircII */
void
encrypt_str(str, len, key)
	char	*str,
		*key;
	int	len;
{
	int	key_len,
	key_pos,
	i;
	char	mix,
	tmp;

	key_len = strlen(key);
	key_pos = 0;
	/*    mix = key[key_len-1]; */
	mix = 0;
	for (i = 0; i < len; i++)
	{
		tmp = str[i];
		str[i] = mix ^ tmp ^ key[key_pos];
		mix ^= tmp;
		key_pos = (key_pos + 1) % key_len;
	}
	str[i] = (char) 0;
}
/* -- from ircII */
static	void
decrypt(str, len, key)
	char	*str,
		*key;
	int	len;
{
	int	key_len,
		key_pos,
		i;
	char	mix,
		tmp;

	key_len = strlen(key);
	key_pos = 0;
	/*    mix = key[key_len-1]; */
	mix = 0;
	for (i = 0; i < len; i++)
	{
		tmp = mix ^ str[i] ^ key[key_pos];
		str[i] = tmp;
		mix ^= tmp;
		key_pos = (key_pos + 1) % key_len;
	}
	str[i] = (char) 0;
}




  #if 0
 	}else
	}else

	if(!strcasecmp(str, "RELAY")){
		/* receive inetaddr and port to connect to; same as dcc send;
			use it to forward a DCC SEND request
			CONNECT CHAT * addr port
			CONNECT DCC file addr port size*/
	       char type[5];
	       if(sscanf(buf, "%*s %s", type)<1) return 1;
	       if(!strcasecmp(type, "SEND")){
	       		char *fname=NULL, *t;
			u_short port;
	       		u_long inaddr;
			char *fsize, *nick2, *email2;
			fname= (char*)malloc(sizeof(char)*ll);
			fsize= (char*)malloc(sizeof(char)*ll);
			nick2= (char*)malloc(sizeof(char)*ll);
			email2=(char*)malloc(sizeof(char)*ll);
			if(!fname || !fsize || !nick2 || !email2) {err_ret("%s %d ", __FILE__, __LINE__);}
			if(sscanf(buf, "%*s %*s %s %s %s %lu %u %[^\r\n]\r\n", 
			nick2, email2, fname, &inaddr, &port, fsize)<5) return 1;
			inaddr=strtoul(nextstr(buf,6), &t,0);
		printf("rel:%s %s %s %u %d %s\n",nick2, email2, fname,inaddr, port,fsize);
		do_dcc_send(nick2, email2, fname, inaddr, port, atoi(fsize));
		}else
		if(!strcasecmp(type, "CHAT")){
 			char port[10];
			u_long inaddr;
			char *fsize, *nick2, *email2;
			nick2= (char*)malloc(sizeof(char)*ll);
			email2=(char*)malloc(sizeof(char)*ll);
			if(sscanf(buf, "%*s CHAT %s %s %lu %[^\r\n]\r\n",
				nick2,email2, &inaddr, port)<4) return 1;
			printf("red:%s %s %u %s\n",nick2, email2, inaddr,port); 
			do_dcc_chat(nick2, email2, inaddr, atol(port));
		}

	}
	else
	       fprintf(stderr,"%s later!!!\n", buf);	

       return 1;	
 
#endif



