#include "ftptool.h"

#ifdef USE_PROTOTYPES
void host_list_clean_proc(Panel_item item, Event *event)
#else
void host_list_clean_proc(item, event)
Panel_item	item;
Event	*event;
#endif
{
	xv_set(host_window.advanced.alias, 
		PANEL_VALUE, "", 
		NULL);
	xv_set(host_window.advanced.last_visited, 
		PANEL_LABEL_STRING, "Never", 
		NULL);
	xv_set(host_window.basic.host, 
		PANEL_VALUE, "", 
		NULL);
	if (!strcmp((char *)xv_get(item, PANEL_LABEL_STRING), "New")) {
		xv_set(host_window.basic.login, 
			PANEL_VALUE, "", 
			NULL);
		xv_set(host_window.basic.password, 
			PANEL_VALUE, "", 
			NULL);
	} else {
		xv_set(host_window.basic.login, 
			PANEL_VALUE, "anonymous", 
			NULL);
		xv_set(host_window.basic.password, 
			PANEL_VALUE, anonftp_password, 
			NULL);
		xv_set(host_window.basic.panel,
			PANEL_CARET_ITEM, host_window.basic.host,
			NULL);
	}
	xv_set(host_window.advanced.proxy, 
		PANEL_VALUE, DEFAULT_PROXY, 
		NULL);
	xv_set(host_window.advanced.transfer_mode, 
		PANEL_VALUE, BINARY, 
		NULL);
	xv_set(host_window.advanced.remote_auto_cd, 
		PANEL_VALUE, ".", 
		NULL);
	xv_set(host_window.advanced.local_auto_cd, 
		PANEL_VALUE, ".", 
		NULL);
	xv_set(host_window.advanced.os_type,
		PANEL_VALUE, REMOTE_OS_UNIX,
		NULL);
	xv_set(host_window.advanced.dir_parse, 
		PANEL_VALUE, UNIX_DIR_PATTERN, 
		XV_SHOW, FALSE,
		NULL);
	xv_set(host_window.advanced.comment, 
		PANEL_VALUE, "", 
		NULL);

	if (try_proxy) {
		xv_set(host_window.advanced.proxy, 
			XV_SHOW, TRUE, 
			NULL);
	} else {
		xv_set(host_window.advanced.proxy, 
			XV_SHOW, FALSE, 
			NULL);
	}
	xv_set(item, 
		PANEL_NOTIFY_STATUS, XV_ERROR, 
		NULL);
}

#ifdef USE_PROTOTYPES
void host_list_item_proc(Menu menu, Menu_item menu_item)
#else
void host_list_item_proc(menu, menu_item)
Menu	menu;
Menu_item menu_item;
#endif
{
	char	*alias = (char *)xv_get(menu_item, MENU_STRING);
	struct	hostlist *tmp;

	tmp = gethostlist(hostlist_head, alias);
	if (tmp == NULL) {
		fprintf(stderr, "Entry %s in menu not found in list.\n", alias);
		exit(1);
	}
	xv_set(host_window.advanced.alias, 
		PANEL_VALUE, tmp->aliasname, 
		NULL);
	xv_set(host_window.advanced.last_visited, 
		PANEL_LABEL_STRING, tmp->last_visited, 
		NULL);
	xv_set(host_window.basic.host, 
		PANEL_VALUE, tmp->host, 
		NULL);
	xv_set(host_window.basic.login, 
		PANEL_VALUE, tmp->login, 
		NULL);
	xv_set(host_window.basic.password, 
		PANEL_VALUE, tmp->password, 
		NULL);
	xv_set(host_window.basic.account, 
		PANEL_VALUE, tmp->account, 
		NULL);
	xv_set(host_window.advanced.proxy, 
		PANEL_VALUE, tmp->proxy, 
		NULL);
	xv_set(host_window.advanced.transfer_mode, 
		PANEL_VALUE, tmp->transfer_mode, 
		NULL);
	xv_set(host_window.advanced.remote_auto_cd, 
		PANEL_VALUE, tmp->remote_directory, 
		NULL);
	xv_set(host_window.advanced.local_auto_cd, 
		PANEL_VALUE, tmp->local_directory, 
		NULL);
	xv_set(host_window.advanced.os_type,
		PANEL_VALUE, tmp->os_type,
		NULL);
	xv_set(host_window.advanced.dir_parse, 
		PANEL_VALUE, tmp->dir_parse, 
		NULL);
	xv_set(host_window.advanced.comment, 
		PANEL_VALUE, tmp->comment, 
		NULL);

	if (try_proxy) {
		xv_set(host_window.advanced.proxy, 
			XV_SHOW, TRUE, 
			NULL);
	} else {
		xv_set(host_window.advanced.proxy, 
			XV_SHOW, FALSE, 
			NULL);
	}
	if (tmp->os_type == REMOTE_OS_OTHER) {
		xv_set(host_window.advanced.dir_parse,
			XV_SHOW, TRUE,
			NULL);
	} else {
		xv_set(host_window.advanced.dir_parse,
			XV_SHOW, FALSE,
			NULL);
	}
	xv_set(menu, 
		MENU_NOTIFY_STATUS, XV_ERROR, 
		NULL);
	if (!connected && auto_connect == TRUE) {
		dowhat = DOCONNECT;
		notify_stop();
	}
	fix_carets();
}


#ifdef USE_PROTOTYPES
void host_list_add_proc(Menu menu, Menu_item menu_item)
#else
void host_list_add_proc(menu, menu_item)
Menu	menu;
Menu_item menu_item;
#endif
{
	enter_host_info(1);
	xv_set(menu, 
		MENU_NOTIFY_STATUS, XV_ERROR, 
		NULL);
}

#ifdef USE_PROTOTYPES
void host_list_change_proc(Menu menu, Menu_item menu_item)
#else
void host_list_change_proc(menu, menu_item)
Menu	menu;
Menu_item menu_item;
#endif
{
	enter_host_info(0);
	xv_set(menu, 
		MENU_NOTIFY_STATUS, XV_ERROR, 
		NULL);
}

