/* ARISA - Admin Interface - User(s) 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_user_new(user_t *u) {
	int i;
	LOCK(global);
	for(i = 0; i < global->no_users; ++i) {
		if(global->users[i] == u) {
			UNLOCK(global);
			return 0;
		}
	}
	UNLOCK(global);
	return 1;
}

/** User Menu Commands **/

MENU_HELPER(cmd_users_helper) {
	int i,j;
	LOCK(global);
	for(j = 0; args[1][j] != '\0'; ++j) {
		if(args[1][j] == '?')
			break;
	}
	for(i = 0; i < global->no_users; ++i) {
		LOCK(global->users[i]);
		if(global->users[i]->deleted == 0 &&
			(j == 0 ||
			 strncmp(global->users[i]->username,args[1],j) == 0))
			admin_msg(ac,"%s",global->users[i]->username);
		UNLOCK(global->users[i]);
	}
	UNLOCK(global);
}

MENU_CMD(cmd_user_delete) {
	user_t *u = (user_t *)ins->data;
	LOCK(u);
	if(no_args == 1) {
		admin_cmsg(ac,"Are you %%9really%%n sure you want to delete the user account \"%s\"?",u->username);
		admin_msg(ac,"If so run '%s %s' instead.",args[0],u->username);
		UNLOCK(u);
	} else if(strcasecmp(args[1],u->username) == 0) {
		admin_msg(ac,"User %s Deleted",u->username);
		UNLOCK(u);
		user_del(u);
		return CHAIN_CMD(cmd_exit);
	}
	return 0;
}

MENU_CMD(cmd_user_save) {
	user_t *u = (user_t *)ins->data;
	int ret;
	if(is_user_new(u)) {
		ret = user_add(u);
		if(ret == 0) {
			ins->free = NULL;
			admin_msg(ac,"Successfully Saved.");
		} else
			admin_msg(ac,"Save Failed.");
	} else
		admin_msg(ac,"User is already saved.");
	return 0;
}

MENU_CMD(cmd_user_info) {
	user_t *u = (user_t *)ins->data;
	char buffer[128];
	int i,j;
	LOCK(u);
	admin_msg(ac,"-- User %s --",u->username);
	admin_msg(ac,"Last Logon: %s",
			u->last_logon == 0 ? "Unknown" :
			timestr(u->last_logon,buffer,sizeof(buffer)));
	admin_msg(ac,"Has Privileges:");
	for(i = 0,j = 0; priv_text[i].text != NULL; ++i) {
		if(PRIV_ISSET(u,priv_text[i].priv)) {
			if((j + strlen(priv_text[i].text)) >= 80) {
				admin_msg(ac,"%s",buffer);
				j = 0;
			}
			j += IMIN(
				snprintf(buffer + j,sizeof(buffer)-j,
					"  %s",priv_text[i].text),
				sizeof(buffer)-j-1);
		}
	}
	if(j != 0)
		admin_msg(ac,"%s",buffer);
	UNLOCK(u);
	return 0;
}

MENU_CMD(cmd_user_hosts) {
	user_t *u = (user_t *)ins->data;
	int ret;
	LOCK(u);
	ret = cmd_strarr(ac,&u->hosts,&u->no_hosts,args,no_args);
	UNLOCK(u);
	return ret;
}

MENU_CMD(cmd_user_dirs) {
	user_t *u = (user_t *)ins->data;
	struct stat sdata;
	int i,ret,sret;
	LOCK(u);
	ret = cmd_strarr(ac,&u->dirs,&u->no_dirs,args,no_args);
	for(i = 0; i < u->no_dirs; ++i) {
		sret = stat(u->dirs[i],&sdata); // hmm, this might block
		if(sret != 0 || !S_ISDIR(sdata.st_mode))
			admin_msg(ac,"Warning Directory %d (\"%s\") is not valid",
					i+1,u->dirs[i]);
	}
	UNLOCK(u);
	return ret;
}

MENU_CMD(cmd_user_settings) {
	user_t *u = (user_t *)ins->data;

	if(no_args == 1) {
		pqueue_t settings;
		pqueue_init(&settings,0);
		
		user_read_settings(u,&settings);
		output_settings(ac,"-- User Settings for %s --",
				u->username,&settings);
	} else if(no_args >= 3) {
		if(user_apply_setting(u,args[1],args[2]) == -1)
			admin_msg(ac,"Invalid setting: %s",args[1]);
	} else
		admin_msg(ac,"%s [<settings> <value>]",args[0]);
	
	return 0;
}

