/* ARISA - Admin Interface - Packlist Menu
 * Copyright (C) 2003 Carl Ritson
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

static int is_packlist_new(packlist_t *p) {
	int i;
	LOCK(global);
	for(i = 0; i < global->no_packlists; ++i) {
		if(global->packlists[i] == p) {
			UNLOCK(global);
			return 0;
		}
	}
	UNLOCK(global);
	return 1;
}

/** Pool Menu Commands **/

MENU_HELPER(cmd_packlists_helper) {
	int i,j;
	
	for(j = 0; args[1][j] != '\0'; ++j) {
		if(args[1][j] == '?')
			break;
	}
	
	LOCK(ac->user);
	if(ac->user->packlists != NULL 
			&& !(PRIV_ISSET(ac->user,PRIV_ALL_PACKLISTS)
				|| PRIV_ISSET(ac->user,PRIV_PACKLISTS))) {
		for(i = 0; i < ac->user->no_packlists; ++i) {
			LOCK(ac->user->packlists[i]);
			if(ac->user->packlists[i]->deleted == 0 &&
				(j == 0 ||
				 strncasecmp(ac->user->packlists[i]->name,args[1],j) == 0))
				admin_msg(ac,"%s",ac->user->packlists[i]->name);
			UNLOCK(ac->user->packlists[i]);
		}
		UNLOCK(ac->user);
	} else {
		UNLOCK(ac->user);
		LOCK(global);
		for(i = 0; i < global->no_packlists; ++i) {
			LOCK(global->packlists[i]);
			if(global->packlists[i]->deleted == 0 &&
				(j == 0 ||
				 strncasecmp(global->packlists[i]->name,args[1],j) == 0))
				admin_msg(ac,"%s",global->packlists[i]->name);
			UNLOCK(global->packlists[i]);
		}
		UNLOCK(global);
	}
}

MENU_CMD(cmd_packlist_delete) {
	packlist_t *p = (packlist_t *)ins->data;
	if(!is_packlist_new(p)) {
		packlist_del(p);
		LOCK(p);
		admin_msg(ac,"Packlist %s Deleted",p->name);
		UNLOCK(p);
	}
	return RET_UP;
}

MENU_CMD(cmd_packlist_rename) {
	char buffer[96];
	packlist_t *p = (packlist_t *)ins->data;
	int i;
	LOCK(global);
	for(i = 0; i < global->no_packlists; ++i) {
		if(global->packlists[i] != p) {
			LOCK(global->packlists[i]);
			if(global->packlists[i]->deleted == 0 &&
				strcasecmp(global->packlists[i]->name,args[1]) == 0) {
				UNLOCK(global->packlists[i]);
				UNLOCK(global);
				admin_msg(ac,"Rename Failed, Packlist %s Already Exists",args[1]);
				return 0;
			}
			UNLOCK(global->packlists[i]);
		}
	}
	LOCK(p);
	if(p->name != NULL)
		xfree(p->name);
	p->name = xstrdup(args[1]);
	UNLOCK(p);
	UNLOCK(global);
	if(ins->prompt != NULL && ins->free_prompt != 0)
		xfree(ins->prompt);
	snprintf(buffer,sizeof(buffer),"Packlist: %s",args[1]);
	ins->prompt = xstrdup(buffer);
	ins->free_prompt = 1;

	admin_output_prompt(ac);
	return 0;
}

MENU_CMD(cmd_packlist_save) {
	packlist_t *p = (packlist_t *)ins->data;
	int ret;
	
	if(is_packlist_new(p)) {
		LOCK(p);
		if(p->queue != NULL) {
			LOCK(p->queue);
			if(p->queue->deleted != 0) {
				UNLOCK(p->queue);
				p->queue = NULL;
			} else
				UNLOCK(p->queue);
		}
		if(p->queue != NULL) {
			UNLOCK(p);
			ret = packlist_add(p);
			if(ret == 0) {
				ins->free = NULL;
				LOCK(p);
				LOCK(p->queue);
				if(p->queue->deleted != 0) {
					UNLOCK(p->queue);
					UNLOCK(p);
					packlist_del(p);
					admin_msg(ac,"Queue was deleted as Packlist was saved, Packlist has now also been deleted.");
					return RET_UP;
				}
				UNLOCK(p->queue);
				UNLOCK(p);
				admin_msg(ac,"Successfully Saved.");
			} else
				admin_msg(ac,"Save Failed.");
		} else {
			UNLOCK(p);
			admin_msg(ac,"Unable to save no Queue set.");
		}
	} else
		admin_msg(ac,"Packlist already saved.");
	return 0;
}