#ifdef USE_PROTOTYPES
void	host_save_proc(Menu menu, Menu_item menu_item)
#else
void	host_save_proc(menu, menu_item)
Menu	menu;
Menu_item	menu_item;
#endif
{
	write_ftptoolrc();
	list_changed = 0;
	timestamped = 0;
	reload_host_list_menu(hostlist_head);
	xv_set(menu, 
		MENU_NOTIFY_STATUS, XV_ERROR, 
		NULL);
}

#ifdef USE_PROTOTYPES
void	host_load_proc(Menu menu, Menu_item menu_item)
#else
void	host_load_proc(menu, menu_item)
Menu	menu;
Menu_item	menu_item;
#endif
{
	int answer;
#ifdef XVIEW3
	Xv_notice	notice;
#endif

	if (timestamped || list_changed) {
#ifdef XVIEW3
		notice = xv_create(host_window.panel, NOTICE,
			NOTICE_MESSAGE_STRINGS,
				"Your host list has changed since the last save.",
				"Really load original?",
				NULL,
			NOTICE_BUTTON_YES, "Yes",
			NOTICE_BUTTON_NO, "No",
			NOTICE_STATUS, &answer,
			XV_SHOW, TRUE,
			NULL);
		xv_destroy_safe(notice);
#else
		answer = notice_prompt(host_window.panel, NULL,
			NOTICE_MESSAGE_STRINGS,
				"Your host list has changed since the last save.",
				"Really load original?",
				NULL,
			NOTICE_BUTTON_YES, "Yes",
			NOTICE_BUTTON_NO, "No",
			NULL);
#endif
		if (answer != NOTICE_YES)
			return;
	}
	free_hostlist(hostlist_head);
	hostlist_head = new_hostlist();
	read_ftptoolrc();
	list_changed = 0;
	timestamped = 0;
	reload_host_list_menu(hostlist_head);
	xv_set(menu, 
		MENU_NOTIFY_STATUS, XV_ERROR, 
		NULL);
}

#ifdef USE_PROTOTYPES
void update_timestamp(void)
#else
void update_timestamp()
#endif
{
	time_t	t;
	time_t	time();
	char	*ctime();
	char	*s, *nl;
	char	*aliasname;
	struct hostlist *tmp;

	t = time((time_t *)NULL);
	s = ctime(&t);
	if (nl = index(s, '\n'))
		*nl = '\0';
	xv_set(host_window.advanced.last_visited,
		PANEL_LABEL_STRING, s,
		NULL);
	aliasname = (char *)xv_get(host_window.advanced.alias, PANEL_VALUE);
	tmp = gethostlist(hostlist_head, aliasname);
	if (tmp == NULL) {
		return;
	}
	free(tmp->last_visited);
	tmp->last_visited = strdup(s);
	if (tmp->last_visited == NULL) {
		fprintf(stderr, "Out of memory.\n");
		goto out;
	}

	timestamped++;
out:
	return;
}

#ifdef USE_PROTOTYPES
void enter_host_info(int warnchange)
#else
void enter_host_info(warnchange)
int		warnchange;
#endif
{
	char	*aliasname;
	char	*last_visited="Never";
	char 	*proxy;
	char 	*host;
	char	*login;
	char	*password;
	char	*account;
	char	*comment;
	int		transfer_mode;
	int		os_type;
	char	*remote_directory;
	char	*local_directory;
	char	*dir_parse;
	int		answer;
#ifdef XVIEW3
	Xv_notice notice;
#endif

	aliasname = (char *)xv_get(host_window.advanced.alias, PANEL_VALUE);
	if (aliasname[0] == '\0') {
		xv_set(host_window.frame, 
			FRAME_SHOW_FOOTER, TRUE, 
			FRAME_LEFT_FOOTER, "Please specify an alias name.",
			NULL);
		goto out;
	}
	proxy = (char *)xv_get(host_window.advanced.proxy, PANEL_VALUE);
	host = (char *)xv_get(host_window.basic.host, PANEL_VALUE);
	login = (char *)xv_get(host_window.basic.login, PANEL_VALUE);
	password = (char *)xv_get(host_window.basic.password, PANEL_VALUE);
	account = (char *)xv_get(host_window.basic.account, PANEL_VALUE);
	transfer_mode = xv_get(host_window.advanced.transfer_mode, PANEL_VALUE);
	remote_directory = (char *)xv_get(host_window.advanced.remote_auto_cd, 
		PANEL_VALUE);
	local_directory = (char *)xv_get(host_window.advanced.local_auto_cd, 
		PANEL_VALUE);
	os_type = xv_get(host_window.advanced.os_type, PANEL_VALUE);
	dir_parse = (char *)xv_get(host_window.advanced.dir_parse, PANEL_VALUE);
	comment = (char *)xv_get(host_window.advanced.comment, PANEL_VALUE);

	if (gethostlist(hostlist_head, aliasname)) {
		if (warnchange) {
#ifdef XVIEW3
			notice = xv_create(host_window.panel, NOTICE,
				NOTICE_MESSAGE_STRINGS,
					"That alias exists. Do you really want to change it?",
					NULL,
				NOTICE_BUTTON_YES, "Yes",
				NOTICE_BUTTON_NO, "No",
				NOTICE_STATUS, &answer,
				XV_SHOW, TRUE,
				NULL);
			xv_destroy_safe(notice);
#else
			answer = notice_prompt(host_window.panel, NULL,
				NOTICE_MESSAGE_STRINGS,
					"That alias exists. Do you really want to change it?",
					NULL,
				NOTICE_BUTTON_YES, "Yes",
				NOTICE_BUTTON_NO, "No",
				NULL);
#endif
			if (answer != NOTICE_YES)
				goto out;
		}
		last_visited = (char *)xv_get(host_window.advanced.last_visited, 
			PANEL_LABEL_STRING);
		delete_hostlist(hostlist_head, aliasname);
	}
	if ((hostlist_head = add_hostalias(hostlist_head, aliasname, 
		last_visited, proxy, host, login, password, account,
		transfer_mode, remote_directory, local_directory, 
		dir_parse, comment, os_type)) == NULL) {
		xv_set(host_window.frame, 
			FRAME_SHOW_FOOTER, TRUE, 
			FRAME_LEFT_FOOTER, "Add failed.",
			NULL);
		goto out;
	}
	list_changed++;
	reload_host_list_menu(hostlist_head);
out:
	return;
}

