/* 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 (edwards@bitchx.dimension6.com) to    */
/* interface cleanly and nicely with BitchX. Blame all bugs on me      */
/* instead of Ananda                                                   */

#define CDCC_FLUD

#include "irc.h"
static char cvsrevision[] = "$Id: cdcc.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(cdcc_c)
#include "ircaux.h"
#include "struct.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 "who.h"

#include "cdcc.h"
#include "misc.h"

#define MAIN_SOURCE
#include "modval.h"

#ifdef WANT_CDCC

/* external ircII stuff */
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 *);

static int l_secure(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 *);

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

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

/* 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_stats(char *, char *);
static int l_type(char *, char *);
/* add a person to the dcc queue */
int BX_add_to_queue(char *, 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" },
	{ "HELP",	l_help,		"cdcc help" },
	{ "MINSPEED",	l_minspeed,	"minspeed for cdcc ( #.##) [mintime in seconds]" },
	{ "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" },
	{ "STATS",	l_stats,	"display cdcc statistics" },
	{ "SECURE",	l_secure,	"adds a password to a pack"},
	{ "ON",		NULL,		"cdcc offers on" },
	{ "OFF",	NULL,		"cdcc offers off" },
	{ empty_string,		NULL,		empty_string } 
};
#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" },
#if 0
	{ "TRESEND",	r_trsend,	"have CDCC tresend pack #N"},
	{ "TSEND",	r_tsend,	"have CDCC tsend pack #N" },
#endif
	{ "LIST",	r_list,		"list of offered packs" },
	{ "INFO",	r_info,		"info on pack #N" },
	{ "QUEUE",	r_queue,	"queue status for us" },
	{ empty_string,		NULL,		empty_string }, 
};
#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;


extern double dcc_max_rate_out, dcc_bytes_out, dcc_max_rate_in, dcc_bytes_in;

#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_list(NULL, NULL);
		put_it("%s: CDCC is [\002%s\002]. Use \002/cdcc help\002 to get help with cdcc", cparse(get_string_var(CDCC_PROMPT_VAR)), on_off(get_int_var(CDCC_VAR)));
		return;
	}
	if (!my_stricmp(cmd, "ON") || !my_stricmp(cmd, "OFF"))	
	{
		set_int_var(CDCC_VAR, !my_stricmp(cmd, "ON") ? 1 : 0);
		put_it("%s: offers \002%s\002", cparse(get_string_var(CDCC_PROMPT_VAR)), cmd);
		return;
	}
	
	for (i = 0; *local[i].name; i++) 
	{
		if (!my_stricmp(local[i].name, cmd) && local[i].function) 
		{  
			local[i].function(rest, args);
			return;
		}
	}
	put_it("%s: unknown command \002%s\002", cparse(get_string_var(CDCC_PROMPT_VAR)), cmd);
	return;
}
			
static int l_help(char *cmd, char *args)
{
int i;
char buffer[BIG_BUFFER_SIZE+1];
	if (!cmd)
	{
		int c = 0;
		*buffer = 0;
		for (i = 0; *local[i].name; i++)
		{
			strmcat(buffer, local[i].name, BIG_BUFFER_SIZE);
			strmcat(buffer, space, BIG_BUFFER_SIZE);
			if (++c == 5)
			{
				put_it("%s", convert_output_format("$G $[13]0 $[13]1 $[13]2 $[13]3 $[13]4", "%s", buffer));
				*buffer = 0;
				c = 0;
			}
		}
		if (c)
			put_it("%s", convert_output_format("$G $[13]0 $[13]1 $[13]2 $[13]3 $[13]4", "%s", buffer));
		userage("CDCC help", "%R[%ncommand%R]%n to get help on specific commands");
	}	
	else
	{
		int done = 0;
		for (i = 0; *local[i].name; i++)
		{
			if (my_stricmp(local[i].name, cmd))
				continue;
			sprintf(buffer, "CDCC %s", cmd);
			userage(buffer, local[i].help?local[i].help:" - No help available");
			done++;
		}
		if (!done)
			put_it("%s", convert_output_format("$G CDCC - No such command", NULL, NULL));
	}
	return 0;
}