MENU_CMD(cmd_packlist_settings) {
	packlist_t *p = (packlist_t *)ins->data;
	
	if(no_args == 1) {
		pqueue_t settings;
		pqueue_init(&settings,0);
		
		LOCK(p);
		if(p->queue != NULL) {
			LOCK(p->queue);
			pqueue_push_back(&settings,
				value_string("queue",p->queue->name));
			UNLOCK(p->queue);
		} else {
			pqueue_push_back(&settings,
				value_string("queue","not set"));
		}
		
		if(p->type == LIST_CHAT) {
			interface_t *intf = p->chat_interface;
			UNLOCK(p);
			
			if(intf != NULL) {
				LOCK(intf);
				pqueue_push_back(&settings,
					value_string("chat-interface",intf->name));
				UNLOCK(intf);
			} else {
				pqueue_push_back(&settings,
					value_string("chat-interface","not set"));
			}
		} else
			UNLOCK(p);
		
		packlist_read_settings(p,&settings);
		
		LOCK(p);
		output_settings(ac,"-- Packlist Settings for %s --",
				p->name,&settings);
		UNLOCK(p);
	} else if(strncasecmp(args[1],"q",1) == 0 && no_args >= 3) {
		if(strchr(args[2],'?') != NULL) {
			cmd_queues_helper(ins,ac,args,no_args);
		} else {
			queue_t *q = queue_find(args[2]);
			if(q != NULL) {
				LOCK(p);
				LOCK(q);
				if(q->deleted == 0)
					p->queue = q;
				UNLOCK(q);
				UNLOCK(p);
			} else
				admin_msg(ac,"Invalid queue %s.",args[2]);
		}
	} else if(strncasecmp(args[1],"ch",2) == 0 && no_args >= 3) {
		if(strcmp(args[2],"-") == 0) {
			LOCK(p);
			p->chat_interface = NULL;
			UNLOCK(p);
		} else {
			interface_t *i = interface_find(args[2]);
			if(i != NULL) {
				LOCK(i);
				if(i->deleted != 0 ||
					i->type != INTERFACE_CHAT) {
					UNLOCK(i);
					i = NULL;
				} else
					UNLOCK(i);
			}
			if(i != NULL) {
				LOCK(p);
				p->chat_interface = i;
				UNLOCK(p);
				LOCK(i);
				if(i->deleted != 0) {
					UNLOCK(i);
					LOCK(p);
					p->chat_interface = NULL;
					UNLOCK(p);
				} else
					UNLOCK(i);
			} else
				admin_msg(ac,"Invalid chat interface: %s.",
						args[2]);
		}
	} else if(no_args >= 3) {
		if(packlist_apply_setting(p,args[1],args[2]) == -1)
			admin_msg(ac,"Invalid setting: %s",args[1]);
	} else
		admin_msg(ac,"%s [<setting> <value>]",args[0]);
	return 0;
}

// declared in settings_menu.c
static int cmd_ignore_list(acontext_t *ac, 
		ignore_list_t *l, char **args, int no_args);
MENU_CMD(cmd_packlist_ignore_list) {
	packlist_t *p = (packlist_t *)ins->data;
	int ret;
	
	LOCK(p);
	ret = cmd_ignore_list(ac,p->ignore_list,args,no_args);
	UNLOCK(p);

	return ret;
}