#ifdef USE_PROTOTYPES
void host_list_delete_proc(Menu menu, Menu_item menu_item)
#else
void host_list_delete_proc(menu, menu_item)
Menu	menu;
Menu_item menu_item;
#endif
{
	char	*aliasname;

	aliasname = (char *)xv_get(host_window.advanced.alias, PANEL_VALUE);

	if (gethostlist(hostlist_head, aliasname) == NULL) {
		xv_set(host_window.frame, 
			FRAME_SHOW_FOOTER, TRUE, 
			FRAME_LEFT_FOOTER, "No such alias",
			NULL);
		return;
	}
	delete_hostlist(hostlist_head, aliasname);
	list_changed++;
	reload_host_list_menu(hostlist_head);
	xv_set(menu, 
		MENU_NOTIFY_STATUS, XV_ERROR, 
		NULL);
}

#ifdef USE_PROTOTYPES
struct hostlist *new_hostlist(void)
#else
struct hostlist *new_hostlist()
#endif
{
	struct hostlist *tmp;

	tmp = (struct hostlist *)malloc(sizeof(struct hostlist));
	if (tmp == NULL)
		return NULL;
	bzero((char *)tmp, sizeof(struct hostlist));
	tmp->next = NULL;
	tmp->aliasname = NULL;
	tmp->last_visited = NULL;
	tmp->proxy = NULL;
	tmp->host = NULL;
	tmp->login = NULL;
	tmp->password = NULL;
	tmp->remote_directory = NULL;
	tmp->local_directory = NULL;
	tmp->dir_parse = NULL;
	tmp->comment = NULL;
	return tmp;
}

#ifdef USE_PROTOTYPES
struct hostlist *add_hostalias(struct hostlist *head, char *aliasname, 
	char *last_visited, char *proxy, char *host, char *login, 
	char *password, char *account, int transfer_mode, char *remote_directory, 
	char *local_directory, char *dir_parse, char *comment,
	int os_type)
#else
struct hostlist *add_hostalias(head, aliasname, last_visited, proxy, 
	host, login, password, account, transfer_mode, remote_directory, 
	local_directory, dir_parse, comment, os_type)
struct hostlist *head;
char	*aliasname;
char	*last_visited;
char	*proxy;
char	*host;
char	*login;
char	*password;
char	*account;
int		transfer_mode;
char	*remote_directory;
char	*local_directory;
char	*dir_parse;
char	*comment;
int		os_type;
#endif
{
	struct hostlist *tmp;
	struct hostlist *oldnext;
	int		rval = 0;

	/* add in sorted order */
	for (tmp = head; tmp->next != NULL; tmp = tmp->next)  {
		if (ignore_case)
			rval = strcasecmp(aliasname, tmp->next->aliasname);
		else
			rval = strcmp(aliasname, tmp->next->aliasname);
		if (rval < 0)
			break;
	}
	oldnext = tmp->next;
	tmp->next = new_hostlist();
	if (tmp->next == NULL) {
		tmp->next = oldnext;
		return NULL;
	}

	tmp->next->aliasname = strdup(aliasname);
	if (tmp->next->aliasname == NULL) {
		goto out;
	}

	tmp->next->last_visited = strdup(last_visited);
	if (tmp->next->last_visited == NULL) {
		goto out;
	}

	tmp->next->proxy = strdup(proxy);
	if (tmp->next->proxy == NULL) {
		goto out;
	}

	tmp->next->host = strdup(host);
	if (tmp->next->host == NULL) {
		goto out;
	}

	tmp->next->login = strdup(login);
	if (tmp->next->login == NULL) {
		goto out;
	}

	tmp->next->password = strdup(password);
	if (tmp->next->password == NULL) {
		goto out;
	}

	tmp->next->account = strdup(account);
	if (tmp->next->account == NULL) {
		goto out;
	}

	tmp->next->transfer_mode = transfer_mode;

	tmp->next->remote_directory = strdup(remote_directory);
	if (tmp->next->remote_directory == NULL) {
		goto out;
	}

	tmp->next->local_directory = strdup(local_directory);
	if (tmp->next->local_directory == NULL) {
		goto out;
	}

	tmp->next->dir_parse = strdup(dir_parse);
	if (tmp->next->dir_parse == NULL) {
		goto out;
	}

	tmp->next->os_type = os_type;
	/* see if it's really UNIX from older versions */
	if (tmp->next->os_type == REMOTE_OS_OTHER 
		&& !strcmp(tmp->next->dir_parse, UNIX_DIR_PATTERN)) {
		tmp->next->os_type = REMOTE_OS_UNIX;
	}

	tmp->next->comment = strdup(comment);
	if (tmp->next->comment == NULL) {
		goto out;
	}

	tmp->next->next = oldnext;
	return head;
out:
	tmp->next->next = NULL;
	free_hostlist(tmp->next);
	tmp->next = oldnext;
	return NULL;
}

#ifdef USE_PROTOTYPES
void free_hostlist(struct hostlist *head)
#else
void free_hostlist(head)
struct hostlist *head;
#endif
{
	struct hostlist *tmp;

	while (head) {
		tmp = head->next;
		if (head->aliasname)
			free(head->aliasname);
		if (head->last_visited)
			free(head->last_visited);
		if (head->proxy)
			free(head->proxy);
		if (head->host)
			free(head->host);
		if (head->login)
			free(head->login);
		if (head->password)
			free(head->password);
		if (head->account)
			free(head->account);
		if (head->remote_directory)
			free(head->remote_directory);
		if (head->local_directory)
			free(head->local_directory);
		if (head->dir_parse)
			free(head->dir_parse);
		if (head->comment)
			free(head->comment);
		free((char *)head);
		head = tmp;
	}
}

#ifdef USE_PROTOTYPES
struct hostlist *gethostlist(struct hostlist *head, char *aliasname)
#else
struct hostlist *gethostlist(head, aliasname)
struct hostlist *head;
char	*aliasname;
#endif
{
	struct hostlist *tmp;

	for (tmp = head->next; tmp != NULL; tmp = tmp->next)
		if (!strcmp(aliasname, tmp->aliasname)) 
			return tmp;
	return NULL;
}