/* parse a remote message CDCC command */
char *msgcdcc(char *from, char *to, char *args)
{
	int i;
	char *secure = NULL;
	char *cdcc, *rest, *cmd, *temp = NULL;

	temp = LOCAL_COPY(args);	
	cdcc = next_arg(temp, &temp);
	if (!cdcc || (my_strnicmp(cdcc, "XDCC", 4) && my_strnicmp(cdcc, "CDCC", 4)))
	 	return args;
	if (!get_int_var(CDCC_VAR))
		return args;
	if ((check_ignore(from, FromUserHost, to, IGNORE_CDCC, NULL) == IGNORED))
		return args;
			
	if (!check_flooding(from, CDCC_FLOOD, args, NULL) || !offerlist)
		return NULL;


	if ((secure = get_string_var(CDCC_SECURITY_VAR)))
	{
		UserList *tmp;
		char *pass = NULL;
		if (*secure == '0' && strlen(secure) == 1)
			goto got_good_pass;
		if (temp && *temp)
			pass = strrchr(temp, ' ');
		if (pass && *pass)
		{ 
			pass++;
			if (*pass && !my_stricmp(pass, secure))
			{
				*pass-- = 0;
				goto got_good_pass;
			}
		}
#ifdef WANT_USERLIST
		if (!(tmp = lookup_userlevelc("*", FromUserHost, "*", NULL)) || !(tmp->flags & ADD_DCC))
			return args;
#else
		return args;
#endif
	}
got_good_pass:
	cmd = next_arg(temp, &temp);
	if (!cmd) 
		return args;

	rest = temp;

	for (i = 0; *remote[i].name; i++) 
	{
		if (!my_stricmp(cmd, remote[i].name)) 
		{
			remote[i].function(from, rest);
			return NULL;
		}
	}
	queue_send_to_server(from_server, "NOTICE %s :try /ctcp %s cdcc help",from, get_server_nickname(from_server));
	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)
			queue_send_to_server(from_server, "NOTICE %s :%d file%s %d gets %ld size %2.4f minspeed %ld time added",args, ptr->numfiles, plural(ptr->numfiles), ptr->gets, ptr->size, ptr->minspeed, ptr->timeadded); 
		else
			queue_send_to_server(from_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);
			queue_send_to_server(from_server, "NOTICE %s :You have %d packs queued at %s", args, num, buffer);
		}
		else
			queue_send_to_server(from_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;
	int count = 0;
				
	if (*command == 'T')
		tdcc = 1;	
	if (!args || !*args)
		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((unsigned char)*temp) || (*(temp+1) && isdigit((unsigned char)*(temp+1))))
		{
			if (*temp == '#')
				temp++;
			for (ptr = offerlist; ptr; ptr = ptr->next)
				if (matchmcommand(temp, ptr->num))
					break;
		}		
		if (ptr)
		{
			if (maxdcc && get_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 count + queued_files;
				}
				queued_files += add_to_queue(args, command, ptr);
				do_hook(CDCC_SEND_NICK_LIST, "%s %s %s %d %d %d %s %s", args, "unknown", command, ptr->num, ptr->numfiles, ptr->gets, ptr->file, ptr->desc);

				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 = new_next_arg(file, &file); p && *p; p = new_next_arg(file, &file)) 
			{
				malloc_sprintf(&dccinfo, "%s \"%s\"", args, p);
				if (!my_stricmp(command, "SEND") || !my_stricmp(command, "TSEND"))
					dcc_filesend(command, dccinfo);
				else
					dcc_resend(command, dccinfo);
			}
			send_numpacks++;
			count++;
			ptr->gets++;
			do_hook(CDCC_SEND_NICK_LIST, "%s %s %s %d %d %d %s %s", args, "unknown", command, ptr->num, ptr->numfiles, ptr->gets, ptr->file, ptr->desc);
		}
		else
		{
			if (offerlist && (isdigit((unsigned char)*temp) || (*(temp+1) && (isdigit((unsigned char)*(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);
				else
					dcc_resend(command, dccinfo);
				count++;
			}
		}  
		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 count + queued_files;
}
 
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;
	char *password = NULL;
	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;
		if (args && *args && (!my_isdigit(args)))
			password = next_arg(args, &args);
		
		for (ptr = offerlist; ptr; ptr = ptr->next)
			if (matchmcommand(temp, ptr->num))
				break;
		if (ptr)
		{
			if (ptr->password && (!password || (password && strcmp(ptr->password, password))))
			{
				put_it("%s: Attempted get of secure pack %d from %s failed. [%s]", cparse(get_string_var(CDCC_PROMPT_VAR)), ptr->num, from, !password? "No Password": "Invalid Password");
				queue_send_to_server(from_server, "NOTICE %s :\002CDCC\002: Failed attempt to get secure pack %d", from, ptr->num);
				continue;
			}
			if (maxdcc && ((maxdcc - get_active_count()) < ptr->numfiles || get_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);
						queue_send_to_server(from_server, "NOTICE %s :\002CDCC\002: all slots full... Some requests ignored. Added to queue for %d requests", from, queued_files);
					}
					else
						queue_send_to_server(from_server, "NOTICE %s :\002CDCC\002: all dcc and queue slots full... Try again later", from);

					return 0;
				}
				queued_files += add_to_queue(from, command, ptr);
				count++;
				do_hook(CDCC_SEND_NICK_LIST, "%s %s %s %d %d %d %s %s", from, FromUserHost, command, ptr->num, ptr->numfiles, ptr->gets, ptr->file, ptr->desc);
				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_resume(command, dccinfo);
#endif
				else
					dcc_filesend(command, dccinfo);
			}
			send_numpacks++;
			ptr->gets++;
			do_hook(CDCC_SEND_NICK_LIST, "%s %s %s %d %d %d %s %s", from, FromUserHost, command, ptr->num, ptr->numfiles, ptr->gets, ptr->file, ptr->desc);
		}
		else if (!count)
			queue_send_to_server(from_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);
		queue_send_to_server(from_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

#if 0
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;
}
#endif

/* 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) 
	{
		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)-get_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))
		{
			queue_send_to_server(from_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))
				queue_send_to_server(from_server, "NOTICE %s :    [%d pack%s %d/%d slots open]", from, 
					cdcc_numpacks, plural(cdcc_numpacks), get_int_var(DCC_SEND_LIMIT_VAR)-get_active_count(), get_int_var(DCC_SEND_LIMIT_VAR));
			else
				queue_send_to_server(from_server, "NOTICE %s :    [%d pack%s]", from, cdcc_numpacks,plural(cdcc_numpacks));
		}
	 	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)); 
				
		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))
		{
			queue_send_to_server(from_server, "NOTICE %s :#%d \037(\037%10s\037:\037\002%4d\002 get%s\037)\037 %s",
				from, ptr->num, size, ptr->gets, plural(ptr->gets), 
				ptr->desc ? ptr->desc : "no description");
		}
		if (ptr->notes && do_hook(CDCC_NOTE_LIST, "%s %s %s", "NOTICE", from, ptr->notes))
			queue_send_to_server(from_server, "NOTICE %s :\t%s", from, ptr->notes);
		once++;
	}
	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)-get_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);
	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))
			{
				queue_send_to_server(from_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)];
		}
		queue_send_to_server(from_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 : empty_string);
		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, empty_string, WAIT_PROMPT_LINE, 1);
	}
	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  %s",
				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:empty_string);
		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, empty_string, WAIT_PROMPT_LINE, 1);
	}
	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)
		chan = LOCAL_COPY(args);
	else	
		chan = LOCAL_COPY(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);
	set_display_target(chan, LOG_CRAP);
	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)-get_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))
	{
		if (get_int_var(QUEUE_SENDS_VAR))
		{
			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));
			queue_send_to_server(from_server, "%s %s :%s", 
				do_notice_list?"NOTICE":"PRIVMSG", chan, string);
			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 - get_active_count(),
				maxdccs, maxqueue - numqueue, maxqueue);
			queue_send_to_server(from_server, "%s %s :%s", 
				do_notice_list?"NOTICE":"PRIVMSG", chan, string);
		}
		else
		{
			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 - get_active_count(),
				maxdccs, maxqueue - numqueue, maxqueue);
			send_text(chan, string, do_notice_list?"NOTICE":NULL, do_cdcc_echo, 0);
		}
	}
	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); 
				
		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))
		{
			if (get_int_var(QUEUE_SENDS_VAR))
			{
				malloc_sprintf(&string, "\037%%\037 #%-2d \037(\037%10s\037:\037\002%4d\002 get%s\037)\037 %s",
					ptr->num, size, ptr->gets, plural(ptr->gets), 
					ptr->desc ? ptr->desc : "no description");
				queue_send_to_server(from_server, "%s %s :%s", 
					do_notice_list?"NOTICE":"PRIVMSG", chan, string);
			}
			else
			{
				malloc_sprintf(&string, "\037%%\037 #%-2d \037(\037%10s\037:\037\002%4d\002 get%s\037)\037 %s",
					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);
			}
		}
		if (ptr->notes && do_hook(CDCC_NOTE_LIST, "%s %s %s", type_msg, chan, ptr->notes))
		{
			malloc_sprintf(&string, "\t%s", ptr->notes);
			if (get_int_var(QUEUE_SENDS_VAR))
				queue_send_to_server(from_server, "%s %s :%s", 
					do_notice_list?"NOTICE":"PRIVMSG", chan, string);
			else
				send_text(chan, string, do_notice_list?"NOTICE":NULL, do_cdcc_echo, 0);
		}
	}
	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)-get_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);
	reset_display_target();
	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));

	set_display_target(chan, LOG_CRAP);	
	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)-get_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))
	{
		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));
		if (get_int_var(QUEUE_SENDS_VAR))
			queue_send_to_server(from_server, "NOTICE %s :%s", chan, string);
		else
			send_text(chan, string, "NOTICE", do_cdcc_echo, 0);
	
	}

	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)-get_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);
	reset_display_target();
	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 && !wild_match(rest, ptr->nick))
				continue;
			if (command)
			{
				if (do_hook(CDCC_QUEUE_LIST, "%s %s %d %d %s", ptr->nick, my_ctime(ptr->time), ptr->num, ptr->numfiles, ptr->desc))
					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) || wild_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)
{
#ifdef PUBLIC_ACCESS
	bitchsay("This command has been disabled on a public access system");
	return;
#else
	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
#if defined(WINNT) || defined(__EMX__)
		fn = "cdcc.save";
#else
		fn = ".cdcc.save";
#endif		
	malloc_sprintf(&expand, "~/%s", fn);
	name = expand_twiddle(expand);
	new_free(&expand);

#if defined(WINNT) || defined(__EMX__)
	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:empty_string);
		new_free(&name);
		return 0;
	}
	fprintf(file, "#cdcc save file 1.0\n");	
	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 %lu 0.00 %d %lu %s\n", ptr->gets, ptr->size, ptr->server, (unsigned long)ptr->timeadded, ptr->password?ptr->password:empty_string);
		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;
#endif
} 

/* 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;
	int got_header = 0;
		
#if defined(WINNT) || defined(__EMX__)
	malloc_sprintf(&expand, "~/%s", args ? args: "cdcc.save");
#else
	malloc_sprintf(&expand, "~/%s", args ? args: ".cdcc.save");
#endif
	buffer = expand_twiddle(expand);
	new_free(&expand);

	if (!buffer || !(file = fopen(buffer, "rt"))) 
	{
		put_it("%s: couldn't open \"%s\"",  cparse(get_string_var(CDCC_PROMPT_VAR)), buffer ? buffer : expand);
		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;
		buffer[BIG_BUFFER_SIZE-1] = 0;
		chop(buffer, 1);
		if (!got_header)
		{
			if (my_strnicmp(p, "#cdcc save file 1.0", 16))
			{
				put_it("File is not a cdcc save file.");
				break;
			}
			got_header++;
			continue;
		}
		ptr = (pack *) new_malloc(sizeof(pack));
		ptr->num = ++cdcc_numpacks;
		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((unsigned char)*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);

		if ((q = next_arg(p, &p)))
			ptr->timeadded = my_atol(q);

		if (p && *p)
			ptr->password = m_strdup(q);

/*		ptr->gets = atoi(strtok(buffer, space));*/
/*		ptr->size = atol(strtok(NULL, space));*/
/*		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;
	char *temp = NULL, *filebuf = NULL;
	char *fptr = NULL, *f_path = NULL;

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

	path = alloca(strlen(rest)+1);
	strcpy(path, rest);
	
	temp = alloca(BIG_BUFFER_SIZE + 1);
	*temp = 0;

	f_path = alloca(BIG_BUFFER_SIZE + 1);
	*f_path = 0;
			
	newpack = (pack *) new_malloc(sizeof(pack));

	while((thefile = new_next_arg(path, &path)))
	{
		if (!thefile || !*thefile)
			break;
		if ((fptr = strrchr(thefile, '/')))
		{
			*fptr++ = 0;
			strcpy(f_path, thefile);
		}
		else 
		{
			fptr = thefile;
			strcpy(f_path, "~");
		}
		if ((expand = expand_twiddle(f_path)))
			dptr = opendir(expand);
		if (!dptr) 
		{
			put_it("%s: you cannot access dir %s. Attempting to continue",  cparse(get_string_var(CDCC_PROMPT_VAR)),expand ? expand : thefile);
			new_free(&expand);
			new_free(&filebuf);
			continue;
		}
		while ((dir = readdir(dptr))) 
		{
			if (!dir->d_ino || !wild_match(fptr, dir->d_name))
				continue;
			sprintf(temp, "%s/%s", expand, dir->d_name);
			stat(temp, &statbuf);
			sprintf(temp, "\"%s/%s\"", expand, dir->d_name);
			if (filebuf)
				malloc_strcat(&filebuf, space);
			malloc_strcat(&filebuf, temp);
			
			if (S_ISDIR(statbuf.st_mode))
				continue;
			newpack->size += statbuf.st_size;
			newpack->numfiles++;
			total_size_of_packs += statbuf.st_size;
		}
		closedir(dptr);
	}
	if (!newpack->numfiles) 
	{
		put_it("%s: no files found, aborting...",  cparse(get_string_var(CDCC_PROMPT_VAR)));
		new_free(&expand);
		new_free(&filebuf);
		new_free((char **) &newpack);
		return;
	}
	
	newpack->timeadded = now;
	newpack->num = ++cdcc_numpacks;
	newpack->server = from_server;
	set_int_var(_CDCC_PACKS_OFFERED_VAR, cdcc_numpacks);
	malloc_strcpy(&newpack->file, filebuf);

	sprintf(temp, "Description of pack #%d : ", cdcc_numpacks);
	add_wait_prompt(temp, add_desc, empty_string, WAIT_PROMPT_LINE, 1);

	new_free(&expand);
	new_free(&filebuf);
	
	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, empty_string, WAIT_PROMPT_LINE, 1);
	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(&last->password);
			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);
				new_free(&ptr->password);
				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 */
int BX_add_to_queue(char *nick, char *command, 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))
					queue_send_to_server(from_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 = now;
	new->numfiles = sendpack->numfiles;
	new->server = from_server;
	new->command = m_strdup(command);
	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, *temp = NULL;
	int old_server = from_server;
	int active =  0;	

	if (!ptr)
		return;	
	active = get_active_count();
	if (active && active >= 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));

	file = LOCAL_COPY(ptr->file);

	if (ptr->server < server_list_size() && is_server_connected(ptr->server))
		from_server = ptr->server;

	while ((temp = new_next_arg(file, &file)))
	{
		if (!temp || !*temp)
			break;
		malloc_sprintf(&dccinfo, "%s %s", ptr->nick, temp);
		if (ptr->command)
		{
			if (!my_stricmp(ptr->command, "RESEND") || !my_stricmp(ptr->command, "TRESEND"))
				dcc_resend(ptr->command, dccinfo);
#ifdef MIRC_BROKEN_DCC_RESUME
			else if (!my_stricmp(ptr->command, "RESUME"))
				dcc_resume(ptr->command, dccinfo);
#endif
			else
				dcc_filesend(NULL, dccinfo);
		}
		else
			dcc_filesend(ptr->command, dccinfo);
	}
	new_free(&dccinfo);

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