MENU_CMD(cmd_packlist_taglines) {
	packlist_t *p = (packlist_t *)ins->data;
	int ret;
	LOCK(p);
	ret = cmd_strarr(ac,&p->taglines,&p->no_taglines,args,no_args);
	UNLOCK(p);
	return ret;
}

MENU_CMD(cmd_packlist_dirs) {
	packlist_t *p = (packlist_t *)ins->data;
	int ret;
	LOCK(p);
	ret = cmd_strarr(ac,&p->dirs,&p->no_dirs,args,no_args);
	UNLOCK(p);
	return ret;
}

MENU_CMD(cmd_packlist_filters) {
	packlist_t *p = (packlist_t *)ins->data;
	int ret;
	LOCK(p);
	ret = cmd_strarr(ac,&p->filters,&p->no_filters,args,no_args);
	UNLOCK(p);
	return ret;
}

MENU_CMD(cmd_packlist_access) {
	packlist_t *p = (packlist_t *)ins->data;
	char title[128];
	access_t *acc;
	
	LOCK(p);
	snprintf(title,sizeof(title),"Packlist %s",p->name);
	if(p->access == NULL && no_args > 1)
		p->access = alloc_access();
	acc = p->access;
	UNLOCK(p);

	return cmd_access_list(ac,acc,title,args,no_args);
}

MENU_CMD(cmd_packlist_info) {
	packlist_t *p = (packlist_t *)ins->data;
	if(no_args == 1) {
		char buffer[32];
		unsigned long long size;
		unsigned long gets;
		int i;
		
		LOCK(p);
		admin_msg(ac,"-- Packlist %s --",p->name);
		for(i = 0,size = 0,gets = 0; i < p->no_packs; ++i) {
			RLOCK(p->packs[i]);
			size += (unsigned long long) p->packs[i]->size;
			gets += p->packs[i]->no_gets;
			RUNLOCK(p->packs[i]);
		}
		admin_msg(ac,"Offering: %d Packs, %s",p->no_packs,
				format_bytes(buffer,sizeof(buffer),size,1));
		admin_msg(ac,"Total Packs Sent: %lu",gets);
		admin_msg(ac,"Last Scan: %s",
				p->last_scan == 0 ? "Unknown" :
				timestr(p->last_scan,buffer,sizeof(buffer)));
		if(p->nolist != 0 && p->nolist > xtime()) {
			if(p->nolist != TIME_MAX)
				admin_msg(ac,"No List Till: %s (Reason: %s)",
					timestr(p->nolist,buffer,
						sizeof(buffer)),
					p->nolist_reason != NULL ?
					 p->nolist_reason : "");
			else
				admin_msg(ac,"Permanent No List (Reason: %s)",
					p->nolist_reason != NULL ?
					 p->nolist_reason : "");
		}
		if(p->nosend != 0 && p->nosend > xtime()) {
			if(p->nosend != TIME_MAX)
				admin_msg(ac,"No Send Till: %s (Reason: %s)",
					timestr(p->nosend,buffer,
						sizeof(buffer)),
					p->nosend_reason != NULL ?
					 p->nosend_reason : "");
			else
				admin_msg(ac,"Permanent No Send (Reason: %s)",
					p->nosend_reason != NULL ?
					 p->nosend_reason : "");
		}
		UNLOCK(p);
	} else if(tolower(args[1][0]) == 'p') {
		pool_t *pool;
		LOCK(p);
		if(p->queue != NULL) {
			LOCK(p->queue);
			pool = p->queue->pool;
			UNLOCK(p->queue);
			UNLOCK(p);
			cmd_pool_show_info(ac,pool);
		} else {
			UNLOCK(p);
			admin_msg(ac,"No Queue Set");
		}
	} else if(tolower(args[1][0]) == 'q') {
		queue_t *q;
		LOCK(p);
		if(p->queue != NULL) {
			q = p->queue;
			UNLOCK(p);
			cmd_queue_show_info(ac,q);
		} else {
			UNLOCK(p);
			admin_msg(ac,"No Queue Set");
		}
	} else
		admin_msg(ac,"%s [<pool|queue>]",args[0]);
	return 0;
}