#ifdef USE_PROTOTYPES
int delete_hostlist(struct hostlist *head, char *aliasname)
#else
int delete_hostlist(head, aliasname)
struct hostlist *head;
char	*aliasname;
#endif
{
	struct hostlist *tmp;
	struct hostlist *tmp2;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next) {
		if (!strcmp(aliasname, tmp->next->aliasname)) {
			/* delete existing entry */
			tmp2 = tmp->next->next;
			tmp->next->next = NULL;
			free_hostlist(tmp->next);
			tmp->next = tmp2;
			return 1;
		}
	}
	return 0;
}

#ifdef USE_PROTOTYPES
void read_ftptoolrc(void)
#else
void read_ftptoolrc()
#endif
{
	int	fd;
	static char	aliasname[MAXCOMMENTLEN + 1];
	static char	host[MAXHOSTNAMELEN + 1];
	static char	login[MAXLOGINLEN + 1];
	static char	password[MAXHOSTNAMELEN + 1];
	static char	comment[MAXCOMMENTLEN + 1];
	FILE	*fp;
	int ch;

	fd = ftptoolrc_fd(O_RDONLY, 0600);
	if (fd == -1) {
		if (netrc_filename)
			host_append_netrc_proc();
		return;
	}
	/* ftptoolrc file format */
	/*
	 Alias
	 direct (ignored)
	 host
	 login
	 password
	 Comment
	 */
	fp = fdopen(fd, "r");
	if (fp == NULL) {
		close(fd);
		return;
	}
	/* see if it's a 'new' format */
	ch = getc(fp);
	switch (ch) {
	case '#':
		fclose(fp);
		read_oldftptoolrc();
		return;
	case '%':
		fclose(fp);
		read_newftptoolrc();
		return;
	default:
		break;
	}
	/* oldest format */
	ungetc(ch, fp);
	while (read_entry(0, fp,aliasname,(char *)NULL, (char *)NULL,
		host,login, password,(int *)NULL, (char *)NULL, (char *)NULL, 
		UNIX_DIR_PATTERN, comment))
			if (add_hostalias(hostlist_head, aliasname, "Never", 
				DEFAULT_PROXY, host, login, password, "", BINARY,
				".", ".", UNIX_DIR_PATTERN, comment,
				REMOTE_OS_OTHER) == NULL)
					break;
	fclose(fp);
}

#ifdef USE_PROTOTYPES
int read_entry(int version, FILE *fp, char *aliasname, char *last_visited, 
	char *proxy, char *host, char *login, char *password, int *transfer_mode, 
	char *rdir, char *ldir, char *dir_parse, char *comment)
#else
int read_entry(version, fp, aliasname, last_visited, proxy, host, 
	login, password, transfer_mode, rdir, ldir, dir_parse, comment)
int		version;
FILE	*fp;
char	*aliasname;
char	*last_visited;
char	*proxy;
char	*host;
char	*login;
char	*password;
int		*transfer_mode;
char	*rdir;
char	*ldir;
char	*dir_parse;
char	*comment;
#endif
{
	char	*nl;
	char	*dpasswd;

	if (fgets(aliasname, MAXCOMMENTLEN+1,fp) == NULL) 
		if (feof(fp))
			return 0;
		else
			goto out;
	if (nl = index(aliasname, '\n'))
		*nl = '\0';
	if (version > 2) {
		if (fgets(last_visited, MAXPATHLEN+1,fp) == NULL) 
			goto out;
		if (nl = index(last_visited, '\n'))
			*nl = '\0';
	}
	if (fgets(scratch, MAXHOSTNAMELEN+1,fp) == NULL) 
		goto out;
	if (version > 0) {
		if (fgets(proxy, MAXHOSTNAMELEN+1,fp) == NULL) 
			goto out;
		if (nl = index(proxy, '\n'))
			*nl = '\0';
	}
	if (fgets(host, MAXHOSTNAMELEN+1,fp) == NULL) 
		goto out;
	if (nl = index(host, '\n'))
		*nl = '\0';
	if (fgets(login, MAXLOGINLEN+1,fp) == NULL) 
		goto out;
	if (nl = index(login, '\n'))
		*nl = '\0';
	if (fgets(password, MAXPASSWORDLEN+1,fp) == NULL) 
		goto out;
	if (nl = index(password, '\n'))
		*nl = '\0';
	if (version > 2) {
		/* decrypt it */
		if (version > 3)
			dpasswd = ftptool_decrypt(password, login_name);
		else
			dpasswd = old_ftptool_decrypt(password, login_name);
		if (dpasswd != NULL) {
			strcpy(password, dpasswd);
			free(dpasswd);
		}
	} 
	if (version > 4) {
		if (fgets(scratch, MAXHOSTNAMELEN+1,fp) == NULL) 
			goto out;
		*transfer_mode = atoi(scratch);
	}
	if (version > 0) {
		if (fgets(rdir, MAXPATHLEN+1,fp) == NULL) 
			goto out;
		if (nl = index(rdir, '\n'))
			*nl = '\0';
	}
	if (version > 1) {
		if (fgets(ldir, MAXPATHLEN+1,fp) == NULL) 
			goto out;
		if (nl = index(ldir, '\n'))
			*nl = '\0';
	}
	if (version > 2) {
		if (fgets(dir_parse, MAXPATHLEN+1,fp) == NULL) 
			goto out;
		if (nl = index(dir_parse, '\n'))
			*nl = '\0';
	}
	if (fgets(comment, MAXCOMMENTLEN+1,fp) == NULL) 
		goto out;
	if (nl = index(comment, '\n'))
		*nl = '\0';
	return 1;
out:
	fprintf(stderr, "bad entry for %s in %s\n", aliasname,
		FTPTOOL_RC);
	return 0;
}

