/* CDCC v2 1.00 (c) Copyright 1996 William Glozer  */
/* ----------------------------------------------- */
/* Last revision 04.19.96 by Ananda                */

/* New CDCC for BitchX and any other clients that deserve to run it :) */
/* Note that I did use a lot of code/ideas from a copy of CDCC written */
/* for BitchX by panasync, so thanks to him for alot of the code and   */
/* ideas :)  I would appreciate any bugs reported to me as soon as is  */
/* possible, and cdcc.c + cdcc.h for any mods you do... if you modify  */
/* it for your client, I can add those mods as #ifdefs so the next ver */
/* will work with your client and have all the new stuff.  -Ananda '96 */
/* Modifed even more by panasync (edwac@sasknet.sk.ca) to interface    */
/* cleanly and nicely with BitchX. Blame all bugs on me instead of     */
/* Ananda */

#define BITCHX
#define CDCC_FLUD

#include "irc.h"
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

#if defined(HAVE_DIRENT_H)
# include <dirent.h>
#else
# define dirent direct
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif


#include "ircaux.h"
#include "commands.h"
#include "ignore.h"
#include "ctcp.h"
#include "hook.h"
#include "dcc.h"
#include "flood.h"
#include "screen.h"
#include "parse.h"
#include "output.h"
#include "input.h"
#include "server.h"
#include "vars.h"
#include "list.h"
#include "userlist.h"
#include "misc.h"
#include "whois.h"

#include "cdcc.h"
#ifdef BITCHX
#include "misc.h"
#endif

/* external ircII stuff */
extern int dcc_active_count;
static int l_timer _((char *, char *));
static int r_info _((char *, char *));
static int r_rmsend _((char *, char *));
static int r_queue _((char *, char *));
static int l_tsend _((char *, char *));
static int l_tresend _((char *, char *));
static int l_resume _((char *, char *));
static int l_describe _((char *, char *));
static int l_help(char *, char *);

/* add a pack to the offer list */
static int l_offer(char *, char *);

/* send a pack to someone */
static int l_send(char *, char *);

/* re-send a pack to someone */
static int l_resend(char *, char *);

/* remove a pack or all packs from the offer list */
static int l_doffer(char *, char *);

/* localy list offered packs */
static int l_list(char *, char *);

/* notify the channel that packs are offered */
static int l_notice(char *, char *);

/* view your queue */
static int l_queue(char *, char *);

/* save all offered packs to cdcc.save */
static int l_save(char *, char *);

/* load packs from cdcc.save */
static int l_load(char *, char *);

static int l_minspeed(char *, char *);

/* remote CDCC commands */
/* -------------------- */

/* show help to a remote user */
static int r_help(char *, char *);

/* list packs offered to remote user */
static int r_list(char *, char *);

/* send pack to remote user */
static int r_send(char *, char *);

/* re-send pack to remote user */
static int r_rsend(char *, char *);

/* send pack to remote user */
static int r_tsend(char *, char *);

/* re-send pack to remote user */
static int r_trsend(char *, char *);


/* add files */
static void add_files(char *, char *);
/* add description */
static void add_desc(char *, char *);
/* remove pack/all packs */
static void del_pack(char *, char *);
/* add/remove public channel */
static int l_channel(char *, char *);
/* add note to a pack */
static int l_note(char *, char *);
static int l_echo(char *, char *);
static int l_type(char *, char *);
/* add a person to the dcc queue */
static int add_to_queue(char *, pack *);

void dcc_getfile_resume _((char *, char *));

/* local commands */
local_cmd local[] = {
	{ "CHANNEL",	l_channel,	"public timer channel" },
	{ "DESCRIBE",	l_describe,	"change description of pack" },
	{ "DOFFER",	l_doffer,	"remove pack from the offer list" },
	{ "LIST",	l_list,		"list the packs you have offered" },
	{ "LOAD",	l_load,		"load packs saved to .cdcc.save or specified name" },
	{ "MINSPEED",	l_minspeed,	"minspeed for cdcc ( #.##)" },
	{ "NOTICE",	l_notice,	"notify the channel of offered packs" },
	{ "OFFER",	l_offer,	"add a pack to the offer list" },
	{ "PLIST",	l_plist,	"publicly list your offered packs" },
	{ "QUEUE",	l_queue,	"view entries in the send queue" },
	{ "SAVE",	l_save,		"save your offerlist to .cdcc.save or specified name" },
	{ "SEND",	l_send,		"send a pack to user" },
	{ "RESEND",	l_resend,	"re-send a pack to user" },
	{ "TSEND",	l_tsend,	"tdcc send a pack to user" },
	{ "TRESEND",	l_tresend,	"tdcc resend a pack to user" },
#ifdef MIRC_BROKEN_DCC_RESUME
	{ "RESUME",	l_resume,	"mirc resume" },
#endif
	{ "TIMER",	l_timer,	"public list timer in minutes" },
	{ "NOTE",	l_note,		"add note to pack number"},
	{ "TYPE",	l_type,		"toggle between public and notice" },
	{ "ECHO",	l_echo,		"toggle echo on/off" },
	{ "",		NULL,		"" } 
};
#define NUM_LOCAL (sizeof(local) / sizeof(local_cmd))

/* remote commands */
remote_cmd remote[] = {
	{ "HELP",	r_help,		"help on CDCC commands" },
	{ "RESEND",	r_rsend,	"have CDCC resend pack #N"},
#ifdef MIRC_BROKEN_DCC_RESUME
	{ "RESUME",	r_rmsend,	"have CDCC resume pack #N"},
#endif
	{ "SEND",	r_send,		"have CDCC send pack #N" },
	{ "TRESEND",	r_trsend,	"have CDCC tresend pack #N"},
	{ "TSEND",	r_tsend,	"have CDCC tsend pack #N" },
	{ "LIST",	r_list,		"list of offered packs" },
	{ "INFO",	r_info,		"info on pack #N" },
	{ "QUEUE",	r_queue,	"queue status for us" },
	{ "",		NULL,		"" }, 
};
#define NUM_REMOTE (sizeof(remote) / sizeof(remote_cmd))

/* ahh yes, global variables :( well, interfacing with ircII is a pain in  */
/* the ass, and I couldn't figure out a better way... more than one of my  */
/* routines need these... the static will keep them from being used by any */
/* functions outside of cdcc.c                                             */

unsigned int cdcc_numpacks = 0;
unsigned int send_numpacks = 0;

pack *offerlist = NULL;
static pack *newpack;

static queue *queuelist = NULL;
static unsigned long total_size_of_packs = 0;
static int numqueue = 0;
static int ptimer = 0;
static int do_notice_list = 0;
static int do_cdcc_echo = 1;

double cdcc_minspeed = 0.0;
static char *public_channel = NULL;


#ifdef BITCHX
extern double dcc_max_rate_out, dcc_bytes_out, dcc_max_rate_in, dcc_bytes_in;
#endif

#define cparse(s) convert_output_format(s, NULL, NULL)

/* parse a users CDCC command */
BUILT_IN_COMMAND(cdcc)
{
	int i;
	char *cmd, *rest;
	
	cmd = next_arg(args, &args);
	rest = next_arg(args, &args);
	if (!cmd) {
		l_help(NULL, NULL);
		return;
	}
	
	for (i = 0; *local[i].name; i++) {
		if (!my_stricmp(local[i].name, cmd)) {  
			local[i].function(rest, args);
			return;
		}
	}
	put_it("%s: unknown command \002%s\002", cparse(get_string_var(CDCC_PROMPT_VAR)), cmd);
	return;
}
			