MENU_CMD(cmd_user_grant_revoke) {
	user_t *u = (user_t *)ins->data;
	int i,j;
	if(no_args == 1)
		admin_msg(ac,"%s <privilege name>",args[0]);
	else {
		if(strchr(args[1],'?') != NULL) {
			for(j = 0; args[1][j] != '\0'; ++j) {
				if(args[1][j] == '?')
					break;
			}
			for(i = 0; priv_text[i].text != NULL; ++i) {
				if(strncasecmp(priv_text[i].text,args[1],j) == 0)
					admin_msg(ac,"%s",priv_text[i].text);
			}
		} else {
			for(j = 1; j < no_args; ++j) {
				for(i = 0; priv_text[i].text != NULL; ++i) {
					if(strcasecmp(priv_text[i].text,args[j]) == 0)
						break;
				}
				if(priv_text[i].text != NULL) {
					LOCK(u);
					if(tolower(args[0][0]) == 'g')
						PRIV_SET(u,priv_text[i].priv);
					else /* tolower(args[0][0]) == 'r' */
						PRIV_UNSET(u,priv_text[i].priv);
					UNLOCK(u);
				} else
					admin_msg(ac,"Invalid Privilege Name: %s",args[j]);
			}
		}
	}
	return 0;
}

MENU_CMD(cmd_user_password) {
	user_t *u = (user_t *)ins->data;
	if(no_args >= 3) {
		if(strcmp(args[1],args[2]) == 0)
			user_set_password(u,args[1]);
		else
			admin_msg(ac,"Passwords do not match");
	} else
		admin_msg(ac,"%s <password> <password>",args[0]);
	return 0;
}

#if 0
MENU_CMD(cmd_user_uploads) {
	interface_t *intf;
	user_t *u = (user_t *)ins->data;
	pqueue_t il;
	int i,j;
	
	if(no_args == 1) {
		time_t now = xtime();
		pqueue_init(&il,0);
		
		LOCK(u);
		admin_msg(ac,"-- Uploads for %s --",u->username);
		UNLOCK(u);
		
		LOCK(global);
		for(i = 0; i < global->no_interfaces; ++i) {
			LOCK(global->interfaces[i]);
			if(global->interfaces[i]->deleted == 0 &&
				global->interfaces[i]->type == INTERFACE_RECV)
				pqueue_push_back(&il,global->interfaces[i]);
			UNLOCK(global->interfaces[i]);
		}
		UNLOCK(global);
		if(pqueue_length(&il) == 0)
			admin_msg(ac,"None");
		while((intf = pqueue_pop_front(&il)) != NULL) {
			LOCK(intf);
			for(i = 0; i < intf->recv.no_uploads; ++i) {
				LOCK(intf->recv.uploads[i]);
				if(intf->recv.uploads[i]->user == u) {
					UNLOCK(intf->recv.uploads[i]);
					cmd_interface_display_upload(ac,
							intf->recv.uploads[i],
							now);
				} else
					UNLOCK(intf->recv.uploads[i]);
			}
			UNLOCK(intf);
		}
	} else if(tolower(args[1][0]) == 'c' && no_args == 3) { // close
		pqueue_init(&il,0);
		LOCK(global);
		for(i = 0; i < global->no_interfaces; ++i) {
			LOCK(global->interfaces[i]);
			if(global->interfaces[i]->deleted == 0 &&
				global->interfaces[i]->type == INTERFACE_RECV)
				pqueue_push_back(&il,global->interfaces[i]);
			UNLOCK(global->interfaces[i]);
		}
		UNLOCK(global);
		
		j = 0;
		while((intf = pqueue_pop_front(&il)) != NULL) {
			LOCK(intf);
			for(i = 0; i < intf->recv.no_uploads; ++i) {
				LOCK(intf->recv.uploads[i]);
				if(intf->recv.uploads[i]->user == u &&
					(send_matchs(intf->recv.uploads[i],
						args[2],NULL,
						no_args >= 4 ? args[3] : NULL,
						NULL,NULL) ||
					 send_matchs(intf->recv.uploads[i],
						args[2],NULL,NULL,
						no_args >= 4 ? args[3] : NULL,
						NULL))) {
					intf->recv.uploads[i]->state = STATE_DELETED;
				}
				UNLOCK(intf->recv.uploads[i]);
			}
			UNLOCK(intf);
		}
		admin_msg(ac,"Closed %d Matching Uploads",j);
	} else
		admin_msg(ac,"%s [close <hostmask> [<nick|file>]]",args[0]);
	return 0;
}
#endif

