/*
 * Gnophone: A client for the Asterisk PBX
 *
 * Copyright (C) 2000-2005, Digium, Inc.
 *
 * Written by Mark Spencer
 *
 * Linux/UNIX version distributed under the terms of
 * the GNU General Public License
 *
 * upgrade.c Automatic upgrade Code
 *
 */
#include "gnophone.h"

#define FTP_SITE "ftp.gnophone.com"
#define FTP_PORT 21
#define FTP_CONNECT_TIMEOUT 5
#define FTP_GET_SEQ "CWD pub/gnophone\r\nLIST\r\n"
#define MAXARGS 15
#define MAXVERSIONARGS 10
#define MAXBUF 256

/* Number of files that need to be upgraded. */
#define NUMFILES 5

const char *files[NUMFILES+1] = {
	"gnophone",
	"mozilla-devel",
	"mozilla",
	"iax-devel",
	"iax",
	0
};

struct {
	int rtag, wtag, ttag, prtag, pwtag;
	int rpmtag, erpmtag;
	int timeoutstat;
	GtkWidget **winpptr;
	GtkWidget *blab, *table;
	in_addr_t servip;
	int conn, pconn, ftpfd;
	char *highestlocal[NUMFILES];
	char *highestnetwork[NUMFILES];
	int pty, tty, epty, etty;
	FILE *stream, *estream;
	GtkObject *adj1, *adj2, *adj3;
} info;

typedef struct upgrade_info UPGRADE;
typedef UPGRADE * UPGRADEPTR;

void upgrade_start(GtkWidget *);
void upgrade_stop(void);
void upgrade_message(char *, ...);
void upgrade_error(char *, ...);
void upgrade_set_status(char *, ...);
int upgrade_set_timeout(gpointer);
char *upgrade_ftp_conn(void);
char *upgrade_ftp_conn_pasv( short);
void upgrade_ftp_read(gpointer, gint, GdkInputCondition);
char *upgrade_ftp_write(int, char *);
int upgrade_ftp_parse(int, char *, int);
int upgrade_file_valid(char *);
void upgrade_file(char *);
int upgrade_compare_files(const char *, const char *);
char * upgrade_rpm_get_files(void);
char * upgrade_rpm_download_files(char *);
char * upgrade_run_rpm(char **);
void upgrade_rpm_read(gpointer, gint, GdkInputCondition);
int upgrade_open_a_pty(int *, int *);
void upgrade_cleanup(void);

GtkWidget *ilabel, *blabel;