/* parse a remote message CDCC command */
char *msgcdcc(char *from, char *to, char *args)
{
	int i;
	int secure = 0;
	char *cdcc, *rest, *cmd, *temp = NULL, *q;

	malloc_strcpy(&temp, args);
	
	q = temp;
	cdcc = next_arg(temp, &temp);
	if (!cdcc || my_strnicmp(++cdcc, "DCC", 3)) {
	 	new_free(&q);
	 	return args;
	 } 
	
	if ((check_ignore(from, FromUserHost, to, IGNORE_CDCC, NULL) == IGNORED))
	{
		new_free(&q);
		return args;
	}
			
	if (!check_flooding(from, CDCC_FLOOD, args, NULL))
	{
		new_free(&q);
		return NULL;
	}
	if (!offerlist)
	{
		new_free(&q);
		return NULL;
	}
	if ((secure = get_int_var(CDCC_SECURITY_VAR)))
	{
		UserList *tmp;
		if (!(tmp = lookup_userlevelc("*", FromUserHost, "*", user_list)) || tmp->level < secure)
		{
			new_free(&q);
			return args;
		}
	}

	cmd = next_arg(temp, &temp);
	if (!cmd) {
		new_free(&q);
		return args;
	}

	rest = temp;

	for (i = 0; *remote[i].name; i++) {
		if (!my_stricmp(cmd, remote[i].name)) {
			remote[i].function(from, rest);
			new_free(&q);
			return NULL;
		}
	}
	send_to_server("NOTICE %s :try /ctcp %s cdcc help",from, get_server_nickname(from_server));
	new_free(&q);
	return args;
}

static int r_info(char *args, char *rest)
{
	if (rest && *rest)
	{
		char *q;
		pack *ptr = NULL;
		
		q = next_arg(rest, &rest);
		for (ptr = offerlist; ptr; ptr = ptr->next)
			if (matchmcommand(q, ptr->num))
				break;
		if (ptr)
			send_to_server("NOTICE %s :%d file%s %d gets %ld size %2.4f minspeed",args, ptr->numfiles, plural(ptr->numfiles), ptr->gets, ptr->size, ptr->minspeed); 
		else
			send_to_server("NOTICE %s :Invalid info request", args);
	}
	return 0;
}
 
static int r_queue(char *args, char *rest)
{
queue *new = NULL;
int count;
int num = 0;
char buffer[BIG_BUFFER_SIZE+1];
	if (queuelist && args)
	{
		*buffer = 0;
		for (new = queuelist, count = 1; new; new = new->next)
		{
			if (!my_stricmp(new->nick, args))
			{
				num++;
				strmopencat(buffer, BIG_BUFFER_SIZE, ltoa(count), ",", NULL);
				count++;
			}
		}
		if (num)
		{
			chop(buffer, 1);
			send_to_server("NOTICE %s :You have %d packs queued at %s", args, num, buffer);
		}
		else
			send_to_server("NOTICE %s :You have no packs queued.", args);
	}
	return 0;
}

static int do_local_send(char *command, char *args, char *rest)
{
	pack *ptr = NULL;
	char *temp = NULL, *file = NULL, *dccinfo = NULL, *q = NULL, *p;
	int maxdcc, maxqueue;
	int tdcc = 0;
	int queued_files =  0;
			
	if (*command == 'T')
		tdcc = 1;	
	if (!args || !*args)
	{
		if (tdcc)
		{
			if (!my_stricmp(command, "TSEND"))
				userage("cdcc", "tsend <nickname> <#\002|\002filename>");
			else if (!my_stricmp(command, "TRESEND"))
				userage("cdcc", "tresend <nickname> <#\002|\002filename>");
		}
		else
		{
			if (!my_stricmp(command, "SEND"))
				userage("cdcc", "send <nickname> <#\002|\002filename>");
			else if (!my_stricmp(command, "RESEND"))
				userage("cdcc", "resend <nickname> <#\002|\002filename>");
#ifdef MIRC_BROKEN_DCC_RESUME
			else if (!my_stricmp(command, "RESUME"))
				userage("cdcc", "resume <nickname> <#\002|\002filename>");
#endif
		}
		return 0;
	}

	maxdcc = get_int_var(DCC_SEND_LIMIT_VAR);
	maxqueue = get_int_var(DCC_QUEUE_LIMIT_VAR);
		
	while (1)
	{
		if (!(temp = next_arg(rest, &rest)))
			break;

		if (isdigit(*temp) || (*(temp+1) && isdigit(*(temp+1))))
		{
			if (*temp == '#')
				temp++;
			for (ptr = offerlist; ptr; ptr = ptr->next)
				if (matchmcommand(temp, ptr->num))
					break;
		}		
		if (ptr)
		{

			if (maxdcc && dcc_active_count >= maxdcc) 
			{
				if (maxqueue && (numqueue >= maxqueue)) 
				{
					put_it("%s: all dcc and queue slots full", cparse(get_string_var(CDCC_PROMPT_VAR)));
					if (queued_files)
						put_it("%s: Queued %d files", cparse(get_string_var(CDCC_PROMPT_VAR)), queued_files );
					return 0;
				}
				queued_files += add_to_queue(args, ptr);

				continue;
			}
	
			put_it("%s: %s %s%s%s pack #\002%d\002 (\002%d\002 file%s)", cparse(get_string_var(CDCC_PROMPT_VAR)),
				!my_stricmp(command, "SEND")||!my_stricmp(command,"TSEND")?"sending":"resending",
				UND_TOG_STR, args, UND_TOG_STR, ptr->num, ptr->numfiles,
				plural(ptr->numfiles));
			malloc_strcpy(&file, ptr->file);
			q = file;

			for (p = next_arg(file, &file); p && *p; p = next_arg(file, &file)) 
			{
				malloc_sprintf(&dccinfo, "%s %s", args, p);
				if (!my_stricmp(command, "SEND"))
					dcc_filesend(command, dccinfo);
#if 0
				else if (!my_stricmp(command, "RESUME"))
					dcc_getfile_resume(command, dccinfo);
#endif
				else
					dcc_resend(command, dccinfo);
			}
			send_numpacks++;
			ptr->gets++;
		}
		else
		{
			if (offerlist && (isdigit(*temp) || (*(temp+1) && (isdigit(*(temp+1))))))
				put_it("%s: No such pack number", cparse(get_string_var(CDCC_PROMPT_VAR)));
			else
			{
				malloc_sprintf(&dccinfo, "%s %s", args, temp);
				if (!my_stricmp(command, "SEND") || !my_stricmp(command, "TSEND"))
					dcc_filesend(command, dccinfo);
#if 0
				else if (!my_stricmp(command, "RESUME"))
					dcc_getfile_resume(command, dccinfo);
#endif
				else
					dcc_resend(command, dccinfo);
			}
		}  
		new_free(&q);
		file = NULL;
	}
	if (queued_files)
		put_it("%s: Queued %d files", cparse(get_string_var(CDCC_PROMPT_VAR)), queued_files);
	new_free(&dccinfo);
	new_free(&q);
	return 0;
}
 
static int l_send(char *args, char *rest)
{
	do_local_send("SEND", args, rest);
	return 0;
}