#if 0
MENU_CMD(cmd_user_upload_dir) {
	user_t *u = (user_t *)ins->data;
	
	if(no_args == 1) {
		LOCK(u);
		admin_msg(ac,"upload-dir: %s", u->upload_dir != NULL ? 
				 u->upload_dir : "Not Set");
		UNLOCK(u);
	} else {
		struct stat sbuf;
		int ret = stat(args[1],&sbuf);
		if(strcmp(args[1],"-") == 0) {
			LOCK(u);
			if(u->upload_dir != NULL)
				xfree(u->upload_dir);
			u->upload_dir = NULL;
		} else if(ret == 0 && S_ISDIR(sbuf.st_mode)) {
			LOCK(u);
			if(u->upload_dir != NULL)
				xfree(u->upload_dir);
			u->upload_dir = xstrdup(args[1]);
			UNLOCK(u);
		} else
			admin_msg(ac,"Invalid Directory: %s",args[1]);
	}
	
	return 0;
}
#endif

MENU_CMD(cmd_user_packlists) {
	user_t *u = (user_t *)ins->data;
	packlist_t *p,**access = NULL;
	int i,no_access = 0;
	
	if(no_args == 1) {
		LOCK(u);
		admin_msg(ac,"-- Packlist Access for %s --",u->username);
		if(u->packlists != NULL) {
			no_access = u->no_packlists;
			access = xalloc(sizeof(packlist_t *)*no_access);
			memcpy(access,u->packlists,sizeof(packlist_t *)*no_access);
		}
		UNLOCK(u);
		if(access != NULL) {
			for(i = 0; i < no_access; ++i) {
				LOCK(access[i]);
				admin_msg(ac,"% 2d: %s",i+1,access[i]->name);
				UNLOCK(access[i]);
			}
			xfree(access);
		} else
			admin_msg(ac,"Empty");
	} else if(tolower(args[1][0]) == 'a') {
		if(no_args >= 3) {
			p = packlist_find(args[2]);
			if(p != NULL) {
				LOCK(u);
				PTRARR_ADD(&u->packlists,&u->no_packlists,p);
				UNLOCK(u);
				LOCK(p);
				if(p->deleted != 0) {
					UNLOCK(p);
					LOCK(u);
					PTRARR_DEL(&u->packlists,
							&u->no_packlists,p);
					UNLOCK(u);
				} else
					UNLOCK(p);
			} else
				admin_msg(ac,"Invalid Packlist: %s",args[2]);
		} else 
			admin_msg(ac,"%s add <packlist>",args[0]);
	} else if(tolower(args[1][0]) == 'd') {
		if(no_args >= 3) {
			i = atoi(args[2]);
			LOCK(u);
			if(i > 0 && i <= u->no_packlists)
				PTRARR_DEL(&u->packlists,&u->no_packlists,
						u->packlists[i-1]);
			else
				admin_msg(ac,"Invalid Access Entry: %d",i);
			UNLOCK(u);
		} else
			admin_msg(ac,"%s del <access entry number>",args[0]);
	} else if(tolower(args[1][0]) == 'w') {
		LOCK(u);
		if(u->packlists != NULL)
			xfree(u->packlists);
		u->packlists = NULL;
		u->no_packlists = 0;
		UNLOCK(u);
	} else
		admin_msg(ac,"%s [<add|del|wipe> [...]]",args[0]);
	return 0;
}	

/** User Menu Structure **/