void upgrade_start(GtkWidget *mw)
{
	GtkWidget *winup;
	GtkWidget *mvbox, *bhbox;
	GtkWidget *bcanc;
	GtkWidget *table;
	GtkWidget *prog1, *prog2, *prog3;
	GtkWidget *label1, *label2, *label3;
	GtkObject *adj1, *adj2, *adj3;
	char *err;

	winup = gtk_window_new(GTK_WINDOW_DIALOG);
	gtk_window_set_position(GTK_WINDOW(winup), GTK_WIN_POS_CENTER);
	gtk_widget_set_usize(GTK_WIDGET(winup), 400, 150);
	gtk_window_set_title(GTK_WINDOW(winup), "Performing automatic upgrade...");
	gtk_container_set_border_width(GTK_CONTAINER(winup), 10);
	gtk_signal_connect_object(GTK_OBJECT(winup), "delete_event", GTK_SIGNAL_FUNC(upgrade_stop), (gpointer)&info);

	mvbox = gtk_vbox_new(FALSE, 10);
	gtk_container_add(GTK_CONTAINER(winup), mvbox);

	info.table = table = gtk_table_new(2, 3, 0);
	gtk_table_set_row_spacings(GTK_TABLE(table), 5);
	gtk_box_pack_start(GTK_BOX(mvbox), table, TRUE, TRUE, 0);

	label1 = gtk_label_new("Preparing:");
	gtk_table_attach_defaults(GTK_TABLE(table), label1, 0, 1, 0, 1);

	label2 = gtk_label_new("Installing:");
	gtk_table_attach_defaults(GTK_TABLE(table), label2, 0, 1, 1, 2);

	label3 = gtk_label_new("Complete:");
	gtk_table_attach_defaults(GTK_TABLE(table), label3, 0, 1, 2, 3);

	adj1 = gtk_adjustment_new(0, 0, 100, 1, 10, 20);
	adj2 = gtk_adjustment_new(0, 0, 100, 1, 10, 20);
	adj3 = gtk_adjustment_new(0, 0, 100, 1, 10, 20);

	prog1 = gtk_progress_bar_new_with_adjustment(GTK_ADJUSTMENT(adj1));
	gtk_progress_set_show_text(GTK_PROGRESS(prog1), TRUE);
	gtk_table_attach_defaults(GTK_TABLE(table), prog1, 1, 2, 0, 1);

	prog2 = gtk_progress_bar_new_with_adjustment(GTK_ADJUSTMENT(adj2));
	gtk_progress_set_show_text(GTK_PROGRESS(prog2), TRUE);
	gtk_table_attach_defaults(GTK_TABLE(table), prog2, 1, 2, 1, 2);

	prog3 = gtk_progress_bar_new_with_adjustment(GTK_ADJUSTMENT(adj3));
	gtk_progress_set_show_text(GTK_PROGRESS(prog3), TRUE);
	gtk_table_attach_defaults(GTK_TABLE(table), prog3, 1, 2, 2, 3);

	ilabel = gtk_label_new("");
	gtk_box_pack_start(GTK_BOX(mvbox), ilabel, FALSE, FALSE, 0);

	bhbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_end(GTK_BOX(mvbox), bhbox, FALSE, FALSE, 0);

	bcanc = gtk_button_new();
	gtk_signal_connect_object(GTK_OBJECT(bcanc), "released", GTK_SIGNAL_FUNC(upgrade_stop), (gpointer)&info);
	gtk_box_pack_start(GTK_BOX(bhbox), bcanc, TRUE, TRUE, 130);

	blabel = gtk_label_new("Cancel");
	gtk_container_add(GTK_CONTAINER(bcanc), blabel);

	memset(&info, 0, sizeof(info));
	info.winpptr = &winup;
	info.blab = blabel;
	info.adj1 = adj1;
	info.adj2 = adj2;
	info.adj3 = adj3;
	info.table = table;

	gtk_widget_show_all(winup);
	gtk_widget_hide(info.table);

	upgrade_set_status("Performing automatic upgrade...");

	if ( (err = upgrade_rpm_get_files()))
		upgrade_error("Failed to run get current rpms.");

	if ( (err = upgrade_ftp_conn()))
		upgrade_error("conn: ", err);

	gtk_widget_set_sensitive(GTK_WIDGET(mw), FALSE);

	while(winup && !gtk_main_iteration_do(TRUE));

	gtk_widget_set_sensitive(GTK_WIDGET(mw), TRUE);

}

void upgrade_stop(void)
{
	gtk_widget_destroy(*info.winpptr);
	*info.winpptr = 0;

	upgrade_cleanup();
}


void upgrade_message(char *fmt, ...)
{
	char lbuff[MAXBUF];

	va_list ap;

	va_start(ap, fmt);
	vsnprintf(lbuff, MAXBUF, fmt, ap);
	va_end(ap);

	if(!GTK_WIDGET_VISIBLE(ilabel)) {
		gtk_widget_show(ilabel);
		gtk_widget_hide(info.table);
	}

	upgrade_set_status(lbuff);	
	gtk_label_set_text(GTK_LABEL(info.blab), "Okay");
}

void upgrade_error(char *fmt, ...)
{
	char lbuff[MAXBUF];

	va_list ap;

	va_start(ap, fmt);
	vsnprintf(lbuff, MAXBUF, fmt, ap);
	va_end(ap);

	if(info.rtag)
		gdk_input_remove(info.rtag);
	if(info.rpmtag)
		gdk_input_remove(info.rpmtag);
	if(info.erpmtag)
		gdk_input_remove(info.erpmtag);

	upgrade_message("Fatal Error: %s", lbuff);
}