static int l_resend(char *args, char *rest)
{
	do_local_send("RESEND", args, rest);
	return 0;
}

#ifdef MIRC_BROKEN_DCC_RESUME
static int l_resume(char *args, char *rest)
{
	do_local_send("RESUME", args, rest);
	return 0;
}
#endif

static int l_tsend(char *args, char *rest)
{
	do_local_send("TSEND", args, rest);
	return 0;
}

static int l_tresend(char *args, char *rest)
{
	do_local_send("TRESEND", args, rest);
	return 0;
}


/*resends a pack to the requestee*/
/*Added by Wicked Angel: wangel@wgrobez1.remote.louisville.edu*/
/*It wasn't hard ... but hey ... it seemed like a good idea :>*/
/* routine modified highly by Colten Edwards. */

static int do_dcc_sends(char *command, char *from, char *args)
{
	pack *ptr;
	char *temp = NULL, *file = NULL, *dccinfo = NULL, *q = NULL, *p;
	int maxdcc, maxqueue;
	int count = 0;
	int queued_files = 0;
		
	maxdcc = get_int_var(DCC_SEND_LIMIT_VAR);
	maxqueue = get_int_var(DCC_QUEUE_LIMIT_VAR);

	while (1)
	{
		if (!(temp = next_arg(args, &args)))
			break;

		for (ptr = offerlist; ptr; ptr = ptr->next)
			if (matchmcommand(temp, ptr->num))
				break;
		if (ptr)
		{

			if (maxdcc && ((maxdcc - dcc_active_count) < ptr->numfiles || dcc_active_count >= maxdcc)) 
			{
				if (maxqueue && (numqueue >= maxqueue)) 
				{
					if (queued_files)
					{
						put_it("%s: Queued %d files for %s", cparse(get_string_var(CDCC_PROMPT_VAR)), queued_files, from);
						send_to_server("NOTICE %s :\002CDCC\002: all slots full... Some requests ignored. Added to queue for %d requests", from, queued_files);
					}
					else
						send_to_server("NOTICE %s :\002CDCC\002: all dcc and queue slots full... Try again later", from);

					return 0;
				}
				queued_files += add_to_queue(from, ptr);
				count++;
				continue;
			}
	
			put_it("%s: %s %s%s%s pack #\002%d\002 (\002%d\002 file%s)", cparse(get_string_var(CDCC_PROMPT_VAR)),
				!my_stricmp(command, "SEND")||!my_stricmp(command, "TSEND")?"sending":!my_stricmp(command,"RESUME")?"resuming":"resending", UND_TOG_STR, from, UND_TOG_STR, 
				ptr->num, ptr->numfiles, plural(ptr->numfiles));
			malloc_strcpy(&file, ptr->file);
			q = file;

			for (p = next_arg(file, &file); p && *p; p = next_arg(file, &file)) 
			{
				malloc_sprintf(&dccinfo, "%s %s", from, p);
				if (!my_stricmp(command, "RESEND") || !my_stricmp(command, "TRESEND"))
					dcc_resend(command, dccinfo);
#ifdef MIRC_BROKEN_DCC_RESUME
				else if (!my_stricmp(command, "RESUME"))
					dcc_getfile_resume(command, dccinfo);
#endif
				else
					dcc_filesend(command, dccinfo);
			}
			send_numpacks++;
			ptr->gets++;
		}
		else if (!count)
			send_to_server("NOTICE %s :\002CDCC\002: invalid pack number", from);
		count++;
		new_free(&q);
		file = NULL;
	}
	if (queued_files)
	{
		put_it("%s: Queued %d files for %s", cparse(get_string_var(CDCC_PROMPT_VAR)), queued_files, from);
		send_to_server("NOTICE %s :\002CDCC\002: all slots full... Added to the queue for %d requests", from, queued_files);
	}
	new_free(&dccinfo);
	new_free(&q);
	return 0;
}


static int r_rsend(char *from, char *args)
{
	do_dcc_sends("RESEND", from, args);
	return 0;
}

#ifdef MIRC_BROKEN_DCC_RESUME
static int r_rmsend(char *from, char *args)
{
	do_dcc_sends("RESUME", from, args);
	return 0;
}
#endif

static int r_trsend(char *from, char *args)
{
	do_dcc_sends("TRESEND", from, args);
	return 0;
}

static int r_tsend(char *from, char *args)
{
	do_dcc_sends("TSEND", from, args);
	return 0;
}

/* send a pack to the remote user */
static int r_send(char *from, char *args)
{
	do_dcc_sends("SEND", from, args);
	return 0;
}
			

/* remote pack list */
static int r_list(char *from, char *args)
{
	pack *ptr;
	char size[30];
	char mrate_out[30];
	char mrate_in[30];
	char bytes_out[30];
	char bytes_in[30];
	char speed_out[30];
	char *type_msg;
	int once = 0;

	sprintf(mrate_out, "%1.3g", dcc_max_rate_out);
	sprintf(mrate_in, "%1.3g", dcc_max_rate_in);
	sprintf(bytes_out, "%1.3g", dcc_bytes_out);
	sprintf(bytes_in, "%1.3g", dcc_bytes_in);
	sprintf(speed_out, "%1.3g", cdcc_minspeed);

	type_msg = (do_notice_list)? "NOTICE":"PRIVMSG";

	for (ptr = offerlist; ptr; ptr = ptr->next) 
	{
#ifdef BITCHX
		if (!once && do_hook(CDCC_PREPACK_LIST, "%s %s %s %d %d %d %d %d %s %s %s %s %lu %s", 
			"NOTICE", from, get_server_nickname(from_server), 
			cdcc_numpacks, 
			get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, 
			get_int_var(DCC_SEND_LIMIT_VAR), numqueue, 
			get_int_var(DCC_QUEUE_LIMIT_VAR), 
			mrate_out, bytes_out, mrate_in, bytes_in, 
			total_size_of_packs, speed_out))
		{
			send_to_server("NOTICE %s :Files Offered: /ctcp %s CDCC send #N for pack N", from, get_server_nickname(from_server));
			if (get_int_var(DCC_SEND_LIMIT_VAR))
				send_to_server("NOTICE %s :    [%d pack%s %d/%d slots open]", from, 
					cdcc_numpacks, plural(cdcc_numpacks), get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR));
			else
				send_to_server("NOTICE %s :    [%d pack%s]", from, cdcc_numpacks,plural(cdcc_numpacks));
		}
#endif
	 	if (ptr->size / 1024 > 999)
			sprintf(size, "\002%4.1f\002mb",
				(((double)ptr->size) / 1024) / 1024);
		else
			sprintf(size, "\002%4.1f\002kb", (((double)ptr->size) / 1024)); 
				
#ifdef BITCHX
		if (do_hook(CDCC_PACK_LIST, "%s %s %d %d %lu %d %s", 
			"NOTICE", from, ptr->num, ptr->numfiles, ptr->size, ptr->gets, ptr->desc))
		{
#endif
			send_to_server("NOTICE %s :#%d \037(\037%10s\037:\037\002%4d\002 get%s\037)\037 %-40.40s",
				from, ptr->num, size, ptr->gets, plural(ptr->gets), 
				ptr->desc ? ptr->desc : "no description");
#ifdef BITCHX
		}
		if (ptr->notes && do_hook(CDCC_NOTE_LIST, "%s %s %s", "NOTICE", from, ptr->notes))
			send_to_server("NOTICE %s :      %s", from, ptr->notes);
		once++;
#endif
	}