static const cmd_t user_menu[] = {
	{"delete",		cmd_user_delete,		0,1,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{"dirs",		cmd_user_dirs,			0,3,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{"grant",		cmd_user_grant_revoke,		0,-1,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{"hosts",		cmd_user_hosts,			0,3,1,0,
				PRIVS_EMPTY,			NULL},
	{"info",		cmd_user_info,			0,0,0,0,
				PRIVS_EMPTY,			NULL},
	{"packlists",		cmd_user_packlists,		0,2,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{"password",		cmd_user_password,		0,2,0,0,
				PRIVS_EMPTY,			NULL},
	{"revoke",		cmd_user_grant_revoke,		0,-1,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{"save",		cmd_user_save,			0,0,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{"settings",		cmd_user_settings,		0,3,1,0,
				PRIV_TO_MASK(PRIV_USERS),	NULL},
//	{"uploads",		cmd_user_uploads,		0,3,0,0,
//				PRIV_TO_MASK(PRIV_UPLOAD),	NULL},
//	{"upload-dir",		cmd_user_upload_dir,		0,1,1,0,
//				PRIV_TO_MASK(PRIV_USERS),	NULL},
	{NULL}
};

/** User Menu Entry **/
MENU_CHECK(cmd_user_check) {
	user_t *u = (user_t *)ins->data;
	int ret = 0;
	LOCK(u);
	if(u->deleted != 0) {
		admin_msg(ac,"User %s, Deleted.",u->username);
		ret = -1;
	}
	UNLOCK(u);
	return ret;
}

MENU_CMD(cmd_user_entry) {
	user_t *u;
	
	if(ins->prompt != NULL && ins->free_prompt)
		xfree(ins->prompt);
	ins->prompt	= xstrdup(args[1]);
	ins->free_prompt= 1;
	ins->check 	= cmd_user_check;
	
	u = user_find(args[1]);
	if(u == NULL) {
		LOCK(ac->user);
		if(PRIV_ISSET(ac->user,PRIV_USERS)) {
			u = alloc_user(args[1]);
			ins->free = (void (*)(void *)) free_user;
			admin_msg(ac,"New User: %s",args[1]);
		} else
			admin_msg(ac,"Insufficient Privileges");
		UNLOCK(ac->user);
	}
	if(u != NULL) {
		ins->data = u;
		return 0;
	} else
		return -1;
}

/** Users Menu Commands **/

MENU_CMD(cmd_users_logons) {
	if(no_args == 1)
		return CHAIN_CMD(cmd_whoison);
	else if(tolower(args[1][0]) == 'c' && no_args >= 3) { // close
		user_t *u = NULL;
		int i,j;
		if(no_args >= 4) {
			u = user_find(args[3]);
			if(u == NULL) {
				admin_msg(ac,"Invalid User: %s",args[3]);
				return 0;
			}
		}
		LOCK(global);
		for(i = 0, j = 0; i < global->no_admins; ++i) {
			LOCK(global->admins[i]);
			if(irc_hostmask_match(global->admins[i]->host,args[2]) &&
				(u == NULL || global->admins[i]->admin.user == u)) {
				global->admins[i]->state = STATE_DELETED;
				j++;
			}
			UNLOCK(global->admins[i]);
		}
		UNLOCK(global);
		admin_msg(ac,"Closed %d Matching Admin Sessions",j);
	} else
		admin_msg(ac,"%s [close <hostmask> [<user>]]",args[0]);
	return 0;
}

/** Users Menu Structure **/

static const cmd_t users_menu[] = {
	{"logons",	cmd_users_logons,	0,3,1,0,
			PRIVS_EMPTY,		NULL},
	{NULL}
};

MENU_CMD(cmd_self_entry) {
	char buffer[64];
	char *nargs[2];
	LOCK(ac->user);
	xstrncpy(buffer,ac->user->username,sizeof(buffer));
	UNLOCK(ac->user);
	nargs[0] = "self";
	nargs[1] = buffer;
	return CHAIN_CMD_ARGS(cmd_user_entry,nargs,2);
}

static void register_users_menu(void) {
	menu_t *us_menu, *u_menu, *s_menu;
	
	us_menu = menu_from_cmds(
			"users",
			0,
			0,
			0,
			0,
			PRIV_TO_MASK(PRIV_USERS),
			NULL,
			NULL,
			(cmd_t *)users_menu);
	u_menu = menu_from_cmds(
			"user",
			1,
			1,
			1,
			0,
			PRIVS_EMPTY,
			cmd_user_entry,
			cmd_users_helper,
			(cmd_t *)user_menu);
	s_menu = menu_from_cmds(
			"self",
			0,
			0,
			0,
			0,
			PRIV_TO_MASK(PRIV_SELF_MOD),
			cmd_self_entry,
			NULL,
			(cmd_t *)user_menu);
	
	menu_add(us_menu,u_menu);
	menu_add(main_menu_root,us_menu);
	menu_add(main_menu_root,s_menu);

	menu_deref(u_menu);
	menu_deref(us_menu);
	menu_deref(s_menu);
}