#ifdef USE_PROTOTYPES
void read_oldftptoolrc(void)
#else
void read_oldftptoolrc()
#endif
{
	int	fd;
	static char	aliasname[MAXALIASLEN + 1];
	static char	last_visited[41];
	static int		transfer_mode;
	static char	proxy[MAXHOSTNAMELEN + 1];
	static char	host[MAXHOSTNAMELEN + 1];
	static char	login[MAXLOGINLEN + 1];
	static char	password[MAXPASSWORDLEN + 1];
	static char	remote_directory[MAXPATHLEN + 1];
	static char	local_directory[MAXPATHLEN + 1];
	static char	dir_parse[MAXPATHLEN + 1];
	static char	comment[MAXCOMMENTLEN + 1];
	FILE	*fp;
	int	version, patch;
	int	list_version;

	fd = ftptoolrc_fd(O_RDONLY, 0600);
	if (fd == -1) {
		if (netrc_filename)
			host_append_netrc_proc();
		return;
	}
	/* new ftptoolrc file format */
	/* first line */
	/* # Host List : Ftptool Version 3.3 */
	/*
	 Alias
	 direct (ignored)
	 proxy
	 host
	 login
	 password
	 remote directory
	 local directory
	 dir parse line
	 Comment
	 */
	fp = fdopen(fd, "r");
	if (fp == NULL) {
		close(fd);
		return;
	}
	/* first line is a comment line */
	fgets(comment, MAXCOMMENTLEN + 1, fp);
	sscanf(comment, "# Host List : Ftptool Version %d.%d\n", &version, &patch);
	if (version >= 4 && patch >= 1)
		list_version = 5;
	else if (version == 4 && patch == 0)
		list_version = 4;
	else if (version == 3 && patch == 3)
		list_version = 3;
	else if (version == 3 && patch == 2)
		list_version = 2;
	else
		list_version = 1;

	while (read_entry(list_version, fp, 
		aliasname, last_visited, proxy, host, login, password, 
		&transfer_mode, remote_directory, local_directory, dir_parse, 
		comment)) {
			if (add_hostalias(hostlist_head, aliasname, 
				list_version >= 3 ? last_visited : "Never", 
				proxy, host, login, password, "",
				list_version >= 5 ? transfer_mode : BINARY,
				remote_directory, 
				list_version >= 2 ? local_directory : ".", 
				list_version >= 3 ? dir_parse : UNIX_DIR_PATTERN,
				comment, REMOTE_OS_OTHER) == NULL)
					break;
	}
	fclose(fp);
}

#ifdef USE_PROTOTYPES
int read_newentry(int version, FILE *fp, char *aliasname, char *last_visited, 
	char *proxy, char *host, char *login, char *password, char *account,
	int *transfer_mode, char *rdir, char *ldir, char *dir_parse, 
	char *comment, int *os_type)
#else
int read_newentry(version, fp, aliasname, last_visited, proxy, host, 
	login, password, account, transfer_mode, rdir, ldir, dir_parse, comment,
	os_type)
int		version;
FILE	*fp;
char	*aliasname;
char	*last_visited;
char	*proxy;
char	*host;
char	*login;
char	*password;
char	*account;
int		*transfer_mode;
char	*rdir;
char	*ldir;
char	*dir_parse;
char	*comment;
int		*os_type;
#endif
{
	char	*dpasswd;
	static char linebuf[2 * MAXCOMMENTLEN + 1];
	/*
	 Alias:Alias
	 Last:Last Visited
	 Proxy:proxy
	 Host:host
	 Login:login
	 Password:password
	 Account:account
	 OS Type:os type
	 DT:dir parse
	 Mode:transfer_mode
	 RCD:remote directory
	 LCD:local directory
	 Comment: comment
	 --
	 */

	if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
		if (feof(fp))
			return 0;
		else
			goto out;
	}
	*aliasname = '\0';
	sscanf(linebuf, "Alias:%[^\n]", aliasname);

	if (fgets(linebuf, MAXPATHLEN+1,fp) == NULL) 
		goto out;
	*last_visited = '\0';
	sscanf(linebuf, "Last:%[^\n]", last_visited);

	if (fgets(linebuf, MAXHOSTNAMELEN+1,fp) == NULL) 
		goto out;
	*proxy = '\0';
	sscanf(linebuf, "Proxy:%[^\n]", proxy);

	if (fgets(linebuf, MAXHOSTNAMELEN+1,fp) == NULL) 
		goto out;
	*host = '\0';
	sscanf(linebuf, "Host:%[^\n]", host);

	if (fgets(linebuf, MAXLOGINLEN+1,fp) == NULL) 
		goto out;
	*login = '\0';
	sscanf(linebuf, "Login:%[^\n]", login);

	if (fgets(linebuf, MAXPASSWORDLEN+1,fp) == NULL) 
		goto out;
	*password = '\0';
	sscanf(linebuf, "Password:%[^\n]", password);
	dpasswd = ftptool_decrypt(password, login_name);
	if (dpasswd != NULL) {
		strcpy(password, dpasswd);
		free(dpasswd);
	}

	/* Possible account field */
	if (fgets(linebuf, MAXPASSWORDLEN+1,fp) == NULL) 
		goto out;
	*account = '\0';
	if (!strncmp(linebuf, "Account:", 8)) {
		sscanf(linebuf, "Account:%[^\n]", account);
		if (fgets(linebuf, MAXHOSTNAMELEN+1,fp) == NULL) 
			goto out;
	}

	*scratch = '\0';
	sscanf(linebuf, "OS Type:%[^\n]", scratch);
	if (!strcmp(scratch, "UNIX"))
		*os_type = REMOTE_OS_UNIX;
	else if (!strcmp(scratch, "VMS"))
		*os_type = REMOTE_OS_VMS;
	else
		*os_type = REMOTE_OS_OTHER;

	if (fgets(linebuf, MAXPATHLEN+1,fp) == NULL) 
		goto out;
	*dir_parse = '\0';
	sscanf(linebuf, "DT:%[^\n]", dir_parse);

	if (fgets(linebuf, MAXHOSTNAMELEN+1,fp) == NULL) 
		goto out;
	*scratch = '\0';
	sscanf(linebuf, "Mode:%[^\n]", scratch);
	*transfer_mode = atoi(scratch);

	if (fgets(linebuf, MAXPATHLEN+1,fp) == NULL) 
		goto out;
	*rdir = '\0';
	sscanf(linebuf, "RCD:%[^\n]", rdir);

	if (fgets(linebuf, MAXPATHLEN+1,fp) == NULL) 
		goto out;
	*ldir = '\0';
	sscanf(linebuf, "LCD:%[^\n]", ldir);

	if (fgets(linebuf, MAXCOMMENTLEN+1,fp) == NULL) 
		goto out;
	*comment = '\0';
	sscanf(linebuf, "Comment:%[^\n]", comment);

	/* Dump separator */
	if (fgets(linebuf, MAXCOMMENTLEN+1,fp) == NULL) 
		goto out;

	return 1;