static time_t plist_last_time = 0;
static void get_minspeed(char *args, char *rest)
{
char *last = NULL;
unsigned long cdcc_mintime = 0;
	if (rest && *rest)
		cdcc_minspeed = strtod(rest, &last);
	if (cdcc_minspeed)
	{
		put_it("%s: Minspeed value to %1.4g KB/s", cparse(get_string_var(CDCC_PROMPT_VAR)),cdcc_minspeed);
		if (last && *last && isdigit((unsigned char)*last))
		{
			cdcc_mintime = strtod(last, NULL);
			set_int_var(_CDCC_MINSPEED_TIME_VAR, cdcc_mintime);
			put_it("%s: Minspeed time to %5d seconds", cparse(get_string_var(CDCC_PROMPT_VAR)), cdcc_mintime);
		}
		else 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, empty_string, WAIT_PROMPT_LINE, 1);
	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, empty_string, WAIT_PROMPT_LINE, 1);
	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 = get_server_channels(i); chan; chan = chan->next)
				m_s3cat(&public_channel, ",", chan->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)
{
	if (!offerlist || !ptimer)
		return;
	if (now - plist_last_time > ptimer)
	{
		plist_last_time = now;
		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)
		{
			if (note && *note)
				malloc_strcpy(&this_pack->notes, note);
			else
				new_free(&this_pack->notes);
		}
		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, empty_string, WAIT_PROMPT_LINE, 1);	
		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, empty_string, WAIT_PROMPT_LINE, 1);
		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, empty_string, WAIT_PROMPT_LINE, 1);
	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, empty_string, WAIT_PROMPT_LINE, 1);
	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;
}