MENU_CMD(cmd_packlist_nolist) {
	packlist_t *p = (packlist_t *)ins->data;
	long sec = strtol(args[1],NULL,10);
	
	LOCK(p);
	
	if(sec == -1)
		p->nolist = TIME_MAX;
	else if(sec > 0)
		p->nolist = xtime() + sec;
	else
		p->nolist = 0;
	
	if(p->nolist_reason != NULL)
		xfree(p->nolist_reason);
	if(p->nolist != 0 && no_args > 2)
		p->nolist_reason = xstrdup(args[2]);
	else
		p->nolist_reason = NULL;
	
	UNLOCK(p);
	
	return 0;
}

MENU_CMD(cmd_packlist_nosend) {
	packlist_t *p = (packlist_t *)ins->data;
	long sec = strtol(args[1],NULL,10);
	
	LOCK(p);

	if(sec == -1)
		p->nosend = TIME_MAX;
	else if(sec > 0)
		p->nosend = xtime() + sec;
	else
		p->nosend = 0;
	
	if(p->nosend_reason != NULL)
		xfree(p->nosend_reason);
	if(p->nosend != 0 && no_args > 2)
		p->nosend_reason = xstrdup(args[2]);
	else
		p->nosend_reason = NULL;
				
	UNLOCK(p);
	
	return 0;
}

MENU_CMD(cmd_packlist_sq_add) {
	packlist_t *p = (packlist_t *)ins->data;
	char buffer[96];
	network_t *n;
	send_t *s;
	int i;
	
	if(no_args >= 5) {
		n = irc_network_find(args[2]);
		if(n != NULL) {
			LOCK(p);
			if(p->queue == NULL) {
				UNLOCK(p);
				admin_msg(ac,"Queue Not Set");
				return 0;
			}
			i = atoi(args[4]);
			if(i > 0 && i <= p->no_packs) {
				queue_t *queue	= p->queue;
				s		= alloc_send();
				s->network	= n;
				s->nick		= xstrdup(args[3]);
				s->pack		= pack_clone(p->packs[i-1]);
				s->queue	= p->queue;
				s->retries	= p->retries;
				if(tolower(args[0][0]) == 's') {
					UNLOCK(p);
					if(queue_enqueue(queue,s,SEND_FORCE,NULL) == -1) {
						admin_msg(ac,"Failed to add send");
						free_send(s);
					}
				} else { /* strcasecmp(args[0],"queues") == 0 */
					UNLOCK(p);
					if(queue_enqueue(queue,s,QUEUE_NO_ULIMIT|QUEUE_NO_CLIMIT,NULL) == -1) {
						admin_msg(ac,"Failed to add queue");
						free_send(s);
					}
				}
				snprintf(buffer,sizeof(buffer),"USERHOST %s",args[3]);
				irc_send_raw(n,buffer);
			} else {
				UNLOCK(p);
				admin_msg(ac,"Invalid Pack Number: %d",i);
			}
		} else
			admin_msg(ac,"Invalid Network: %s",args[2]);
	} else if(no_args == 3) {
		if(strchr(args[2],'?') != NULL)
			cmd_networks_helper(ins,ac,args,no_args);
		else
			admin_msg(ac,"%s add <network> <nick> <pack number>",
					args[0]);
	} else
		admin_msg(ac,"%s add <network> <nick> <pack number>",args[0]);
	
	return 0;
}