out:
	fprintf(stderr, "%s: Bad entry for %s in %s\n", 
		program_name, aliasname, FTPTOOL_RC);
	return 0;
}


#ifdef USE_PROTOTYPES
void read_newftptoolrc(void)
#else
void read_newftptoolrc()
#endif
{
	int	fd;
	static char	aliasname[MAXALIASLEN + 1];
	static char	last_visited[41];
	int		transfer_mode;
	int		os_type;
	static char	proxy[MAXHOSTNAMELEN + 1];
	static char	host[MAXHOSTNAMELEN + 1];
	static char	login[MAXLOGINLEN + 1];
	static char	password[MAXPASSWORDLEN + 1];
	static char	account[MAXPASSWORDLEN + 1];
	static char	remote_directory[MAXPATHLEN + 1];
	static char	local_directory[MAXPATHLEN + 1];
	static char	dir_parse[MAXPATHLEN + 1];
	static char	comment[MAXCOMMENTLEN + 1];
	FILE	*fp;
	int	version, patch;

	fd = ftptoolrc_fd(O_RDONLY, 0600);
	if (fd == -1) {
		if (netrc_filename)
			host_append_netrc_proc();
		return;
	}
	/* new ftptoolrc file format */
	/* first line */
	/* % Host List : Ftptool Version 4.4 */
	/*
	 Alias:Alias
	 Last:Last Visited
	 Proxy:proxy
	 Host:host
	 Login:login
	 Password:password
	 OS Type:os type
	 DT:dir parse
	 Mode:transfer_mode
	 RCD:remote directory
	 LCD:local directory
	 Comment: Comment
	 --
	 */
	fp = fdopen(fd, "r");
	if (fp == NULL) {
		close(fd);
		return;
	}
	/* first line is a comment line */
	fgets(comment, MAXCOMMENTLEN + 1, fp);
	sscanf(comment, "%% Host List : Ftptool Version %d.%d\n", &version, &patch);

	while (read_newentry(0, fp, 
		aliasname, last_visited, proxy, host, login, password, account,
		&transfer_mode, remote_directory, local_directory, dir_parse, 
		comment, &os_type)) {
			if (add_hostalias(hostlist_head, aliasname, 
				last_visited, proxy, host, login, password, account,
				transfer_mode, remote_directory, 
				local_directory, dir_parse, comment, os_type) == NULL)
					break;
	}
	fclose(fp);
}

#ifdef USE_PROTOTYPES
void write_ftptoolrc(void)
#else
void write_ftptoolrc()
#endif
{
	int	fd;
	struct hostlist *tmp;
	FILE	*fp;
	char	*epasswd;
	char	*type;

	fd = ftptoolrc_fd(O_WRONLY | O_TRUNC | O_CREAT, 0600);
	if (fd == -1) {
		perror("writing host list");
		return;
	}
	/* ftptoolrc file format */
	/*
	 Alias:Alias
	 Last:Last Visited
	 Proxy:proxy
	 Host:host
	 Login:login
	 Password:password
	 Account:account
	 OS Type:os type
	 DT:dir parse
	 Mode:transfer_mode
	 RCD:remote directory
	 LCD:local directory
	 Comment: Comment
	 --
	 */
	fp = fdopen(fd, "w"); 
	if (fp == NULL) {
		close(fd);
		return;
	}
	fprintf(fp, "%% Host List : %s\n", header_name);
	for (tmp = hostlist_head->next; tmp != NULL; tmp = tmp->next) {
		epasswd = ftptool_encrypt(tmp->password, login_name);
		if (epasswd == NULL) {
			fprintf(stderr, "Out of memory.\n");
			exit(1);
		}
		fprintf(fp, "Alias:%s\n", tmp->aliasname);
		fprintf(fp, "Last:%s\n", tmp->last_visited);
		fprintf(fp, "Proxy:%s\n", tmp->proxy);
		fprintf(fp, "Host:%s\n", tmp->host);
		fprintf(fp, "Login:%s\n", tmp->login);
		fprintf(fp, "Password:%s\n", epasswd);
		fprintf(fp, "Account:%s\n", tmp->account);
		switch(tmp->os_type) {
		case REMOTE_OS_UNIX:
			type = "UNIX";
			break;
		case REMOTE_OS_VMS:
			type = "VMS";
			break;
		case REMOTE_OS_OTHER:
		default:
			type = "Other";
			break;
		}
		fprintf(fp, "OS Type:%s\n", type);
		fprintf(fp, "DT:%s\n", tmp->dir_parse);
		fprintf(fp, "Mode:%d\n", tmp->transfer_mode);
		fprintf(fp, "RCD:%s\n", tmp->remote_directory);
		fprintf(fp, "LCD:%s\n", tmp->local_directory);
		fprintf(fp, "Comment:%s\n", tmp->comment);
		fprintf(fp, "--\n");
		free(epasswd);
	}

	fclose(fp);
}

#ifdef USE_PROTOTYPES
int ftptoolrc_fd(int flags, int mode)
#else
int ftptoolrc_fd(flags, mode)
int		flags;
int		mode;
#endif
{
	char	*filename=NULL;
	int		fd;

	filename = find_dotfile(FTPTOOL_RC);
	if (filename == NULL) {
		if (flags == O_RDONLY) {
			/* try global one */
			filename = strdup(GLOBAL_FTPTOOLRC);
			if (filename == NULL) {
				return -1;
			}
		} else {
			if ((filename = create_dotfile(FTPTOOL_RC, 0600)) == NULL)
				return -1;
		}
	}

	/* try current directory */
	if ((fd = open(filename, flags, mode)) != -1) {
		free(filename);
		return fd;
	}
	free(filename);
	return -1;
}