static int l_stats(char *args, char *rest)
{
	char cdcc_minspeed_s[80];
	sprintf(cdcc_minspeed_s, "%1.3f", cdcc_minspeed);
	put_it("%s",convert_output_format("       %GÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ%K[%C    cdcc stat     %K]%GÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸", NULL));
	put_it("%s",convert_output_format("       %G³                                                                 ³", NULL));
	put_it("%s",convert_output_format("       %G³%gÖÄ%K[%Cp%ctimer  %K]%gÄÖ-%K[%Ct%cype     %K]%gÄ·Ä%K[%Ct%cotal %Cp%cacks%K]%gÄÖÄ%K[%Cs%cent  %K]%gÄ·Ä[%Cq%cueue%K]%gÄ·%G³", NULL));
	put_it("%s",convert_output_format("       %G³%gº %W$[-10]0 %gº  %W$[-10]1 %gº    %W$[-10]2 %gº %W$[-8]3 %gº %W$[-7]4 %gº%G³", "%d %s %d %d %d", ptimer, do_notice_list ?"notice":"privmsg", cdcc_numpacks, send_numpacks, numqueue));
	put_it("%s",convert_output_format("       %G³%gÓÄÄÄÄÄÄÄÄÄÄÄĽÄÄÄÄÄÄÄÄÄÄÄÄÄÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄĽÄÄÄÄÄÄÄÄÄÄÓÄÄÄÄÄÄÄÄĽ%G³", NULL));
	put_it("%s",convert_output_format("       %G³ CDCC channel                                                    ³", NULL));
	put_it("%s",convert_output_format("       %G³ %W$[63]0-%G ³", "%s", !public_channel ? "current channel": public_channel));
	put_it("%s",convert_output_format("       %gÖÄÄÄÄ%K[%C %c  %C %c    %K]%gÄÄÄÖÄÄÄ%K[%C %c   %C %c    %K]%gÄÄÄ·ÄÄÄÄÄÄÄÄÄÄ%K[%Ct%coggles%K]%gÄÄÄÄÄÄÄÄÄÄ·", NULL));
	put_it("%s",convert_output_format("       %gº %C %n    %W$[-6]0%n%R     %gº %C %n    %W$[-6]1%n%R     %gº   %Ct%nimer:   %W$[-3]2%n   %Ce%ncho:  %W$[-3]3 %gº", "1 1 %s %s", on_off(ptimer), on_off(do_cdcc_echo)));
	put_it("%s",convert_output_format("       %gº %C %n    %W$[-6]0%n%R     %gº %C %n    %W$[-6]1%n%R     %gº %Cm%ninspeed:  %W$[-3]2%n   %Cs%necure:%W$[-3]3 %gº", "1 1 %s %s", cdcc_minspeed == 0.0 ? "off":cdcc_minspeed_s, on_off(get_string_var(CDCC_SECURITY_VAR) ? 1 : 0)));
	put_it("%s",convert_output_format("       %gÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĽÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĽ", NULL));
	return 0;
}