void upgrade_set_status(char *fmt, ...)
{
	char lbuff[MAXBUF];

	va_list ap;

	va_start(ap, fmt);
	vsnprintf(lbuff, MAXBUF, fmt, ap);
	va_end(ap);

	gtk_label_set_text(GTK_LABEL(ilabel), lbuff);

	while(gtk_events_pending())
		gtk_main_iteration();
}

int upgrade_set_timeout(gpointer data)
{
	switch(info.timeoutstat) {
		case 0:
			upgrade_error("Connection to %s timed out...", FTP_SITE);
			break;
		case 1:
			upgrade_error("Logging in timed out...");
			break;
		case 2:
			upgrade_error("Passive mode attempt timed out...");
			break;
		case 3:
			upgrade_error("Connection in passive mode timed out...");
			break;
		case 4:
			upgrade_error("Reading from passive connection timed out...");
			break;
		case 5:
			upgrade_error("Closing timed out...");
	}

	return FALSE;
}

char *upgrade_ftp_conn(void)
{
	int ftpfd;
	static struct sockaddr_in ftpaddr;
	struct hostent *hptr;

	upgrade_set_status("Looking up %s...", FTP_SITE);

	if ( !(hptr = gethostbyname(FTP_SITE)))
		return (char *)hstrerror(h_errno);

	if ( (info.ftpfd = ftpfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return strerror(errno);

	if(fcntl(ftpfd, F_SETFL, O_NONBLOCK))
		return strerror(errno);

	bzero(&ftpaddr, sizeof(ftpaddr));

	ftpaddr.sin_family = AF_INET;
	ftpaddr.sin_port = htons(FTP_PORT);

	memcpy(&ftpaddr.sin_addr, *hptr->h_addr_list, hptr->h_length);
	info.servip = ftpaddr.sin_addr.s_addr;

	upgrade_set_status("Connecting to %s...", FTP_SITE);

	if (connect(ftpfd, (struct sockaddr *)&ftpaddr, sizeof(ftpaddr)) < 0)
		if(errno != EINPROGRESS)
			return strerror(errno);

	info.rtag = gdk_input_add(ftpfd, GDK_INPUT_READ, upgrade_ftp_read, 0);
	info.wtag = gdk_input_add(ftpfd, GDK_INPUT_WRITE, upgrade_ftp_read, 0);
	info.ttag = gtk_timeout_add(FTP_CONNECT_TIMEOUT*1000, upgrade_set_timeout, 0);

	return 0;
}

char *upgrade_ftp_conn_pasv( short pasvport)
{
	struct sockaddr_in pasvaddr;
	int pasvfd;

	if ( (pasvfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return strerror(errno);

	if(fcntl(pasvfd, F_SETFL, O_NONBLOCK))
		return strerror(errno);

	bzero(&pasvaddr, sizeof(pasvport));

	pasvaddr.sin_family = AF_INET;
	pasvaddr.sin_port = pasvport;
	pasvaddr.sin_addr.s_addr = info.servip;

	if(connect(pasvfd, (struct sockaddr *)&pasvaddr, sizeof(pasvaddr)) < 0)
		if(errno != EINPROGRESS)
			return strerror(errno);

	info.prtag = gdk_input_add(pasvfd, GDK_INPUT_READ, upgrade_ftp_read, (gpointer)1);
	info.pwtag = gdk_input_add(pasvfd, GDK_INPUT_WRITE, upgrade_ftp_read, (gpointer)1);

	return 0;
}

void upgrade_ftp_read(gpointer data, gint ftpfd, GdkInputCondition cond)
{
	char dbuff[1460], pbuff[1460];
	int ret;
	unsigned int retlen;
	static char *loc;
	int i = 0;
	loc = pbuff;

	if(data && !info.pconn) {
		info.pconn++;
		retlen = sizeof(ret);
		getsockopt(ftpfd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
		if(ret) {
			if(info.prtag)
				gdk_input_remove(info.prtag);
			if(info.pwtag)
				gdk_input_remove(info.pwtag);
			close(ftpfd);
			upgrade_error(strerror(ret));
			return;
		} else if(info.pwtag) {
				gdk_input_remove(info.pwtag);
			info.timeoutstat = 4;
			return;
		}
	}

	if(!info.conn) {
		info.conn++;
		retlen = sizeof(ret);
		getsockopt(ftpfd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
		if(ret) {
			if(info.rtag)
				gdk_input_remove(info.rtag);
			if(info.wtag)
				gdk_input_remove(info.wtag);
			close(ftpfd);
			upgrade_error(strerror(ret));
			return;
		} else {
			if(info.wtag)
				gdk_input_remove(info.wtag);
			info.timeoutstat = 1;
			upgrade_set_status("Connection established...");
			return;
		}
	}

	gtk_timeout_remove(info.ttag);
	info.ttag = gtk_timeout_add(FTP_CONNECT_TIMEOUT*1000, upgrade_set_timeout, 0);

	bzero(dbuff, sizeof(dbuff));
	bzero(pbuff, sizeof(pbuff));

	while ( (read(ftpfd, dbuff + i, 1) > 0) && (dbuff[i++] != '\n'))
		if (i == sizeof(dbuff))
			i--;

	if (i)
		dbuff[--i] = 0;
	

	if (upgrade_ftp_parse(ftpfd, dbuff, strlen(dbuff)))
		return;	

	if (i == 0)
		ret = 0;
	else
		ret = 1;

	if(!ret) {
		close(ftpfd);
		if(info.ttag)
			gtk_timeout_remove(info.ttag);
		if(info.rtag);

		if(data)
			gdk_input_remove(info.prtag);
		else if(info.timeoutstat < 5)
			upgrade_error("Connection closed prematurely.");
		else {
			gtk_input_remove(info.rtag);
			upgrade_file(0);
		}
	} else if(ret < 0) {
		if(errno == EAGAIN)
			return;
		perror("read");
	}
}

char *upgrade_ftp_write(int ftpfd, char *buff)
{
	int ret;

	if ( (ret = write(ftpfd, buff, strlen(buff))) < 0)
		return strerror(errno);

	return 0;
}

int upgrade_ftp_parse(int ftpfd, char *rbuff, int size)
{
	static char *argv[MAXBUF];
	static char buff[1460];
	char *parse = buff;
	int argc = 0, t = 0;
	int prefix;
	static short pasvport;
	char *err;

	bzero(buff, sizeof(buff));
	memcpy(buff, rbuff, size);

#ifdef DEBUG
	fprintf(stdout, "%s\n", buff);
#endif

	/* Don't mess with anything that doesn't exist...*/
	if(!*parse)
		return 0;

	bzero(argv, sizeof(argv));
	while(*parse) {
		if((*parse < 33) /*|| (*parse > 128)*/) {
			*parse = 0, t++;
			if(t > MAXBUF) {
				upgrade_error("Argument on FTP server is too long.");
				return -1;
			}
		} else if(t || !argc) {
			if(argc == MAXARGS) {
				upgrade_error("Argument count on FTP server is too high.");
				return -1;
			}
			argv[argc++] = parse;
			t = 0;
		}

		parse++;
	}

	if(!argc)
		return 0;

	if ( !(prefix = atoi((const char*)argv))) {
		if(info.timeoutstat == 4 && argv[8])
			upgrade_file(argv[8]);
		else return 0;

	}

	if(prefix == 220) {
		upgrade_ftp_write(ftpfd, "user ftp\r\n");
	} else if(prefix == 331) {
		upgrade_ftp_write(ftpfd, "pass upgrade@linux-support.net\r\n");
	} else if(prefix == 230) {
		info.timeoutstat = 2;
		upgrade_ftp_write(ftpfd, "PASV\r\n");
	} else if(prefix == 227) {
		 char ptrs[2] = { 0,0 };
		char *pos = argv[4];
		int nc = 5;

		if(argc < 5)
			return 0;
		while(*pos) {
			if(*pos == ',') {
				*pos = 0;
				nc--;
			} else if(nc == 1 && !ptrs[0])
					ptrs[0] = atoi(pos);
			else if(nc == 0 && !ptrs[1])
					ptrs[1] = atoi(pos);
			else if(*pos == ')')
				*pos = 0;

			pos++;
		}

		memcpy(&pasvport, &ptrs, 2);
		info.timeoutstat = 3;

		upgrade_ftp_write(ftpfd, FTP_GET_SEQ);
		if ( (err = upgrade_ftp_conn_pasv(pasvport))) {
			upgrade_error("passive connection: %s", err);
			return -1;
		}
	} else if(prefix == 226) {
		if(argv[0][3] != '-') {
			upgrade_ftp_write(ftpfd, "quit\r\n");
			info.timeoutstat = 5;
		}
	} else if(prefix >= 500) {
		upgrade_error("ftp: %s", rbuff);
		return -1;
	}

	return 0;
}

int upgrade_file_valid(char *file)
{
	const char **pptr;
	int num;

	for(pptr = files, num = 0; *pptr; pptr++, num++)
		if ( (strstr(file, *pptr)) == file)
			return num;

	return -1;
}

void upgrade_file( char *file)
{
	char lbuff[MAXBUF], buff[MAXBUF];
	int num;

	if(!file) {
		int c = 0;
		memset(lbuff, 0, MAXBUF);
		for(num = 0; num < NUMFILES; num++) {
			if(!info.highestlocal[num] && info.highestnetwork[num]) {
				snprintf(buff, MAXBUF, "ftp://%s/pub/gnophone/%s.i386.rpm ", FTP_SITE, info.highestnetwork[num]);
				strncat(lbuff, buff, sizeof(lbuff)-strlen(lbuff+1));
				c++;
			} else if(info.highestlocal[num] && info.highestnetwork[num]) {
				if(upgrade_compare_files(info.highestlocal[num], info.highestnetwork[num])) {
					snprintf(buff, MAXBUF, "ftp://%s/pub/gnophone/%s.i386.rpm ", FTP_SITE, info.highestnetwork[num]);
					strncat(lbuff, buff, sizeof(lbuff)-strlen(lbuff+1));
					c++;
				}
			}
		}

		if(!c)
			upgrade_message("Nothing on FTP site to be upgraded.");
		else {
			lbuff[strlen(lbuff)-1] = 0;
			if(upgrade_rpm_download_files(lbuff)) {
				upgrade_error("Failed to download files.");
				return ;
			}
			upgrade_message("Upgrade completed.");
		}

		return;
	}

	if ( (num = upgrade_file_valid(file)) >= 0) {
		/* Postfix of files... */
		if((strstr(file, ".i386.rpm")) != ((file+strlen(file))-9))
			return;
		*(file+strlen(file)-9) = 0;
		if(!info.highestnetwork[num])
			info.highestnetwork[num] = strdup(file);
		else if(upgrade_compare_files(info.highestnetwork[num], file)) {
			free(info.highestnetwork[num]);
			info.highestnetwork[num] = strdup(file);
		}
	}
}

/*	Assuming gnophone-X.X.X [-X] [-preX].i386.rpm
 *	mozilla-X.X.X [-X] [-preX].i386.rpm
 *	mozilla-devel-X.X.X [-X] [-preX].i386.rpm
 *
 *	Returns 1 if comparing version is greater.
 */

int upgrade_compare_files(const char *compto, const char *compfrom)
{
	float vers[MAXVERSIONARGS+1];
	float vers2[MAXVERSIONARGS+1];
	const char *ptr = compfrom;
	float *nptr = vers;
	int num = 0, nump;
	int pretest;

	while(1) {
		while(*(++ptr)) {
			if ((*(ptr-1) == '.' || *(ptr-1) == '-') 
			&& ((nptr[num] = atoi(ptr)) || *ptr == '0')) {
				if(!num && (*ptr == '0'))
					continue;
				else if(*(ptr-1) == '-')
					num--;
				if(num++ == MAXVERSIONARGS)
					break;
			}

			/* Sub versions prefixed with 'pre' or '-' */
			if(num && (*(ptr-1) == '-' || strstr(ptr-3, "pre") == ptr-3) 
			&& (nptr[num] = atoi(ptr))) {
				/* Forget about subversions of subversions! */
				pretest = nptr[num-1];
				if(nptr[num-1]-pretest)
					continue;

				/* Add fraction of subversion to previous */
				while(nptr[num] >= 1)
					nptr[num] /= 10;
				nptr[num-1] += nptr[num];
			}
		}

		if(nptr == vers2)
			break;

		ptr = compto;
		nptr = vers2;
		nump = num;
		num = 0;
	}

	if(nump > num)
		return 1;
	else if(nump == num) {
		for(num = 0; num < nump; num++)
			if(vers[num] > vers2[num])
				return 1;
			else if(vers[num] < vers2[num])
				break;
	}

	return 0;
}

char *upgrade_rpm_get_files(void)
{
	int stat;
	char *rpm_list[] = {
		"/bin/rpm",
		"-qa",
		0
	};
	char *err;

	if ( (err = upgrade_run_rpm(rpm_list)))
		return err;

	info.rpmtag = gdk_input_add(info.pty, GDK_INPUT_READ, upgrade_rpm_read, 0);

	while(waitpid(-1, &stat, WNOHANG) <= 0 && !gtk_main_iteration_do(TRUE));
	close(info.tty), close(info.pty);
	close(info.etty), close(info.epty);
	gdk_input_remove(info.rpmtag);
	gdk_input_remove(info.erpmtag);

	return 0;
}

char *upgrade_rpm_download_files( char *files)
{
	int stat;
	char *rpm_get[] = {
		"/bin/rpm",
		"-Uvh",
		"--force",
		"--nodeps",
		files,
		0
	};
	char *err;

#ifdef DEBUG
	fprintf(stderr, "Trying <%s>\n", files);
#endif

	if ( (err = upgrade_run_rpm(rpm_get)))
		return err;

	gtk_widget_hide(ilabel);
	gtk_widget_show(info.table);

	info.rpmtag = gdk_input_add(info.pty, GDK_INPUT_READ, upgrade_rpm_read, (gpointer)1);

	while(waitpid(-1, &stat, WNOHANG) <= 0 && !gtk_main_iteration_do(TRUE));
	close(info.tty), close(info.pty);
	close(info.etty), close(info.epty);
	gdk_input_remove(info.rpmtag);
	gdk_input_remove(info.erpmtag);

	return 0;
}

char *upgrade_run_rpm(char **args)
{
	int childpid;

	if(upgrade_open_a_pty(&info.pty, &info.tty) < 0)
		return "Unable to open pty";

	if(upgrade_open_a_pty(&info.epty, &info.etty) < 0)
		return "Unable to open pty";

	if(fcntl(info.pty, F_SETFL, O_NONBLOCK))
		return strerror(errno);

	if(fcntl(info.tty, F_SETFL, O_NONBLOCK))
		return strerror(errno);

	if(fcntl(info.epty, F_SETFL, O_NONBLOCK))
		return strerror(errno);

	if(fcntl(info.etty, F_SETFL, O_NONBLOCK))
		return strerror(errno);

	if ( !(childpid = fork())) {
		int std_err;

		std_err = dup(2);
		dup2(info.tty, 1);
		dup2(info.etty, 2);

		execv(args[0], args);

		dup2(std_err, 2);

		perror("execlp");
		exit(1);
	}

	if ( !(info.stream = fdopen(info.pty, "r")))
		return strerror(errno);

	if ( !(info.estream = fdopen(info.epty, "r")))
		return strerror(errno);

	info.erpmtag = gdk_input_add(info.epty, GDK_INPUT_READ, upgrade_rpm_read, (gpointer)3);
	return 0;
}

void upgrade_rpm_read(gpointer data, gint rpmfd, GdkInputCondition cond)
{
	char lbuff[MAXBUF];
	char *ret;
	int num;

	if(data == (gpointer)3) {
		while ( (ret = fgets(lbuff, MAXBUF, info.estream))) {
			if(strstr(lbuff, "warning:") || strstr(lbuff, "removing")
			|| strstr(lbuff, "LOOP:") || strstr(lbuff, "Requires"))
				continue;

			gdk_input_remove(info.erpmtag);
			upgrade_error("RPM returned an error...");
		}
		return;
	}

	while ( ( ret = fgets(lbuff, MAXBUF, info.stream))) {
		if(!data) {
			if ( (num = upgrade_file_valid(lbuff)) >= 0) {
				lbuff[strlen(lbuff)-1] = 0;
				if(!info.highestlocal[num])
					info.highestlocal[num] = strdup(lbuff);
				else if(upgrade_compare_files(info.highestlocal[num], lbuff)) {
					free(info.highestlocal[num]);
					info.highestlocal[num] = strdup(lbuff);
				}
			}
		} else if(data == (gpointer)1) {
			char *str;
			int r;
			static GtkObject *current;

			if(strstr(lbuff, "Retrieving"))
				continue;
			else if(strstr(lbuff, "Preparing"))
				current = info.adj1;

			for(str = lbuff; *str; str++) {
				if(*str == '(') {
					if(current == info.adj3)
						current = info.adj2;
				} else if(*str == '[') {
					if(current == info.adj1) {
						current = info.adj2;
						continue;
					} if(current == info.adj2)
						current = info.adj3;
				} else continue;

				*(str+4) = 0;
				while(*(++str) == ' ');
				r = atoi(str);
				gtk_adjustment_set_value(GTK_ADJUSTMENT(current), r);
				break;
			}
		}
	}
}

int upgrade_open_a_pty(int *pty, int *tty)
{
	char line[ 12 ];
	char *p1, *p2;
	char *cp;
	int i;

	sprintf( line, "/dev/ptyXX" );
	p1 = &line[ 8 ];
	p2 = &line[ 9 ];

	for ( cp = "pqrstuvwxyzPQRST"; *cp; cp++ ) {
		struct stat stb;

		*p1 = *cp;
		*p2 = '0';
		/*
		 * This stat() check is just to keep us from
		 * looping through all 256 combinations if there
		 * aren't that many ptys available.
		 */
		if ( stat( line, &stb ) < 0 )
			break;
		for ( i = 0; i < 16; i++ ) {
			*p2 = "0123456789abcdef"[ i ];
			*pty = open( line, O_RDWR );
			if ( *pty > 0 ) {
				line[ 5 ] = 't';
				/* Now open appropriate tty */
				if ( ( *tty = open( line, O_RDWR ) ) < 0 ) {
					line[ 5 ] = 'p';
					close( *pty );
					continue;
				}
				return 0;
			}
		}
	}
	return - 1;
}

void upgrade_cleanup(void)
{
	int num;

	if(info.rtag)
		gdk_input_remove(info.rtag);
	if(info.wtag)
		gdk_input_remove(info.wtag);
	if(info.prtag)
		gdk_input_remove(info.prtag);
	if(info.pwtag)
		gdk_input_remove(info.pwtag);
	if(info.ttag)
		gtk_timeout_remove(info.ttag);
	if(info.rpmtag)
		gtk_input_remove(info.rpmtag);
	if(info.erpmtag)
		gtk_input_remove(info.erpmtag);

	if(info.ftpfd)
		close(info.ftpfd);

	for(num = 0; num < NUMFILES; num++) { 
		if(info.highestlocal[num])
			free(info.highestlocal[num]);
		if(info.highestnetwork[num])
			free(info.highestnetwork[num]);
	}

	memset(&info, 0, sizeof(info));
}