#ifdef USE_PROTOTYPES
void reload_host_list_menu(struct hostlist *head)
#else
void reload_host_list_menu(head)
struct hostlist *head;
#endif
{
	Menu	menu = xv_get(host_window.hosts, PANEL_ITEM_MENU);
	int	nitems = xv_get(menu, MENU_NITEMS);
	int	row;
	struct hostlist *tmp;
	Menu_item	mi;
	Frame	frame;
	int		isshown;
	int		maxwidth=0;
	int		width=0;
	double	cols;

	frame = (Panel)xv_get(menu, MENU_PIN_WINDOW);
	isshown = FALSE;
	if (frame) 
		isshown = xv_get(frame, XV_SHOW);

	if (isshown) {
		xv_set(frame, 
#ifdef XVIEW3
			FRAME_CMD_PIN_STATE, FRAME_CMD_PIN_OUT,
#else
			FRAME_CMD_PUSHPIN_IN, FALSE,
#endif
			XV_SHOW, FALSE,
			NULL);
	}
	/* 2 items minimum, 1 for title */
	for (row = nitems; row > 1; row--)
		xv_set(menu,
			MENU_REMOVE, row,
			NULL);

	nhostlist_items = 0;
	for (tmp=head->next; tmp != NULL; tmp=tmp->next) {
		width = strlen(tmp->aliasname);
		maxwidth = MAX(maxwidth, width);
		mi = (Menu_item)xv_create(XV_NULL, MENUITEM,
			MENU_STRING, tmp->aliasname,
			MENU_NOTIFY_PROC, host_list_item_proc,
			MENU_RELEASE,
			NULL);
		if (mi == XV_NULL) {
			fprintf(stderr, "Out of memory for menu item.\n");
			exit(1);
		}
		xv_set(menu,
			MENU_APPEND_ITEM, mi,
			PANEL_PAINT, PANEL_NONE,
			NULL);
		nhostlist_items++;
	}
	if (nhostlist_items == 0) {
		mi = (Menu_item)xv_create(XV_NULL, MENUITEM,
			MENU_STRING, "No Hosts!",
			MENU_RELEASE,
			NULL);
		if (mi == XV_NULL) {
			fprintf(stderr, "Out of memory for menu item.\n");
			exit(1);
		}
		xv_set(menu,
			MENU_APPEND_ITEM, mi,
			NULL);
	}

	if (maxwidth == 0)
		maxwidth = 1;
	
	cols = 2.0 * nhostlist_items;
	cols /= maxwidth;
	cols = ceil(sqrt(cols));
	xv_set(menu,
		MENU_NCOLS,(int)cols,
		NULL);
	if (isshown) {
		xv_set(frame, 
#ifdef XVIEW3
			FRAME_CMD_PIN_STATE, FRAME_CMD_PIN_IN,
#else
			FRAME_CMD_PUSHPIN_IN, TRUE,
#endif
			XV_SHOW, TRUE,
			NULL);
	}
}

#ifdef USE_PROTOTYPES
char *create_dotfile(char *dotfile, int mode)
#else
char *create_dotfile(dotfile, mode)
char	*dotfile;
int		mode;
#endif
{
	char	*home;
	char	*filename=NULL;
	int		fd;

	home = getenv("HOME");
	if (home != NULL && home[0] != '\0') {
		/* try $HOME/dotfile */
		filename = malloc((unsigned int)(strlen(home)+1+strlen(dotfile)+1));
		if (filename == NULL)
			return NULL;
		sprintf(filename, "%s/%s", home, dotfile);
		if ((fd = creat(filename, mode)) == -1) {
			free(filename);
			return NULL;
		}
		close(fd);
		/* found it */
		return filename;
	}
	filename = strdup(dotfile);
	if (filename == NULL)
		return NULL;
	if ((fd = creat(filename, mode)) == -1) {
		free(filename);
		return NULL;
	}
	close(fd);

	return filename;
}

#ifdef USE_PROTOTYPES
char *find_dotfile(char *dotfile)
#else
char *find_dotfile(dotfile)
char	*dotfile;
#endif
{
	char	*home;
	char	*filename=NULL;

	home = getenv("HOME");
	if (home != NULL && home[0] != '\0') {
		/* try $HOME/dotfile */
		filename = malloc((unsigned int)(strlen(home)+1+strlen(dotfile)+1));
		if (filename == NULL)
			return NULL;
		sprintf(filename, "%s/%s", home, dotfile);
		if (access(filename, F_OK) == -1) {
			free(filename);
			return NULL;
		}
		/* found it */
		return filename;
	}
	filename = strdup(dotfile);
	if (filename == NULL)
		return NULL;
	if (access(filename, F_OK) == -1) {
		free(filename);
		return NULL;
	}

	return filename;
}

#define MACHINE		1
#define LOGIN		2
#define PASSWORD	3
#define ACCOUNT		4
#define MACDEF		5

#ifdef USE_PROTOTYPES
int netrc_token(FILE *fp)
#else
int netrc_token(fp)
FILE	*fp;
#endif
{
	char	keyword[MAXPATHLEN + 1];
	int		ch, lastchar;

	for (;;) {
		if (fscanf(fp,"%s", keyword) == EOF)
			return EOF;
		if (!strcmp(keyword, "machine"))
			return MACHINE;
		if (!strcmp(keyword, "login"))
			return LOGIN;
		if (!strcmp(keyword, "password"))
			return PASSWORD;
		if (!strcmp(keyword, "macdef")) {
			lastchar = 'e';
			ch = 'f';
			do {
				lastchar = ch;
				ch = getc(fp);
			} while (ch != EOF && (ch != '\n' && lastchar != '\n'));
		}
	}
}