/* 
 * TimeCawp needed this particular function so I coded it for him.
 * personally I don't think it's needed, but then who am I to say.
 */
 
BUILT_IN_FUNCTION(function_cdcc)
{
pack *ptr;
char *p;
int it = 0;
	if (!cdcc_numpacks)
		return m_strdup(empty_string);

	if (!(p = next_arg(input, &input)))
		return m_strdup(empty_string);
	if (!(it = my_atol(p)))
		return m_sprintf("%d", cdcc_numpacks);

	for (ptr = offerlist; ptr; ptr= ptr->next)
	{
		if (it == ptr->num)
			return m_sprintf("%d %d %lu %d %s", ptr->num, ptr->numfiles, ptr->size, ptr->gets, ptr->desc);
	}
	return m_strdup(empty_string);
}

BUILT_IN_FUNCTION(function_sendcdcc)
{
char *nick = NULL;
int count = 0;
int old_window_display = window_display;

	window_display = 0;
	if ((nick = next_arg(input, &input)))
		count = do_local_send("SEND", nick, input);
	window_display = old_window_display;
	return m_sprintf("%d", count);
}

static void add_password(unsigned long pnum, char *password)
{
pack *this_pack = NULL;
int i;
	if (pnum && password)
	{
		for (i = 1, this_pack = offerlist; this_pack; this_pack = this_pack->next, i++)
			if (i == pnum)
				break;
		if (this_pack)
		{
			if (password && *password)
				malloc_strcpy(&this_pack->password, password);
			else
			{
				new_free(&this_pack->password);
				put_it("%s: Removed passwd from %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);

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


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

static void get_password(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_password(pnum, rest);
	else
	{
		got_dnum = pnum;
		malloc_sprintf(&temp, "%s password to add to pack %ld: ", cparse(get_string_var(CDCC_PROMPT_VAR)), pnum);
		add_wait_prompt(temp, get_passwd, empty_string, WAIT_PROMPT_LINE, 1);
		new_free(&temp);
	}
}

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

int BX_get_num_queue(void)
{
	return numqueue;
}

#else /* WANT_CDCC */

/* this is required for functions.c to compile properly */

BUILD_IN_FUNCTION(function_cdcc)
{
	return m_strdup(empty_string);
}

BUILT_IN_FUNCTION(function_sendcdcc)
{
	return m_strdup(empty_string);
}

int get_num_queue(void)
{
	return 0;
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1