#ifdef BITCHX
	if (once)
		do_hook(CDCC_POSTPACK_LIST, "%s %s %s %d %d %d %d %d %s %s %s %s %lu %s", 
			"NOTICE", from, get_server_nickname(from_server), cdcc_numpacks, get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR), numqueue, get_int_var(DCC_QUEUE_LIMIT_VAR), 
			mrate_out, bytes_out, mrate_in, bytes_in, total_size_of_packs, speed_out);
#endif
	return 0;
}		

/* remote help display  */
static int r_help(char *from, char *args)
{
	int i;
#ifdef CDCC_FLUD
	if (args && *args)
	{
		char *q;
		q = next_arg(args, &args);
		for (i = 0; *remote[i].name; i++) 
		{
			if (!my_stricmp(remote[i].name, q))
			{
				send_to_server("NOTICE %s :\002CDCC\002: %-6s - %s",
					from, remote[i].name, remote[i].help);
				break;
			}
		}
	} 
	else
	{
		char buffer[BIG_BUFFER_SIZE+1];
		char *q = buffer;
		for (i = 0; *remote[i].name; i++) 
		{
			snprintf(q, BIG_BUFFER_SIZE, "%s ", remote[i].name);
			q = &buffer[strlen(buffer)];
		}
		send_to_server("NOTICE %s :\002CDCC\002: %s", from, buffer);
	}
#endif
	return 0;
}


/* remove a pack or all packs from the offerlist */
static int l_doffer(char *args, char *rest)
{
	if (!cdcc_numpacks) {
		put_it("%s: you have no packs offered", cparse(get_string_var(CDCC_PROMPT_VAR)));
		return 0;
	}
	if (args && *args)
	{
		char * temp = NULL;
		malloc_sprintf(&temp, "%s %s", args, rest ? rest : "");
		del_pack(NULL, temp);
		new_free(&temp);
		l_list(NULL, NULL);
	}
	else 
	{
		l_list(NULL, NULL);
		add_wait_prompt("Remove pack [* for all packs]: ", del_pack, "", WAIT_PROMPT_LINE);
	}
	return 0;
}

/* localy list the packs you have offered */
static int l_list(char *args, char *rest)
{
	pack *ptr;
	char temp[30];
	
	if (!cdcc_numpacks) {
		put_it("%s: you have no packs offered", cparse(get_string_var(CDCC_PROMPT_VAR)));
		return 0;
	}
	if (args)
	{
		char *num = next_arg(args, &args);
		int it;

		it = my_atol(num);
		if (it <= 0)
			return 0;
		for (ptr = offerlist; ptr; ptr= ptr->next)
		{
			if (it == ptr->num)
			{
				char *temp = NULL;
				char *p, *q;
				int i = 1;
				malloc_strcpy(&temp, ptr->file);
				q = temp;				
				while (temp && *temp)
				{
					p = next_arg(temp, &temp);
					put_it("#%-2d %-2d  %-2d   %s",
						ptr->num, ptr->gets, i++, p);
				}
				
				new_free(&q);
				break;
			}
		}
	}
	else 
	{
		
		put_it("%s", convert_output_format("#   files    size     gets minspeed  description", NULL, NULL));
		for (ptr = offerlist; ptr; ptr = ptr->next) 
		{
			sprintf(temp, "%4.1f", _GMKv(ptr->size));
/* buggy SUNOS doesn't like this next line at all. Why?
			sprintf(temp2, "%4.1f", (double)(ptr->minspeed));*/
			put_it("%-2d    \002%3d\002  %6s%-4s \002%4d\002     0.0  %-39.39s",
				ptr->num, ptr->numfiles,
				temp, _GMKs(ptr->size), /*temp2*/ptr->gets, ptr->desc);
		}
	}
	return 0;
}

/* add a pack to the offer list */
static int l_offer(char *args, char *rest)
{
char *tmp = NULL;
	if (args && *args)
	{
		tmp = m_sprintf("%s %s", args, rest?rest:"");
		add_files(NULL, tmp);
	}
	else
	{
		malloc_sprintf(&tmp, "Add file(s) to pack%s #%d : ", plural(cdcc_numpacks), cdcc_numpacks+1);
		add_wait_prompt(tmp, add_files, "", WAIT_PROMPT_LINE);
	}
	new_free(&tmp);
	return 0;
}

/* display the offerlist to current channel */
int l_plist(char *args, char *rest)
{
	pack *ptr;
	char *chan = NULL, *string = NULL;
	char size[20];
	char mrate_out[30];
	char mrate_in[30];
	char bytes_out[30];
	char bytes_in[30];
	char speed_out[30];
	char *type_msg;
	int maxdccs, blocksize, maxqueue;
	
	if (!get_current_channel_by_refnum(0) || !cdcc_numpacks || (args && *args && !is_channel(args))) {
		put_it("%s: you %s",cparse(get_string_var(CDCC_PROMPT_VAR)),
			cdcc_numpacks ? "are not on a channel!" :
				   "have no packs offered!");
		return 0;
	}
	type_msg = (do_notice_list)? "NOTICE":"PRIVMSG";
	
	if (args && *args)
		malloc_strcpy(&chan, args);
	else	
		malloc_strcpy(&chan, get_current_channel_by_refnum(0));

	maxdccs = get_int_var(DCC_SEND_LIMIT_VAR);
	blocksize = get_int_var(DCC_BLOCK_SIZE_VAR);
	maxqueue = get_int_var(DCC_QUEUE_LIMIT_VAR);
	message_from(chan, LOG_CRAP);
#ifdef BITCHX
	sprintf(mrate_out, "%1.3g", dcc_max_rate_out);
	sprintf(mrate_in, "%1.3g", dcc_max_rate_in);
	sprintf(bytes_out, "%1.3g", dcc_bytes_out);
	sprintf(bytes_in, "%1.3g", dcc_bytes_in);
	sprintf(speed_out, "%1.3g", cdcc_minspeed);
	if (do_hook(CDCC_PREPACK_LIST, "%s %s %s %d %d %d %d %d %s %s %s %s %lu %s", type_msg, chan, get_server_nickname(from_server), cdcc_numpacks, get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR), numqueue, get_int_var(DCC_QUEUE_LIMIT_VAR), mrate_out, bytes_out, mrate_in, bytes_in, total_size_of_packs, speed_out))
	{
#endif
		malloc_sprintf(&string, "\037[\037cdcc\037]\037 \002%d\002 file%s offered\037-\037 /ctcp \002%s\002 cdcc send #x for pack #x",
			cdcc_numpacks, plural(cdcc_numpacks), get_server_nickname(from_server));
		send_text(chan, string, do_notice_list?"NOTICE":NULL, do_cdcc_echo, 0);
		malloc_sprintf(&string, "\037[\037cdcc\037]\037 dcc block size\037:\037 \002%d\002, slots open\037:\037 \002%2d\002/\002%2d\002, dcc queue\037:\037 \002%2d\002/\002%2d\002",
			(blocksize) ? blocksize : 1024, maxdccs - dcc_active_count,
			maxdccs, maxqueue - numqueue, maxqueue);
		send_text(chan, string, do_notice_list?"NOTICE":NULL, do_cdcc_echo, 0);
#ifdef BITCHX
	}