MENU_CMD(cmd_packlist_sq) {
	packlist_t *p = (packlist_t *)ins->data;
	unsigned long privs;
	queue_t *queue;
	pool_t *pool;
	int ret = RET_OK;

	LOCK(p);
	if(p->queue == NULL) {
		UNLOCK(p);
		admin_msg(ac,"Queue Not Set");
		return 0;
	}
	queue = p->queue;
	LOCK(p->queue);
	pool = p->queue->pool;
	UNLOCK(p->queue);
	UNLOCK(p);

	LOCK(ac->user);
	privs = ac->user->uprivs;
	UNLOCK(ac->user);
	
	if(no_args >= 2) {
		if(tolower(args[1][0]) == 'd' || 
			tolower(args[1][0]) == 'c') {
			if(PRIV_U_ISSET(privs,PRIV_SEND_DEL)) {
				if(tolower(args[0][0]) == 's')
					ret = cmd_pool_sends_p(pool,ac,args,no_args);
				else /* strcasecmp(args[0],"queues") == 0 */
					ret = cmd_queue_queues_q(queue,ac,args,no_args);
			} else
				admin_msg(ac,"Insufficient Privileges");
		} else if(tolower(args[1][0]) == 'a' || 
				tolower(args[1][0]) == 'o') {
			if(PRIV_U_ISSET(privs,PRIV_SEND_ADD))
				ret = CHAIN_CMD(cmd_packlist_sq_add);
			else
				admin_msg(ac,"Insufficient Privileges");
		} else if(PRIV_U_ISSET(privs,PRIV_SEND_MOD)) {
			if(tolower(args[0][0]) == 's')
				ret = cmd_pool_sends_p(pool,ac,args,no_args);
			else /* strcasecmp(args[0],"queues") == 0 */
				ret = cmd_queue_queues_q(queue,ac,args,no_args);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else {
		if(tolower(args[0][0]) == 's')
			ret = cmd_pool_sends_p(pool,ac,args,no_args);
		else /* strcasecmp(args[0],"queues") == 0 */
			ret = cmd_queue_queues_q(queue,ac,args,no_args);
	}
	
	return ret;
}

MENU_CMD(cmd_packlist_packs) {
	packlist_t *p = (packlist_t *)ins->data;
	unsigned long privs;
	colourtype_t colour;
	
	LOCK(ac->user);
	privs = ac->user->uprivs;
	UNLOCK(ac->user);
	
	colour = ac->colour;

	if(no_args == 1) {
		packlist_xdl_body(p,ac->msgqueue,colour,"","");
	} else if(tolower(args[1][0]) == 'a') {
		if(PRIV_U_ISSET(privs,PRIV_PACK_ADD)) {
			if(no_args >= 3) {
				char pathbuf[512],fn[512];
				char **dirs;
				struct stat sdata;
				int i,ret = -1,no_dirs;
				
				clean_ds_path(args[2],fn,sizeof(fn));
				
				LOCK(p);
				dirs = strarr_dup(p->dirs,p->no_dirs,&no_dirs);
				UNLOCK(p);
				
				for(i = 0; i < no_dirs && ret != 0; ++i) {
					snprintf(pathbuf,sizeof(pathbuf),
							"%s/%s",dirs[i],fn);
					ret = stat(pathbuf,&sdata);
					if(ret == 0 && S_ISREG(sdata.st_mode))
						ret = pack_add_file(p,pathbuf,
							NULL,&sdata,NULL);
					else
						ret = -1;
				}
				if(dirs != NULL)
					strarr_free(&dirs,&no_dirs);
				if(ret == 0) {
					if(no_args >= 4) {
						LOCK(p);
						for(i = (p->no_packs-1),ret = 0; i >= 0 && !ret; --i) {
							WLOCK(p->packs[i]);
							if(strcmp(p->packs[i]->file,pathbuf) == 0) {
								if(p->packs[i]->label != NULL)
									xfree(p->packs[i]->label);
								p->packs[i]->label = xstrdup(args[3]);
								ret = 1;
							}
							WUNLOCK(p->packs[i]);
						}
						UNLOCK(p);
					}
					admin_msg(ac,"Pack Added");
				} else
					admin_msg(ac,"Error Occured Adding: \"%s\"",fn);
			} else
				admin_msg(ac,"%s add <file> [<label>]",args[0]);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(tolower(args[1][0]) == 'd') {
		if(PRIV_U_ISSET(privs,PRIV_PACK_DEL)) {
			if(no_args >= 3) {
				int i = atoi(args[2]);
				LOCK(p);
				if(i > 0 && i <= p->no_packs) {
					pack_t *pack = p->packs[i-1];
					UNLOCK(p);
					if(pack_del(p,pack) == 0)
						admin_msg(ac,"Pack Deleted");
					else
						admin_msg(ac,"Unknown Error while deleting pack.");
				} else {
					UNLOCK(p);
					admin_msg(ac,"Invalid Pack Number: %d",i);
				}
			} else
				admin_msg(ac,"%s del <pack number>",args[0]);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(tolower(args[1][0]) == 'l') {
		if(PRIV_U_ISSET(privs,PRIV_PACK_MOD)) {
			if(no_args >= 4) {
				int i = atoi(args[2]);
				LOCK(p);
				if(i > 0 && i <= p->no_packs) {
					WLOCK(p->packs[i-1]);
					if(p->packs[i-1]->label != NULL)
						xfree(p->packs[i-1]->label);
					p->packs[i-1]->label = xstrdup(args[3]);
					WUNLOCK(p->packs[i-1]);
				} else
					admin_msg(ac,"Invalid Pack Number: %d",i);
				UNLOCK(p);
			} else
				admin_msg(ac,"%s label <pack number> <label>",args[0]);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(tolower(args[1][0]) == 'm' || 
			strncasecmp(args[1],"sw",2) == 0) {
		if(PRIV_U_ISSET(privs,PRIV_PACK_MOD)) {
			if(no_args >= 4) {
				int i = atoi(args[2])-1, j = atoi(args[3])-1;
				LOCK(p);
				if(tolower(args[1][0]) == 'm') {
					if(PTRARR_MOVE(&(p->packs),&(p->no_packs),i,j) != 0)
						admin_msg(ac,"Invalid Move: %d to %d",i,j);
				} else {
					if(PTRARR_SWAP(&(p->packs),&(p->no_packs),i,j) != 0)
						admin_msg(ac,"Invalid Swap: %d with %d",i,j);
				}
				UNLOCK(p);
			} else
				admin_msg(ac,"%s %s <src no> <dst no>",
						args[0],args[1]);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(tolower(args[1][0]) == 'r') {
		if(PRIV_U_ISSET(privs,PRIV_PACK_MOD)) {
			if(no_args >= 3) {
				int i = atoi(args[2]);
				LOCK(p);
				if(i > 0 && i <= p->no_packs) {
					WLOCK(p->packs[i-1]);
					p->packs[i-1]->hashed = 0;
					if(p->packs[i-1]->crc != NULL)
						xfree(p->packs[i-1]->crc);
					p->packs[i-1]->crc = NULL;
					if(p->packs[i-1]->md5 != NULL)
						xfree(p->packs[i-1]->md5);
					p->packs[i-1]->md5 = NULL;
					WUNLOCK(p->packs[i-1]);
					admin_msg(ac,"Pack %d Marked for Rehash",i);
				} else
					admin_msg(ac,"Invalid Pack Number: %d",i);
				UNLOCK(p);
			} else
				admin_msg(ac,"%s rehash <pack number>",args[0]);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(strncasecmp(args[1],"sc",2) == 0) {
		if(PRIV_U_ISSET(privs,PRIV_PACKLIST_SCAN)) {
			int depth = -1;
			if(no_args > 2)
				depth = atoi(args[2]);
			packlist_scan(p,depth);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(tolower(args[1][0]) == 'c') {
		if(PRIV_U_ISSET(privs,PRIV_PACKLIST_SCAN))
			packlist_check(p);
		else
			admin_msg(ac,"Insufficient Privileges");
	} else if(strncasecmp(args[1],"so",2) == 0) {
		if(PRIV_U_ISSET(privs,PRIV_PACK_MOD)) {
			int type = SORT_AZ;
			if(no_args > 2)
				type = str_to_sorttype(args[2]);
			packlist_sort(p,type);
		} else
			admin_msg(ac,"Insufficient Privileges");
	} else if(tolower(args[1][0]) == 'v') {
		if(no_args >= 3) {
			int i = atoi(args[2]);
			LOCK(p);
			if(i > 0 && i <= p->no_packs) {
				pack_t *pa = p->packs[i-1];
				RLOCK(pa);
				admin_msg(ac,"-- %s:%d --",p->name,i);
				admin_msg(ac,"File: %s (%lld bytes)",
						pa->file,(long long)pa->size);
				admin_msg(ac,"Label: %s",pa->label);
				admin_msg(ac,"Sent: %d (%lld bytes)",
					pa->no_gets,
					(long long)pa->size * pa->no_gets);
				if(pa->hashed == 0) {
					admin_msg(ac,"Not Hashed");
				} else {
					char tbuf[32];
					admin_msg(ac,"Hashed: %s",timestr(pa->hashed,tbuf,sizeof(tbuf)));
					admin_msg(ac,"CRC: %s",pa->crc);
					admin_msg(ac,"MD5: %s",pa->md5);
				}
				RUNLOCK(pa);
			}
			UNLOCK(p);
		}
	} else
		admin_msg(ac,"%s [<add|check|del|label|move|rehash|scan|sort|swap|view> [...]]",args[0]);
	return 0;
}

MENU_CMD(cmd_packlist_plist) {
	packlist_t *p = (packlist_t *)ins->data;
	network_t *n;
	channel_t *c;
	pformat_t pformat = PFORMAT_FULL;
	pqueue_t pq;
	char *ptr;
	
	if(no_args < 3) {
		admin_msg(ac,"%s <network> <channel|nick> [<pformat>]",args[0]);
		return 0;
	} else if(is_packlist_new(p)) {
		admin_msg(ac,"You must save the packlist first");
		return 0;
	}

	n = irc_network_find(args[1]);
	if(n != NULL) {
		c = irc_channel_find(n,args[2]);
		if(no_args >= 4) {
			if((pformat = str_to_pformat(args[3])) == -1) {
				admin_msg(ac,"Invalid Pformat: %s",args[3]);
				return 0;
			}
		} else if(c != NULL) {
			LOCK(c);
			pformat = c->pformat;
			UNLOCK(c);
		}
		
		pqueue_init(&pq,0);
		packlist_plist_generate(p,&pq,pformat,COLOUR_IRC,n,c,"","");

		while((ptr = pqueue_pop_front(&pq)) != NULL) {
			irc_send_msg(n,args[2],ptr);
			xfree(ptr);
		}
	} else
		admin_msg(ac,"Invalid Network %s",args[1]);
	return 0;
}
				

/** Pool Menu Structure **/

static const cmd_t packlist_menu[] = {
	{"access",	cmd_packlist_access,	0,-1,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"delete",	cmd_packlist_delete,	0,0,1,0,
			PRIV_TO_MASK(PRIV_PACKLISTS),		NULL},
	{"dirs",	cmd_packlist_dirs,	0,3,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"filters",	cmd_packlist_filters,	0,3,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"ignore-list",	cmd_packlist_ignore_list,0,3,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"info",	cmd_packlist_info,	0,1,0,0,
			PRIVS_EMPTY,				NULL},
	{"nolist",	cmd_packlist_nolist,	1,2,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"nosend",	cmd_packlist_nosend,	1,2,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"packs",	cmd_packlist_packs,	0,3,1,0,
			PRIVS_EMPTY,				NULL},
	{"plist",	cmd_packlist_plist,	0,3,1,0,
		PRIV_TO_MASK(PRIV_PACKLIST_SEND),cmd_net_channels_helper},
	{"queues",	cmd_packlist_sq,	0,4,1,0,
			PRIVS_EMPTY,				NULL},
	{"rename",	cmd_packlist_rename,	1,1,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"save",	cmd_packlist_save,	0,0,1,0,
			PRIV_TO_MASK(PRIV_PACKLISTS),		NULL},
	{"sends",	cmd_packlist_sq,	0,4,1,0,
			PRIVS_EMPTY,				NULL},
	{"settings",	cmd_packlist_settings,	0,2,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{"taglines",	cmd_packlist_taglines,	0,3,1,0,
			PRIV_TO_MASK(PRIV_PACKLIST_MOD),	NULL},
	{NULL}
};

/** Pool Menu Entry **/
MENU_CHECK(cmd_packlist_check) {
	packlist_t *p = (packlist_t *)ins->data;
	int ret = 0;
	LOCK(p);
	if(p->deleted != 0) {
		admin_msg(ac,"Packlist %s, Deleted.",p->name);
		ret = -1;
	} else if(p->queue != NULL) {
		float f;
		int i;
		
		LOCK(p->queue);
		LOCK(p->queue->pool);
		for(i = 0, f = 0.0; i < AVGSPEED; ++i)
			f += (float)p->queue->pool->avgspeed[i];
		f /= ((float)AVGSPEED) * 1024.0;
		
		admin_msg(ac,"Stat: %d/%d Sls, %d/%d Q, %.1fKiB/s Rcd (Bdw: %.0f%%, %.1fKiB/s, %.1fKiB/s)",
				p->queue->pool->no_csends,p->queue->pool->sends,
				p->queue->no_qsends,p->queue->length,
				((float)p->queue->pool->record_send)/1024.0,
				p->queue->pool->bandwidth != 0 ?
				 (f / (float)p->queue->pool->bandwidth)*100 :
				 0.0,
				f,((float)p->queue->pool->record_rate)/1024.0);
		UNLOCK(p->queue->pool);
		UNLOCK(p->queue);
	}	
	UNLOCK(p);
	return ret;
}

MENU_CMD(cmd_packlist_entry) {
	char buffer[96];
	unsigned long privs;
	packlist_t **access;
	int no_access;
	packlist_t *p;
	int j,permit = 0;

	LOCK(ac->user);
	privs = ac->user->uprivs;
	if(ac->user->packlists != NULL) {
		no_access = ac->user->no_packlists;
		access = xalloc(sizeof(packlist_t *)*no_access);
		memcpy(access,ac->user->packlists,
				sizeof(packlist_t *)*no_access);
	} else {
		access = NULL;
		no_access = 0;
	}
	UNLOCK(ac->user);
	
	snprintf(buffer,sizeof(buffer),"Packlist: %s",args[1]);
	if(ins->prompt != NULL && ins->free_prompt)
		xfree(ins->prompt);
	ins->prompt	= xstrdup(buffer);
	ins->free_prompt= 1;
	ins->check 	= cmd_packlist_check;
	
	p = packlist_find(args[1]);
	if(p == NULL) {
		if(PRIV_U_ISSET(privs,PRIV_PACKLISTS)) {
			p = alloc_packlist();
			p->name = xstrdup(args[1]);
			ins->free = (void (*)(void *)) free_packlist;
			admin_msg(ac,"New Packlist: %s",args[1]);
			permit = 1;
		}
	} else if(access != NULL && !(PRIV_U_ISSET(privs,PRIV_ALL_PACKLISTS)
				|| PRIV_U_ISSET(privs,PRIV_PACKLISTS))) {
		for(j = 0; j < no_access && permit == 0; ++j) {
			if(access[j] == p)
				permit = 1;
		}
	} else if(PRIV_U_ISSET(privs,PRIV_ALL_PACKLISTS) ||
			PRIV_U_ISSET(privs,PRIV_PACKLISTS)) {
		permit = 1;
	}
	
	if(access != NULL)
		xfree(access);
	
	if(permit == 0) {
		admin_msg(ac,"You do not have sufficient access privileges.");
		return -1;
	} else {
		ins->data = p;
		return 0;
	}
}

static void register_packlist_menu(void) {
	menu_t *p_menu = menu_from_cmds(
			"packlist",			// name
			1,				// min_args
			1,				// max_args
			1,				// log
			0,				// hide
			PRIVS_EMPTY,			// privs
			cmd_packlist_entry,		// func
			cmd_packlists_helper,		// helper
			(cmd_t *)packlist_menu);

	menu_add(main_menu_root,p_menu);
	menu_deref(p_menu);
}