#ifdef USE_PROTOTYPES
void	host_append_netrc_proc(void)
#else
void	host_append_netrc_proc()
#endif
{
	FILE 	*fp;
	char	machine[MAXHOSTNAMELEN + 1];
	char	login[MAXHOSTNAMELEN + 1];
	char	password[MAXPASSWORDLEN+ 1];
	int		foundmachine=0;

	if ((fp = fopen(netrc_filename, "r")) == NULL) {
		footer_message("%s exists but can not be read.", 
			netrc_filename, (char *)NULL);
		return;
	}
	/* read each line */
	/* looking for "machine" surrounded by white space */
	/* if we find it, then look for other tokens until EOF or "machine" */
	/* look for "login" name or "password" name */
	/* machine ray login demo password mypassword */

	for (;;) {
		switch(netrc_token(fp)) {
		case MACHINE:
			if (foundmachine)
				add_netrc(machine, login, password);
			foundmachine = 1;
			strcpy(machine, "");
			strcpy(login, "");
			strcpy(password, "");
			if (fscanf(fp, "%s", machine) == EOF) {
				goto out;
			}
			break;
		case LOGIN:
			if (!foundmachine || fscanf(fp, "%s", login) == EOF) {
				footer_message("Error in .netrc", (char *)NULL);
				goto out;
			}
			break;
		case PASSWORD:
			if (!foundmachine || fscanf(fp, "%s", password) == EOF) {
				footer_message("Error in .netrc", (char *)NULL);
				goto out;
			}
			break;
		case EOF:
			add_netrc(machine, login, password);
			goto out;
		default:
			break;
		}
	}

out:
	fclose(fp);
	return;
}

#ifdef USE_PROTOTYPES
void add_netrc(char *machine, char *login, char *password)
#else
void add_netrc(machine, login, password)
char	*machine;
char	*login;
char	*password;
#endif
{
	if (machine[0] == '\0' || login[0] == '\0') {
		footer_message("Incomplete .netrc entry", (char *)NULL);
		return;
	}
	sprintf(scratch, "%s %s", machine, login);
	if (gethostlist(hostlist_head, scratch)) {
		footer_message("Alias \"%s\" already exists.", scratch, (char *)NULL);
		return;
	}
	if ((hostlist_head = add_hostalias(hostlist_head, scratch, "Never",
		DEFAULT_PROXY, machine, login, password, "", BINARY,
		".", ".", UNIX_DIR_PATTERN, "From .netrc", REMOTE_OS_UNIX)) == NULL) {
		xv_set(host_window.frame, 
			FRAME_LEFT_FOOTER, "Add failed",
			NULL);
		return;
	}
	list_changed++;
	reload_host_list_menu(hostlist_head);
}

#ifdef USE_PROTOTYPES
char key_to_char(char *key)
#else
char key_to_char(key)
char	*key;
#endif
{
	char	*tmp = key;
	char	c = '\0';

	while (*tmp) {
		c ^= *tmp;
		tmp++;
	}
	c &= 0x7f;
	return c;
}

#ifdef USE_PROTOTYPES
char *ftptool_encrypt(char *s, char *key)
#else
char *ftptool_encrypt(s, key)
char	*s;
char	*key;
#endif
{
	char	k;
	char	c;
	char	*es;
	char	*scanstr;
	char	*changestr;

	k = key_to_char(key);
	es = (char *)malloc((unsigned int)(2 * strlen(s) + 1));
	if (es == NULL) {
		fprintf(stderr, "Out of memory\n");
		return NULL;
	}
	scanstr = s;
	changestr = es;
	while (*scanstr) {
		c = *scanstr++ ^ k;
		if (iscntrl(c)) {
			*changestr++ = '^';
			if (c == '\177')
				*changestr++ = '?';
			else
				*changestr++ = c + 'A';
		} else if (c == '^') {
			*changestr++ = '^';
			*changestr++ = '~';
		} else {
			*changestr++ = c;
		}
	}
	*changestr = '\0';
	return es;
}

#ifdef USE_PROTOTYPES
char *ftptool_decrypt(char *s, char *key)
#else
char *ftptool_decrypt(s, key)
char	*s;
char	*key;
#endif
{
	char	k;
	char	c;
	char	*ds;
	char	*scanstr;
	char	*changestr;

	k = key_to_char(key);
	ds = (char *)malloc((unsigned int)(strlen(s) + 1));
	if (ds == NULL) {
		fprintf(stderr, "Out of memory\n");
		return NULL;
	}
	scanstr = s;
	changestr = ds;
	while (*scanstr) {
		c = *scanstr++;
		if (c != '^') {
			*changestr++ = c ^ k;
		} else {
			c = *scanstr++;
			if (c == '?') {
				*changestr = '\177';
			} else if (c == '~') {
				*changestr = '^';
			} else {
				*changestr = c - 'A';
			}
			*changestr++ ^= k;
		}
	}
	*changestr = '\0';
	return ds;
}

#ifdef USE_PROTOTYPES
char *old_ftptool_decrypt(char *s, char *key)
#else
char *old_ftptool_decrypt(s, key)
char	*s;
char	*key;
#endif
{
	char	k;
	char	*ds;
	char	*tmp;

	k = key_to_char(key);
	ds = strdup(s);
	if (ds == NULL) {
		fprintf(stderr, "Out of memory\n");
		return NULL;
	}
	tmp = ds;
	while (*tmp) {
		*tmp++ ^= k;
	}
	return ds;
}

#ifdef USE_PROTOTYPES
struct hostlist *sort_hostlist(struct hostlist *head)
#else
struct hostlist *sort_hostlist(head)
struct hostlist *head;
#endif
{
	struct hostlist *tmp;
	struct hostlist *newhead, *current;
	int	rval;

	newhead = new_hostlist();
	if (newhead == NULL)
		return head;

	while (head->next != NULL) {
		/* remove first element */
		current = head->next;
		head->next = current->next;
		current->next = NULL;
		/* insert in proper place */
		for (tmp = newhead; tmp->next != NULL; tmp = tmp->next)  {
			if (ignore_case)
				rval = strcasecmp(current->aliasname, tmp->next->aliasname);
			else
				rval = strcmp(current->aliasname, tmp->next->aliasname);
			if (rval < 0) {
				break;
			}
		}
		current->next = tmp->next;
		tmp->next = current;
	}
	free_hostlist(head);
	return newhead;
}