#endif	
	for (ptr = offerlist; ptr; ptr = ptr->next) 
	{
	 	if (ptr->size / 1024 > 999)
			sprintf(size, "\002%3.2f\002mb",
				(float) (ptr->size / 1024) / 1024);
		else
			sprintf(size, "\002%3.2f\002kb", (float) ptr->size / 1024); 
				
#ifdef BITCHX
		if (do_hook(CDCC_PACK_LIST, "%s %s %d %d %lu %d %s", 
			type_msg, chan, ptr->num, ptr->numfiles, ptr->size, ptr->gets, ptr->desc))
		{
#endif
			malloc_sprintf(&string, "\037%%\037 #%-2d \037(\037%10s\037:\037\002%4d\002 get%s\037)\037 %-39.39s",
				ptr->num, size, ptr->gets, plural(ptr->gets), 
				ptr->desc ? ptr->desc : "no description");
			send_text(chan, string, do_notice_list?"NOTICE":NULL, do_cdcc_echo, 0);
#ifdef BITCHX
		}
		if (ptr->notes && do_hook(CDCC_NOTE_LIST, "%s %s %s", type_msg, chan, ptr->notes))
		{
			malloc_sprintf(&string, "         %s", ptr->notes);
			send_text(chan, string, do_notice_list?"NOTICE":NULL, do_cdcc_echo, 0);
		}
#endif
	}
#ifdef BITCHX
	do_hook(CDCC_POSTPACK_LIST, "%s %s %s %d %d %d %d %d %s %s %s %s %lu %s", type_msg, chan, get_server_nickname(from_server), cdcc_numpacks, get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR), numqueue, get_int_var(DCC_QUEUE_LIMIT_VAR), 
		mrate_out, bytes_out, mrate_in, bytes_in, total_size_of_packs, speed_out);
#endif
	message_from(NULL, LOG_CRAP);				
	new_free(&chan);
	new_free(&string);
	return 0;
}
		
/* display CDCC help */
static int l_help(char *args, char *rest)
{
	int i;
	
	if (args) {
		for (i = 0; *local[i].name; i++) {
			if (!my_stricmp(args, local[i].name)) {
				put_it("%s: %-9s - %s", cparse(get_string_var(CDCC_PROMPT_VAR)),local[i].name, local[i].help);
				return 0;
			}
		}
		put_it("%s: unknown command \002%s\002", cparse(get_string_var(CDCC_PROMPT_VAR)),args);
		return 0;
	} else 
		for (i = 0; *local[i].name; i++)
			put_it("%s: %-9s - %s", cparse(get_string_var(CDCC_PROMPT_VAR)),local[i].name, local[i].help);
	
	return 0;
}

/* notify the current channel that packs are offered */
static int l_notice(char *args, char *rest)
{
	char *string = NULL, *chan = NULL;
	char mrate_out[30];
	char mrate_in[30];
	char bytes_out[30];
	char bytes_in[30];
	char speed_out[30];	
	
	if (!get_current_channel_by_refnum(0) || !cdcc_numpacks || (args && *args && !is_channel(args))) {
		put_it("%s: you %s",cparse(get_string_var(CDCC_PROMPT_VAR)),
			cdcc_numpacks ? "are not on a channel!" :
				   "have no packs offered!");
		return 0;
	}
	
	if (args && *args)
		malloc_strcpy(&chan, args);
	else
		malloc_strcpy(&chan, get_current_channel_by_refnum(0));

	message_from(chan, LOG_CRAP);	
#ifdef BITCHX
	sprintf(mrate_out, "%1.3g", dcc_max_rate_out);
	sprintf(mrate_in, "%1.3g", dcc_max_rate_in);
	sprintf(bytes_out, "%1.3g", dcc_bytes_out);
	sprintf(bytes_in, "%1.3g", dcc_bytes_in);
	sprintf(speed_out, "%1.3g", cdcc_minspeed);
	if (do_hook(CDCC_PREPACK_LIST, "%s %s %s %d %d %d %d %d %s %s %s %s %lu %s", "NOTICE", chan, get_server_nickname(from_server), cdcc_numpacks, get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR), numqueue, get_int_var(DCC_QUEUE_LIMIT_VAR), mrate_out, bytes_out, mrate_in, bytes_in, total_size_of_packs, speed_out))
	{
#endif
		malloc_sprintf(&string, "\037[\037cdcc\037]\037 \002%d\002 file%s offered\037-\037 \037\"\037/ctcp \002%s\002 cdcc list\037\"\037 for pack list",   
			cdcc_numpacks, plural(cdcc_numpacks), get_server_nickname(from_server));
		send_text(chan, string, "NOTICE", do_cdcc_echo, 0);
	
#ifdef BITCHX
	}
#endif	

#ifdef BITCHX
	do_hook(CDCC_POSTPACK_LIST, "%s %s %s %d %d %d %d %d %s %s %s %s %lu %s", "NOTICE", chan, get_server_nickname(from_server), cdcc_numpacks, get_int_var(DCC_SEND_LIMIT_VAR)-dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR), numqueue, get_int_var(DCC_QUEUE_LIMIT_VAR), 
		mrate_out, bytes_out, mrate_in, bytes_in, total_size_of_packs, speed_out);
#endif
	message_from(NULL, LOG_CRAP);
	new_free(&chan);
	new_free(&string);
	return 0;		
}
		
/* display entries in your send queue */
static int l_queue(char *args, char *rest)
{
	queue *ptr, *del, *prev = NULL;
	int num = 1;
	char *command = NULL;	
	int count = 0;

	if (!queuelist) 
	{
		put_it("%s: there are no queue entries",cparse(get_string_var(CDCC_PROMPT_VAR)));
		return 0;
	}

	if (args && *args)
		command = next_arg(args, &args);
	if ((command == NULL) || !my_stricmp(command, "LIST"))
	{
		for (ptr = queuelist; ptr; ptr = ptr->next, num++) 
		{
			if (command && rest && *rest && !match(rest, ptr->nick))
				continue;
			if (command)
			{
#ifdef BITCHX
				if (do_hook(CDCC_QUEUE_LIST, "%s %s %d %d %s", ptr->nick, my_ctime(ptr->time), ptr->num, ptr->numfiles, ptr->desc))
#endif
					put_it("#\002%2d\002 nick: \037%9s\037 (\002%d\002 file%s)",
						num, ptr->nick, ptr->numfiles,
						plural(ptr->numfiles));
			} else
				count++;
		}
		if (count && !command)
			put_it("%s: %d entries in the queue", cparse(get_string_var(CDCC_PROMPT_VAR)), count);
		return 0;
	} 
	else if (!my_stricmp(command, "REMOVE"))
	{	
		char *n;
		int success = 0;
		n = rest;
		if (!n || !*n)
			return 0;
		for (ptr = queuelist; ptr;)
		{
			del = ptr;
			ptr = ptr->next;
			count++;
			if (matchmcommand(n, count) || match(n, del->nick))
			{
				if (prev) 
					prev->next=del->next;
				else 
					queuelist=del->next;
				new_free(&del->file);
				new_free(&del->nick);
				new_free(&del->desc);
				new_free((char **)&del);                                        
				success++;
			} else
				prev = del;
		}
		put_it("%s: deleted %d of %d entries from the queue", cparse(get_string_var(CDCC_PROMPT_VAR)), success, count);
		
	}
	else
		put_it("%s: /Cdcc queue remove #|nick /Cdcc queue list [nick]", cparse(get_string_var(CDCC_PROMPT_VAR)));
	return 0;
} 

/* save all of your offered packs */
static int l_save(char *args, char *rest)
{
	FILE *file;
	char *name = NULL, *expand = NULL;
	pack *ptr;
	int count = 0;
	char *fn;
		
	if (!offerlist) 
	{
		put_it("%s: you have no packs offered", cparse(get_string_var(CDCC_PROMPT_VAR)));
		return 0;
	}
	if (args)
		fn = args;
	else
		fn = ".cdcc.save";
		
	malloc_sprintf(&expand, "~/%s", fn);
	name = expand_twiddle(expand);
	new_free(&expand);

#ifdef WINNT
	if (!name || !(file = fopen(name, "wt"))) 
#else
	if (!name || !(file = fopen(name, "w"))) 
#endif
	{
		put_it("%s: couldn't open \"%s\"", cparse(get_string_var(CDCC_PROMPT_VAR)),name?name:"");
		new_free(&name);
		return 0;
	}
	
	for (ptr = offerlist; ptr; ptr = ptr->next) 
	{
		fprintf(file, "%s\n", ptr->file);
		fprintf(file, "%s %d\n", ptr->desc, ptr->numfiles);
		fprintf(file, "%s\n", ptr->notes?ptr->notes:empty_string);
		fprintf(file, "%d %ld 0.00 %d\n", ptr->gets, ptr->size, ptr->server);
		count++;
	}
		
	fclose(file);
	put_it("%s: \002%d\002 pack%s saved to %s", cparse(get_string_var(CDCC_PROMPT_VAR)),
		count, plural(count), name);
	new_free(&name);

	return 0;
} 

/* load packs from cdcc.save */
static int l_load(char *args, char *rest)
{
	FILE *file;
	char *buffer = NULL, *expand = NULL, *temp;
	pack *ptr, *last = NULL;
	char *p, *q;
	int count = 0;
	
	malloc_sprintf(&expand, "~/%s", args ? args: ".cdcc.save");
	buffer = expand_twiddle(expand);
	new_free(&expand);

	if (!(file = fopen(buffer, "rt"))) 
	{
		put_it("%s: couldn't open \"%s\"",  cparse(get_string_var(CDCC_PROMPT_VAR)), buffer);
		new_free(&buffer);
		return 0;
	}

	for (ptr = offerlist; ptr; ptr = ptr->next)
		last = ptr;
	
	new_free(&buffer);
	buffer = new_malloc(BIG_BUFFER_SIZE+1);
		
	while (fgets(buffer, BIG_BUFFER_SIZE, file)) {
		p = buffer;
		ptr = (pack *) new_malloc(sizeof(pack));
		ptr->num = ++cdcc_numpacks;
		chop(buffer, 1);
		malloc_strcpy(&ptr->file, buffer);

		fgets(buffer, BIG_BUFFER_SIZE, file);
		chop(buffer, 1);

		temp = strrchr(buffer, ' ');
		*temp = '\0';
		temp++;
		malloc_strcpy(&ptr->desc, buffer);

		if (*temp && !isdigit(*temp))
		{
			put_it("%s: not a cdcc pack aborting",  cparse(get_string_var(CDCC_PROMPT_VAR)));
			new_free(&ptr->file);
			new_free((char **)&ptr);
			fclose(file);
			return 0;
		}
		ptr->numfiles = atoi(temp);

		fgets(buffer, BIG_BUFFER_SIZE, file);
		chop(buffer, 1);
		if (*buffer)
			malloc_strcpy(&ptr->notes, buffer);
	

		fgets(buffer, BIG_BUFFER_SIZE, file);
		chop(buffer, 1);

		if ((q = next_arg(p, &p)))
			ptr->gets = my_atol(q);
		if ((q = next_arg(p, &p)))
			ptr->size = my_atol(q);
		next_arg(p, &p); /* skip over min speed for now */
		if ((q = next_arg(p, &p)))
			ptr->server = my_atol(q);

/*		ptr->gets = atoi(strtok(buffer, " "));*/
/*		ptr->size = atol(strtok(NULL, " "));*/
/*		ptr->minspeed = atof(strtok(NULL,"\r")); */

		total_size_of_packs += ptr->size;	
		ptr->next = NULL;
		
		if (last) {
			last->next = ptr;
			last = ptr;
		} else {
			offerlist = ptr;
			last = offerlist;
		}
		count++;
	}
		
	fclose(file);
	put_it("%s: \002%d\002 pack%s loaded",  cparse(get_string_var(CDCC_PROMPT_VAR)), count, plural(count));
	set_int_var(_CDCC_PACKS_OFFERED_VAR, cdcc_numpacks);
	new_free(&buffer);

	return 0;
} 
	
	
 
/* --- Misc functions --- */

/* add file/files to a pack */
static void add_files(char *args, char *rest)
{
	char *thefile = NULL, *expand = NULL, *path = NULL, *pptr;
	char *temp = NULL, *filebuf = NULL;
	char *fptr = NULL, *f_path = NULL;

	DIR *dptr;
	struct dirent *dir;
	struct stat statbuf;

	pptr = path = m_strdup(rest);
	temp = new_malloc(BIG_BUFFER_SIZE + 1);
		
	newpack = (pack *) new_malloc(sizeof(pack));

	while((thefile = next_arg(path, &path)))
	{
		if ((fptr = strrchr(thefile, '/')))
		{
			*fptr++ = '\0';
			f_path = m_strdup(thefile);
		}
		else 
		{
			fptr = thefile;
			f_path = m_strdup("~");
		}
		expand = expand_twiddle(f_path);
		dptr = opendir(expand);
		new_free(&f_path);
		if (!dptr) {
			put_it("%s: you cannot access dir %s",  cparse(get_string_var(CDCC_PROMPT_VAR)),expand);
			new_free(&expand);
			new_free(&pptr); /* path free */
			new_free(&filebuf);
			new_free(&temp); 
			new_free((char **)&newpack);
			return;
		}
		while ((dir = readdir(dptr))) {
			if (!dir->d_ino || !match(fptr, dir->d_name))
				continue;
			sprintf(temp, "%s/%s", expand, dir->d_name);
			if (filebuf)
				malloc_strcat(&filebuf, " ");
			malloc_strcat(&filebuf, temp);
			stat(temp, &statbuf);
			if (S_ISDIR(statbuf.st_mode))
				continue;
			newpack->size += statbuf.st_size;
			newpack->numfiles++;
			total_size_of_packs += statbuf.st_size;
		}
	}

	if (!newpack->numfiles) {
		put_it("%s: no files found, aborting...",  cparse(get_string_var(CDCC_PROMPT_VAR)));
		new_free(&expand);
		new_free(&pptr);
		new_free(&filebuf);
		new_free(&temp); 
		new_free((char **) &newpack);
		return;
	}
	
	newpack->file = new_malloc(strlen(filebuf) + 1);
	newpack->num = ++cdcc_numpacks;
	newpack->server = from_server;
	set_int_var(_CDCC_PACKS_OFFERED_VAR, cdcc_numpacks);
	strcpy(newpack->file, filebuf);
	malloc_sprintf(&temp, "Description of pack #%d : ", cdcc_numpacks);
	add_wait_prompt(temp, add_desc, "", WAIT_PROMPT_LINE);
	new_free(&expand);
	new_free(&filebuf);
	new_free(&pptr);
	new_free(&temp);
	
	return;
}

/* add a notes type description to the pack */
static void add_note(char *args, char *rest)
{
	if (rest && *rest)
	{
		malloc_strcpy(&newpack->notes, rest);
		put_it("%s: added note to pack #\002%d\002 %s",  cparse(get_string_var(CDCC_PROMPT_VAR)), newpack->num, newpack->notes);
	}
}

/* add a description to the new pack, and add to list */
static void add_desc(char *args, char *rest)
{
	pack *ptr, *last = NULL; 
	char size[20];
	char *temp = NULL;
		
	malloc_strcpy(&newpack->desc, rest);

	for (ptr = offerlist; ptr; ptr = ptr->next)
		last = ptr;

	if (last)
		last->next = newpack;
	else
		offerlist = newpack;
	newpack->next = NULL;
			
	if (newpack->size / 1024 > 999)
		sprintf(size, "\002%3.2f\002mb",
		(double) (newpack->size / 1024) / 1024);
	else
		sprintf(size, "\002%3.2f\002kb",
			(double) newpack->size / 1024); 
	put_it("%s: added pack #\002%d\002, \002%d\002 file%s (%s)", cparse(get_string_var(CDCC_PROMPT_VAR)),
		newpack->num, newpack->numfiles,
		plural(newpack->numfiles == 1), size);
	malloc_sprintf(&temp, "Notes for pack #%d : ", cdcc_numpacks);
	add_wait_prompt(temp, add_note, "", WAIT_PROMPT_LINE);
	new_free(&temp);
	return;
}

/* handle the actual removing of packs / all packs */
static void del_pack(char *args, char *rest)
{
	pack *ptr, *last = offerlist;
	int packnum;
	int num = 0;
			
	if (!rest || !*rest)
	{
		put_it("%s: No pack specified for removal", cparse(get_string_var(CDCC_PROMPT_VAR)));
		return;
	}
	if (*rest == '*') {
		cdcc_numpacks = 0;
		for (ptr = last = offerlist; last;) {
			ptr = last->next;
			new_free(&last->desc);
			new_free(&last->notes);
			new_free(&last->file);
			new_free((char **) &last);
			last = ptr;
		}
		offerlist = NULL;
		total_size_of_packs = 0;
		set_int_var(_CDCC_PACKS_OFFERED_VAR, 0);
		put_it("%s: removed all packs from offer list", cparse(get_string_var(CDCC_PROMPT_VAR)));
		return;
	}
	
	while (rest && *rest)
	{
		packnum = atoi(next_arg(rest, &rest));
		if (packnum <= 0) 
		{
			put_it("%s: invalid pack specification", cparse(get_string_var(CDCC_PROMPT_VAR)));
			continue;
		}

		for (ptr = offerlist; ptr; ptr = ptr->next) 
		{
			if (ptr->num == packnum) 
			{
				if (ptr != offerlist)
					last->next = ptr->next;
				else
					offerlist = ptr->next;

				new_free(&ptr->desc);
				new_free(&ptr->file);
				new_free(&ptr->notes);
				total_size_of_packs -= ptr->size;
				new_free((char **) &ptr);
				cdcc_numpacks--;
				set_int_var(_CDCC_PACKS_OFFERED_VAR, cdcc_numpacks);
				put_it("%s: removed pack \002%d\002 from offer list",cparse(get_string_var(CDCC_PROMPT_VAR)), packnum);	
				num++;
				break;
			}
			last = ptr;
		}
	}
	if (num)
	{
		for (ptr = offerlist,num = 1; ptr; ptr = ptr->next)
			ptr->num = num++;
	}
	else
		put_it("%s: pack \002%s\002 does not exist", cparse(get_string_var(CDCC_PROMPT_VAR)),rest); 
	return;
}

/* add a person to the dcc send queue */
static int add_to_queue(char *nick, pack *sendpack)
{
	queue *ptr = NULL, *last = NULL, *new = NULL;
	static char *lastnick = NULL;
	int count;
		
	if (!sendpack || !sendpack->file)
	{
		put_it("%s: ERROR occured in cdcc add to queue", cparse(get_string_var(CDCC_PROMPT_VAR)));
		return 0;
	}
	if (queuelist) 
	{
		for (new = queuelist, count = 1; new; new = new->next)
		{
			if (!my_stricmp(nick, new->nick) && !my_stricmp(sendpack->file, new->file))
			{
				if (!lastnick || my_stricmp(lastnick, nick))
					send_to_server("NOTICE %s :\002CDCC\002: You're already Queued for %s at position %d", nick, new->desc, count);
				put_it("%s: Already queued %d files for %s at %d", cparse(get_string_var(CDCC_PROMPT_VAR)), sendpack->numfiles, nick, count);
				return 0;
			}
		}
	}
	new = (queue *) new_malloc(sizeof(queue));
	malloc_strcpy(&new->nick, nick);
	malloc_strcpy(&new->file, sendpack->file);
	malloc_strcpy(&new->desc, sendpack->desc);
	new->time = time(NULL);
	new->numfiles = sendpack->numfiles;
	new->server = from_server;
	new->next = NULL;
	sendpack->gets++;
		
	for (ptr = queuelist, count = 1; ptr; ptr = ptr->next, count++)
		last = ptr;

	if (last)
		last->next = new;
	else
		queuelist = new;
	numqueue++;
	put_it("%s: Queue position %d queuing %d files for %s", cparse(get_string_var(CDCC_PROMPT_VAR)), numqueue, sendpack->numfiles, nick);
	if (!lastnick || (lastnick && my_stricmp(lastnick, nick)))
		malloc_strcpy(&lastnick, nick);
	return 1;
}
	
/* check queue & send files... called by irc.c io() */
void dcc_sendfrom_queue(void) 
{
	queue *ptr = queuelist;
	char *dccinfo = NULL, *file = NULL, *q, *temp = NULL;
	int old_server = from_server;
	
	if (!ptr)
		return;	
	if (dcc_active_count && dcc_active_count >= get_int_var(DCC_SEND_LIMIT_VAR))
		return;	

	put_it("%s: sending \037%s\037 \002%d\002 file%s from queue", cparse(get_string_var(CDCC_PROMPT_VAR)),
		ptr->nick, ptr->numfiles,
		plural(ptr->numfiles));

	malloc_strcpy(&file, ptr->file);
	q = temp = file;

	if (ptr->server < number_of_servers && server_list[ptr->server].connected)
		from_server = ptr->server;
	for (temp = next_arg(file, &file); temp && *temp; temp = next_arg(file, &file)) 
	{
		malloc_sprintf(&dccinfo, "%s %s", ptr->nick, temp);
		dcc_filesend(NULL, dccinfo);
	}
	new_free(&dccinfo);

	queuelist = ptr->next;
	new_free(&ptr->nick);
	new_free(&ptr->file);
	new_free((char **) &ptr);
	new_free(&q);
	numqueue--;
	from_server = old_server;	

	return;
}

static time_t plist_last_time = 0;
static void get_minspeed(char *args, char *rest)
{
	if (rest && *rest)
		cdcc_minspeed = strtod(rest, NULL);
	if (cdcc_minspeed)
	{
		put_it("%s: Minspeed value to %1.4g KB/s", cparse(get_string_var(CDCC_PROMPT_VAR)),cdcc_minspeed);
		if (!get_int_var(_CDCC_MINSPEED_TIME_VAR))
			put_it("%s: Make sure and /set _CDCC_MINSPEED_TIME as well", cparse(get_string_var(CDCC_PROMPT_VAR)));
	}
	else
		cdcc_minspeed = 0.0;
	
}

static int l_minspeed(char *args, char *rest)
{
char *temp = NULL;
	malloc_sprintf(&temp, "%s min-speed (0 to disable): ", cparse(get_string_var(CDCC_PROMPT_VAR)));
	if (args && *args)
		get_minspeed(NULL, args);
	else
		add_wait_prompt(temp, get_minspeed, "", WAIT_PROMPT_LINE);
	new_free(&temp);
	return 0;
}

static void get_ptimer(char *args, char *rest)
{
	if (rest && *rest)
		ptimer = strtoul(rest, NULL, 10);
	ptimer *= 60;
	if (ptimer)
		put_it("%s: Ptimer interval %d minutes", cparse(get_string_var(CDCC_PROMPT_VAR)),ptimer/60);
	else
		plist_last_time = 0;
}

static int l_timer(char *args, char *rest)
{
char *temp = NULL;
	malloc_sprintf(&temp, "%s p-timer interval(s) (0 to disable): ", cparse(get_string_var(CDCC_PROMPT_VAR)));
	if (args && *args)
		get_ptimer(NULL, args);
	else
		add_wait_prompt(temp, get_ptimer, "", WAIT_PROMPT_LINE);
	new_free(&temp);
	return 0;
}

static void get_pchannel(char *args, char *rest)
{
	if (rest && *rest && is_channel(rest))
		malloc_strcpy(&public_channel, rest);
	else if (rest && *rest && *rest == '*')
	{
		int i = get_window_server(0);
		ChannelList *chan;
		if (i != -1)
		{
			new_free(&public_channel);
			for (chan = server_list[i].chan_list; chan; chan = chan->next)
			{
				malloc_strcat(&public_channel, chan->channel);
				if (chan->next)
					malloc_strcat(&public_channel, ",");
			}
		} else
			new_free(&public_channel);
	}
	else
		new_free(&public_channel);
	if (public_channel)
		put_it("%s: Public timer channel(s) are [%s]", cparse(get_string_var(CDCC_PROMPT_VAR)),public_channel);
	else
		put_it("%s: Disabled %s public timer channel(s)", cparse(get_string_var(CDCC_PROMPT_VAR)), cparse(get_string_var(CDCC_PROMPT_VAR)));
}

static int l_channel(char *args, char *rest)
{
	if (args && *args)
		get_pchannel(NULL, args);
	else
		if (public_channel)
			put_it("%s: Public timer channel is [%s]", cparse(get_string_var(CDCC_PROMPT_VAR)),public_channel);
		else
			put_it("%s: Disabled %s public timer channel", cparse(get_string_var(CDCC_PROMPT_VAR)), cparse(get_string_var(CDCC_PROMPT_VAR)));
	return 0;
}

void cdcc_timer_offer(void)
{
	time_t timenow = time(NULL);
	if (!offerlist || !ptimer)
		return;
	if (timenow - plist_last_time > ptimer)
	{
		plist_last_time = timenow;
		if (do_notice_list)
			l_notice(public_channel, NULL);
		else
			l_plist(public_channel, NULL);
	}
}

static void add_note1(unsigned long pnum, char *note)
{
pack *this_pack = NULL;
int i;
	if (pnum && note)
	{
		for (i = 1, this_pack = offerlist; this_pack; this_pack = this_pack->next, i++)
			if (i == pnum)
				break;
		if (this_pack)
			malloc_strcpy(&this_pack->notes, note);
		else
			put_it("%s: Invalid pack number %ld", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);

	}
	else
		put_it("%s: Invalid pack number %ld", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);
}

static void add_describe(unsigned long pnum, char *describe)
{
pack *this_pack = NULL;
int i;
	if (pnum && describe)
	{
		for (i = 1, this_pack = offerlist; this_pack; this_pack = this_pack->next, i++)
			if (i == pnum)
				break;
		if (this_pack)
			malloc_strcpy(&this_pack->desc, describe);
		else
			put_it("%s: Invalid pack number %ld", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);

	}
	else
		put_it("%s: Invalid pack number %ld", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);
}

static unsigned long got_pnum = 0;
static unsigned long got_dnum = 0;

static void get_pnote1(char *args, char *rest)
{
	if (got_pnum && rest)
		add_note1(got_pnum, rest);
	got_pnum = 0;
}

static void get_desc(char *args, char *rest)
{
	if (got_dnum && rest)
		add_describe(got_dnum, rest);
	got_dnum = 0;
}

static void get_pnote(char *args, char *rest)
{
unsigned long pnum = 0;
char *temp = NULL;
char *p;
	if ((p = next_arg(rest, &rest)))
		pnum = strtoul(p, NULL, 10);
	if (rest && *rest)
		add_note1(pnum, rest);
	else
	{
		got_pnum = pnum;
		malloc_sprintf(&temp, "%s note to add to pack %ld ", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);
		add_wait_prompt(temp, get_pnote1, "", WAIT_PROMPT_LINE);	
		new_free(&temp);
	}
}

static void get_describe(char *args, char *rest)
{
unsigned long pnum = 0;
char *temp = NULL;
char *p;
	if ((p = next_arg(rest, &rest)))
		pnum = strtoul(p, NULL, 10);
	if (rest && *rest)
		add_describe(pnum, rest);
	else
	{
		got_dnum = pnum;
		malloc_sprintf(&temp, "%s description to add to pack %ld ", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);
		add_wait_prompt(temp, get_desc, "", WAIT_PROMPT_LINE);
		new_free(&temp);
	}
}

static int l_note(char *args, char *rest)
{
char *temp = NULL;
	if (!offerlist)
		return 0;
	malloc_sprintf(&temp, "%s add note to pack # ", cparse(get_string_var(CDCC_PROMPT_VAR)));
	if (args && *args)
		get_pnote(NULL, args);
	else
		add_wait_prompt(temp, get_pnote, "", WAIT_PROMPT_LINE);
	new_free(&temp);
	return 1;
}

static int l_describe(char *args, char *rest)
{
char *temp = NULL;
	if (!offerlist)
		return 0;
	malloc_sprintf(&temp, "%s change description of pack # ", cparse(get_string_var(CDCC_PROMPT_VAR)));
	if (args && *args)
		get_describe(NULL, args);
	else
		add_wait_prompt(temp, get_describe, "", WAIT_PROMPT_LINE);
	new_free(&temp);
	return 1;
}

static int l_type(char *args, char *rest)
{
	if (args && *args)
		do_notice_list = do_notice_list ? 0 : 1;
	put_it("%s: type of output for offer set to [%s]", cparse(get_string_var(CDCC_PROMPT_VAR)), do_notice_list? "Notice":"privmsg");
	return 0;
}

static int l_echo(char *args, char *rest)
{
	if (args && *args)
		do_cdcc_echo = do_cdcc_echo ? 0 : 1;
	put_it("%s: local echo set to [%s]", cparse(get_string_var(CDCC_PROMPT_VAR)), on_off(do_cdcc_echo));
	return 0;
}
