/*****************************************************************************/
/*  gftp.c - the main user interface for the ftp program                     */
/*  Copyright (C) 1998 Brian Masney <masneyb@newwave.net>                    */
/*                                                                           */
/*  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., 675 Mass Ave, Cambridge, MA 02139, USA.                */
/*****************************************************************************/

#include "ftp.h"

/* This are the pixmaps and the masks for some of the built in icons */
GdkPixmap *dotdot_pixmap, *dir_pixmap, *linkdir_pixmap, *linkfile_pixmap,
   *exe_pixmap, *doc_pixmap, *up_pixmap, *down_pixmap;
GdkBitmap *dotdot_mask, *dir_mask, *linkdir_mask, *linkfile_mask, 
   *exe_mask, *doc_mask, *up_mask, *down_mask;

struct pix_ext *registered_exts; /* Registered file extensions */
char emailaddr[MAXSTR] = ""; /* What to send when we log in as anonymous */
char version[] = "gFTP 0.2"; /* Version string */
struct conn_categories *hostcat; /* Our host categories/sites for the connection
                                    manager */

struct ftp_transfer_data *file_transfers = NULL; /* All of our transfers */
pthread_mutex_t transfer_mutex = PTHREAD_MUTEX_INITIALIZER; /* Mutex for 
                                    protecting the file_transfers struct */

struct ftp_log_queue *file_transfer_logs = NULL; /* Waiting log entries from
                                    the threads */
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; /* Mutex for protecting
                                    the file_transfer_logs struct */

GtkWidget *logwdw, /* Logging text widget */
          *dlwdw; /* Download list box */
/*****************************************************************************/
int main(int argc, char *argv[]) {
   struct ftp_window_data wdata1, wdata2;
   struct ftp_both_windows windows;
   GtkWidget *window, *ui;

   windows.window1 = &wdata1;
   windows.window2 = &wdata2;
   gtk_init(&argc, &argv);
   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(exitCB), NULL);
   gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(exitCB), NULL);
   gtk_window_set_title(GTK_WINDOW(window), version);
   
   ui = CreateFTPWindows(window, &windows);
   gtk_container_add(GTK_CONTAINER(window), ui);
   gtk_widget_show(window);

   ftp_log(LOG_MISC, "%s, Copyright (C) 1998 Brian Masney <", version);
   ftp_log(LOG_RECV, "masneyb@newwave.net");
   ftp_log(LOG_MISC, ">\nIf you have any questions, comments, or suggestions about this program, please\nfeel free to email them to me.\n");
   ftp_log(LOG_MISC, "gFTP comes with ABSOLUTELY NO WARRANTY; for details, see the COPYING file\n");
   ftp_log(LOG_MISC, "This is free software, and you are welcome to redistribute it under certain conditions;\nfor details, see the COPYING file\n");
                
   init_gftp(argc, argv, &windows, window);
   add_local_files(&wdata1); 
   gtk_timeout_add(1000, update_downloads, (gpointer) &windows);

   gtk_main();
   return(0);
}  
/*****************************************************************************/
void init_gftp(int argc, char *argv[], struct ftp_both_windows *windows, GtkWidget *parent) {
   struct pix_ext *tempext;
   char tempstr[MAXSTR], temp1str[MAXSTR], *pos, *endpos;
   GtkWidget *sort_wid;
   GtkStyle *style;
   int len;

   windows->window1->local = windows->window2->local = -1;
   read_config_file();
   if(strcmp(emailaddr, "") == 0) {
      printf("Error in config file: No email address specified\n");
      printf("Please add a email=<your email address> to the config file\n");
      exit(0);
   }
                        
   style = gtk_widget_get_style(parent);

   expand_path(BASE_CONF_DIR "/dotdot.xpm", tempstr, sizeof(tempstr));
   dotdot_pixmap = gdk_pixmap_create_from_xpm(parent->window, &dotdot_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(dotdot_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/dir.xpm", tempstr, sizeof(tempstr));
   dir_pixmap = gdk_pixmap_create_from_xpm(parent->window, &dir_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(dir_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/linkdir.xpm", tempstr, sizeof(tempstr));
   linkdir_pixmap = gdk_pixmap_create_from_xpm(parent->window, &linkdir_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(linkdir_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/linkfile.xpm", tempstr, sizeof(tempstr));
   linkfile_pixmap = gdk_pixmap_create_from_xpm(parent->window, &linkfile_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(linkfile_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/exe.xpm", tempstr, sizeof(tempstr));
   exe_pixmap = gdk_pixmap_create_from_xpm(parent->window, &exe_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(exe_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/doc.xpm", tempstr, sizeof(tempstr));
   doc_pixmap = gdk_pixmap_create_from_xpm(parent->window, &doc_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(doc_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/up.xpm", tempstr, sizeof(tempstr));
   up_pixmap = gdk_pixmap_create_from_xpm(parent->window, &up_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(up_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }

   expand_path(BASE_CONF_DIR "/down.xpm", tempstr, sizeof(tempstr));
   down_pixmap = gdk_pixmap_create_from_xpm(parent->window, &down_mask, &style->bg[GTK_STATE_NORMAL], tempstr);
   if(down_pixmap == NULL) {
      printf("Error: Cannot file %s\n", tempstr);
      exit(0);
   }
   
   if(windows->window1->sortasds) sort_wid = gtk_pixmap_new(down_pixmap, down_mask);
   else sort_wid = gtk_pixmap_new(up_pixmap, up_mask);
   gtk_widget_show(sort_wid);
   gtk_clist_set_column_widget(GTK_CLIST(windows->window1->listbox), 0, sort_wid);

   if(windows->window2->sortasds) sort_wid = gtk_pixmap_new(down_pixmap, down_mask);
   else sort_wid = gtk_pixmap_new(up_pixmap, up_mask);
   gtk_widget_show(sort_wid);
   gtk_clist_set_column_widget(GTK_CLIST(windows->window2->listbox), 0, sort_wid);
   

   tempext = registered_exts;
   while(tempext != NULL) {
      snprintf(tempstr, sizeof(tempstr), "%s/%s", BASE_CONF_DIR, tempext->filename);
      tempstr[sizeof(tempstr)-1] = '\0';
      expand_path(tempstr, temp1str, sizeof(temp1str));
      tempext->pixmap = gdk_pixmap_create_from_xpm(parent->window, &tempext->mask, &style->bg[GTK_STATE_NORMAL], temp1str);
      if(tempext->pixmap == NULL) {
          printf("Error: Could not read XPM file %s\n", tempext->filename);
          exit(0);
      }
      tempext = tempext->next;
   }
   if(argc > 2) usage();
   else if(argc == 2) {
      windows->window2->host->port = 0;
      windows->window2->host->host[0] = windows->window2->host->dir[0] = '\0';
      strncpy(windows->window2->host->user, ANON_LOGIN, sizeof(windows->window2->host->user));
      strncpy(windows->window2->host->passwd, emailaddr, sizeof(windows->window2->host->passwd));
      pos = argv[1];
      if(strncmp(pos, "ftp://", 6) == 0) pos += 6;
      if(strchr(pos, '@') != NULL) {
         /* A user/password was entered */
         if((endpos = strchr(pos, ':')) == NULL) usage();
         len = endpos - pos + 1 > sizeof(windows->window2->host->user) ? sizeof(windows->window2->host->user) : endpos - pos + 1;
         strncpy(windows->window2->host->user, pos, len);
         windows->window2->host->user[len-1] = 0;

         pos = endpos+1;
         if((endpos = strchr(pos, '@')) == NULL) usage();
         if(strchr(endpos+1, '@') != NULL) endpos = strchr(endpos+1, '@');
         len = endpos - pos + 1 > sizeof(windows->window2->host->passwd) ? sizeof(windows->window2->host->passwd) : endpos - pos + 1;
         strncpy(windows->window2->host->passwd, pos, len);
         windows->window2->host->passwd[len-1] = 0;
         pos = endpos+1;
      }
      /* Now get the hostname and an optional port and optional directory */
      if((endpos = strchr(pos, ':')) != NULL) {
         len = endpos - pos + 1 > sizeof(windows->window2->host->host) ? sizeof(windows->window2->host->host) : endpos - pos + 1;
      }
      else if((endpos = strchr(pos, '/')) != NULL) {
         len = endpos - pos + 1 > sizeof(windows->window2->host->host) ? sizeof(windows->window2->host->host) : endpos - pos + 1;
      }
      else len = sizeof(windows->window2->host->host);
      strncpy(windows->window2->host->host, pos, len);
      windows->window2->host->host[len-1] = 0;
      if((endpos = strchr(pos, ':')) != NULL) {
         /* A port was entered */
         pos = endpos + 1;
         if((endpos = strchr(pos, '/')) != NULL) {
            len = endpos - pos + 1 > sizeof(tempstr) ? sizeof(tempstr) : endpos - pos + 1;
         }
         snprintf(tempstr, len, pos);
         tempstr[len-1] = 0;
         windows->window2->host->port = strtol(pos, (char **) NULL, 10);
      }
      if((endpos = strchr(pos, '/')) != NULL) {
         strncpy(windows->window2->host->dir, endpos, sizeof(windows->window2->host->dir));
      }
      fix_display();
      if(ftp_connect(windows->window2, windows->window2->host, NULL)) {
         ftp_list_files(windows->window2, windows->window2->host);
      }
   }
}
/*****************************************************************************/
void usage(void) {
   printf("usage: gftp [[ftp://][user:pass@]ftp-site[:port][/directory]]\n");
   exit(0);
}
/*****************************************************************************/
GtkWidget *CreateFTPWindows(GtkWidget *ui, struct ftp_both_windows *windows) {
   GtkWidget *win1frame, *win2frame, *putbutton, *retrbutton, *box, *dlbox;
   GtkWidget *winpane, *dlpane, *logpane, *vscrollbar, *logtable;
   GtkWidget *mainvbox, *scroll_list, *asciimenu, *binarymenu;
   GtkItemFactory *factory;
   GtkAccelGroup *accel_group;
   char *dltitles[4] = {"Filename", "Progress", "Hostname"};
   GtkItemFactoryEntry menu_items[] = {
      {"/_FTP",			NULL,	0,		0,	"<Branch>"},
      {"/FTP/tearoff",		NULL,	0,		0,	"<Tearoff>"},
      {"/FTP/Ascii", 		NULL, 	change_ascii,	0,	"<RadioItem>"},
      {"/FTP/Binary", 		NULL, 	change_binary,	0,	"<RadioItem>"},
      {"/FTP/sep", 		NULL, 	0, 		0,	"<Separator>"},
      {"/FTP/_Quit", 		"<control>Q", 	exitCB, 	0},
      {"/_Local",		NULL,	0,		0,	"<Branch>"},
      {"/Local/tearoff",	NULL,	0,		0,	"<Tearoff>"},
      {"/Local/Select All", 	NULL, 	selectall, 	0},
      {"/Local/Deselect All", 	NULL, 	deselectall, 	0},
      {"/Local/sep",		NULL,	0,		0,	"<Separator>"},
      {"/Local/Change Directory", NULL,	chdirfunc, 	0},
      {"/Local/Make Directory...", NULL, mkdir_dialog, 	0},
      {"/Local/Rename...", 	NULL, 	rename_dialog, 	0},
      {"/Local/Delete...", 	NULL, 	delete_dialog, 	0},
      {"/Local/Refresh", 	NULL, 	refresh, 	0},
      {"/_Remote",		NULL,	0,		0,	"<Branch>"},
      {"/Remote/tearoff",	NULL,	0,		0,	"<Tearoff>"},
      {"/Remote/_Connect...", 	"<control>C", 	connect_dialog, 0},
      {"/Remote/_Disconnect", 	"<control>D", 	disconnect, 	0},
      {"/Remote/sep",		NULL,	0,		0,	"<Separator>"},
      {"/Remote/Select All", 	NULL, 	selectall, 	0},
      {"/Remote/Deselect All", 	NULL, 	deselectall, 	0},
      {"/Remote/sep",		NULL,	0,		0,	"<Separator>"},
      {"/Remote/Change Directory", NULL, chdirfunc, 	0},
      {"/Remote/Make Directory...", NULL, mkdir_dialog,	0},
      {"/Remote/Rename...", 	NULL, 	rename_dialog, 	0},
      {"/Remote/Delete...", 	NULL, 	delete_dialog, 	0},
      {"/Remote/Refresh", 	NULL, 	refresh, 	0},
      {"/_Transfers",		NULL,	0,		0,	"<Branch>"},
      {"/Transfers/tearoff",	NULL,	0,		0,	"<Tearoff>"},
      {"/Transfers/Stop Transfer", NULL, stop_transfer,	0},
      {"/L_ogging",		NULL,	0,		0,	"<Branch>"},
      {"/Logging/tearoff",	NULL,	0,		0,	"<Tearoff>"},
      {"/Logging/Clear", 	NULL, 	clearlog, 	0},
      {"/Logging/Save log...", 	NULL, 	savelog, 	0}};
   int num_menu_items = sizeof(menu_items) / sizeof(menu_items[0]), i;
   
   /* FIXME */
   menu_items[2].callback_action = (int) windows->window2;
   menu_items[3].callback_action = (int) windows->window2;
   menu_items[5].callback_action = (int) windows;
   for(i=8; i<16; i++) menu_items[i].callback_action = (int) windows->window1;
   for(i=18; i<36; i++) menu_items[i].callback_action = (int) windows->window2;
      
   mainvbox = gtk_vbox_new(FALSE, 0);
   gtk_widget_show(mainvbox);

   accel_group = gtk_accel_group_new();
   factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group);
   gtk_item_factory_create_items(factory, num_menu_items, menu_items, NULL);
   gtk_accel_group_attach(accel_group, GTK_OBJECT(ui));
   gtk_box_pack_start(GTK_BOX(mainvbox), factory->widget, FALSE, FALSE, FALSE);
   gtk_widget_show(factory->widget);

   asciimenu = gtk_item_factory_get_widget(factory, menu_items[2].path);
   binarymenu = gtk_item_factory_get_widget(factory, menu_items[3].path);

   gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(asciimenu), gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(binarymenu)));

   gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(asciimenu), FALSE);
   gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(binarymenu), TRUE);

   winpane = gtk_hpaned_new();
   box = gtk_hbox_new(FALSE, 0);
   win1frame = CreateFTPWindow(win1frame, windows->window1);
   gtk_box_pack_start(GTK_BOX(box), win1frame, TRUE, TRUE, TRUE);
   gtk_widget_show(win1frame);

   dlbox = gtk_vbox_new(FALSE, 0);
   gtk_container_border_width(GTK_CONTAINER(dlbox), 5);

   putbutton = gtk_button_new_with_label("->");
   gtk_box_pack_start(GTK_BOX(dlbox), putbutton, TRUE, FALSE, FALSE);
   gtk_signal_connect(GTK_OBJECT(putbutton), "clicked", GTK_SIGNAL_FUNC(putCB), (gpointer) windows);
   gtk_widget_show(putbutton);

   retrbutton = gtk_button_new_with_label("<-");
   gtk_box_pack_start(GTK_BOX(dlbox), retrbutton, TRUE, FALSE, FALSE);
   gtk_signal_connect(GTK_OBJECT(retrbutton), "clicked", GTK_SIGNAL_FUNC(retrCB), (gpointer) windows);
   gtk_widget_show(retrbutton);

   gtk_box_pack_start(GTK_BOX(box), dlbox, FALSE, FALSE, FALSE);
   gtk_widget_show(dlbox);
   gtk_widget_show(box);
   gtk_paned_add1(GTK_PANED(winpane), box);
   
   win2frame = CreateFTPWindow(win2frame, windows->window2);
   gtk_paned_add2(GTK_PANED(winpane), win2frame);
   gtk_widget_show(win2frame);
   gtk_widget_show(winpane);

   dlpane = gtk_vpaned_new();
   gtk_paned_add1(GTK_PANED(dlpane), winpane);

   scroll_list = gtk_scrolled_window_new(NULL, NULL);
   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_list),
      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   dlwdw = gtk_clist_new_with_titles(3, dltitles);
   gtk_container_add(GTK_CONTAINER(scroll_list), dlwdw);
   gtk_widget_set_usize(dlwdw, 1, 50);
   gtk_clist_set_column_width(GTK_CLIST(dlwdw), 0, 100);
   gtk_clist_set_column_width(GTK_CLIST(dlwdw), 1, 250);
   gtk_clist_set_column_width(GTK_CLIST(dlwdw), 2, 100);
   gtk_container_border_width(GTK_CONTAINER(dlwdw), 5);
   gtk_signal_connect(GTK_OBJECT(dlwdw), "select_row", GTK_SIGNAL_FUNC(selectdl), NULL);
   gtk_signal_connect(GTK_OBJECT(dlwdw), "unselect_row", GTK_SIGNAL_FUNC(unselectdl), NULL);
   gtk_paned_add2(GTK_PANED(dlpane), scroll_list);
   gtk_widget_show(dlwdw);
   gtk_widget_show(scroll_list);
   gtk_widget_show(dlpane);

   logpane = gtk_vpaned_new();
   gtk_paned_add1(GTK_PANED(logpane), dlpane);

   logtable = gtk_table_new(1, 2, FALSE);
   logwdw = gtk_text_new(NULL, NULL);
   gtk_widget_set_usize(logwdw, 1, 105);
   gtk_text_set_editable(GTK_TEXT(logwdw), FALSE);
   gtk_table_attach(GTK_TABLE(logtable), logwdw, 0, 1, 0, 1,
      GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
   gtk_widget_show(logwdw);

   vscrollbar = gtk_vscrollbar_new (GTK_TEXT(logwdw)->vadj);
   gtk_table_attach(GTK_TABLE(logtable), vscrollbar, 1, 2, 0, 1,
      GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_widget_show(vscrollbar);
   gtk_widget_show(logtable);
   
   gtk_paned_add2(GTK_PANED(logpane), logtable);
   gtk_widget_show(logpane);      
   gtk_box_pack_start(GTK_BOX(mainvbox), logpane, TRUE, TRUE, TRUE);

   return(mainvbox);
}   
/*****************************************************************************/
GtkWidget *CreateFTPWindow(GtkWidget *parent, struct ftp_window_data *wdata) {
   char *titles[7] = {"", "Filename", "Size", "User", "Group", "Date", "Attribs"};
   GtkWidget *box, *scroll_list;

   wdata->host = mymalloc(sizeof(struct ftp_host_data));
   wdata->host->files = wdata->host->last = NULL;
   wdata->host->type = FTP_BINARY;
   wdata->sortcol = 1;
   wdata->sortasds = 1;
   wdata->totalitems = wdata->numselected = 0;
   wdata->items = NULL;

   parent = gtk_frame_new(NULL);
   gtk_container_border_width(GTK_CONTAINER(parent), 5);
   gtk_widget_set_usize(parent, 255, 275);
   
   box = gtk_vbox_new(FALSE, 0);
   gtk_container_border_width(GTK_CONTAINER(box), 5);
   gtk_container_add(GTK_CONTAINER(parent), box);
   
   wdata->diredit = gtk_entry_new_with_max_length(MAXSTR);
   gtk_box_pack_start(GTK_BOX(box), wdata->diredit, FALSE, FALSE, FALSE);
   gtk_signal_connect(GTK_OBJECT(wdata->diredit), "activate", GTK_SIGNAL_FUNC(chdiredit), (gpointer) wdata);
   gtk_widget_show(wdata->diredit);
   
   wdata->hoststxt = gtk_label_new("Not connected");
   gtk_misc_set_alignment(GTK_MISC(wdata->hoststxt), 0, 0);
   gtk_box_pack_start(GTK_BOX(box), wdata->hoststxt, FALSE, FALSE, FALSE);
   gtk_widget_show(wdata->hoststxt);

   scroll_list = gtk_scrolled_window_new(NULL, NULL);
   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_list),
      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   wdata->listbox = gtk_clist_new_with_titles(7, titles);
   gtk_container_add(GTK_CONTAINER(scroll_list), wdata->listbox);
   gtk_clist_set_selection_mode(GTK_CLIST(wdata->listbox), GTK_SELECTION_EXTENDED);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 0, 16);
   gtk_clist_set_column_justification(GTK_CLIST(wdata->listbox), 0, GTK_JUSTIFY_CENTER);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 1, 100);
   gtk_clist_set_column_justification(GTK_CLIST(wdata->listbox), 2, GTK_JUSTIFY_RIGHT);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 2, 85);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 3, 65);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 4, 65);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 5, 85);
   gtk_clist_set_column_width(GTK_CLIST(wdata->listbox), 6, 65);
   gtk_box_pack_start(GTK_BOX(box), scroll_list, TRUE, TRUE, TRUE);
   gtk_signal_connect(GTK_OBJECT(wdata->listbox), "click_column", GTK_SIGNAL_FUNC(sortrows), (gpointer) wdata);
   gtk_signal_connect(GTK_OBJECT(wdata->listbox), "select_row", GTK_SIGNAL_FUNC(selectrow), (gpointer) wdata);
   gtk_signal_connect(GTK_OBJECT(wdata->listbox), "unselect_row", GTK_SIGNAL_FUNC(unselectrow), (gpointer) wdata);
   gtk_signal_connect(GTK_OBJECT(wdata->listbox), "button_press_event", GTK_SIGNAL_FUNC(list_dblclick), (gpointer) wdata); 
   gtk_widget_show(wdata->listbox);
   gtk_widget_show(scroll_list);

   gtk_widget_show(box);
   gtk_widget_show(parent);

   return(parent);
}
/*****************************************************************************/
void sortrows(GtkCList *clist, gint column, gpointer data) {
   struct ftp_file_data *newfle, *tempfle, *prevfle, *curfle;
   struct ftp_window_data *wdata;
   GtkWidget *sort_wid;

   if(column == 5) { /* FIXME */
      ftp_log(LOG_MISC, "Sort: Sorting the date is not implemented yet\n");
      return;
   }
   wdata = (struct ftp_window_data *) data;
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Sort: Not connected to a remote site\n");
      return;
   }
   newfle = wdata->host->files;
   if(column == 0) {
      wdata->sortasds = !wdata->sortasds;
      column = wdata->sortcol;
      if(wdata->sortasds) sort_wid = gtk_pixmap_new(down_pixmap, down_mask);
      else sort_wid = gtk_pixmap_new(up_pixmap, up_mask);
      gtk_widget_show(sort_wid);
      gtk_clist_set_column_widget(clist, 0, sort_wid);
   }
   else wdata->sortcol = column;
   wdata->host->files = wdata->host->files->next;
   newfle->next = NULL;
   while(wdata->host->files != NULL) {
      curfle = wdata->host->files;
      wdata->host->files = wdata->host->files->next;
      tempfle = prevfle = newfle;
      if(column == 1) {
         while(tempfle != NULL && (wdata->sortasds ?
            strcmp(tempfle->file, curfle->file) <= 0 :
            strcmp(tempfle->file, curfle->file) >= 0)) {

            prevfle = tempfle;
            tempfle = tempfle->next;
         }
      }
      else if(column == 2) {
         while(tempfle != NULL && (wdata->sortasds ? 
            tempfle->size <= curfle->size : 
            tempfle->size >= curfle->size)) {

            prevfle = tempfle;
            tempfle = tempfle->next;
         }
      }
      else if(column == 3) {
         while(tempfle != NULL && (wdata->sortasds ?
            strcmp(tempfle->user, curfle->user) <= 0 :
            strcmp(tempfle->user, curfle->user) >= 0)) {

            prevfle = tempfle;
            tempfle = tempfle->next;
         }
      }
      else if(column == 4) {
         while(tempfle != NULL && (wdata->sortasds ?
            strcmp(tempfle->group, curfle->group) <= 0 :
            strcmp(tempfle->group, curfle->group) >= 0)) {

            prevfle = tempfle;
            tempfle = tempfle->next;
         }
      }
      else if(column == 6) {
         while(tempfle != NULL && (wdata->sortasds ?
            strcmp(tempfle->attribs, curfle->attribs) <= 0 :
            strcmp(tempfle->attribs, curfle->attribs) >= 0)) {

            prevfle = tempfle;
            tempfle = tempfle->next;
         }
      }
      if(tempfle == NULL) {
         prevfle->next = curfle;
         curfle->next = NULL;
      }
      else if(tempfle == prevfle) {
         curfle->next = newfle;
         newfle = curfle;
      }
      else {            
         curfle->next = prevfle->next;
         prevfle->next = curfle;
      }
   }
   wdata->host->files = newfle;
   gtk_clist_freeze(clist);
   deselectall(NULL, (gpointer) wdata);
   gtk_clist_clear(clist);
   tempfle = newfle;
   while(tempfle != NULL) {
      add_file_listbox(wdata, tempfle);
      tempfle = tempfle->next;
   }
   gtk_clist_thaw(clist);
}
/*****************************************************************************/
void delete_ftp_file_info(struct ftp_window_data *wdata) {
   struct ftp_file_data *fle;
      
   deselectall(NULL, (gpointer) wdata);
   gtk_clist_clear(GTK_CLIST(wdata->listbox));
   fle = wdata->host->files;
   while(fle != NULL) {
      wdata->host->files = wdata->host->files->next;
      free(fle);
      fle = wdata->host->files;
   }
   wdata->host->last = NULL;
}
/*****************************************************************************/
void queue_log(int type, char *format, ...) {
   struct ftp_log_queue *newlog, *templog;
   char tempstr[MAXSTR];
   va_list argp;
   
   newlog = mymalloc(sizeof(struct ftp_log_queue));
   newlog->type = type;
   va_start(argp, format);
   vsnprintf(tempstr, sizeof(tempstr), format, argp);
   tempstr[sizeof(tempstr)-1] = '\0';
   strncpy(newlog->msg, tempstr, sizeof(newlog->msg));
   newlog->next = NULL;
   pthread_mutex_lock(&log_mutex);
   if(file_transfer_logs == NULL) file_transfer_logs = newlog;
   else {
      templog = file_transfer_logs;
      while(templog->next != NULL) templog = templog->next;
      templog->next = newlog;
   }
   pthread_mutex_unlock(&log_mutex);
}
/*****************************************************************************/
void ftp_log(int type, char *format, ...) {
   char tempstr[MAXSTR];
   GdkColormap *cmap;
   GdkColor fore;
   va_list argp;
   guint pos;
   int upd;
   
   upd = GTK_TEXT(logwdw)->vadj->upper - GTK_TEXT(logwdw)->vadj->page_size == GTK_TEXT(logwdw)->vadj->value;
   cmap = gdk_colormap_get_system();
   fore.red = fore.green = fore.blue = 0;
   if(type == LOG_SEND) fore.green = 0x8600;
   else if(type == LOG_RECV) fore.blue = 0xffff;
   else fore.red = 0xffff;
   if(!gdk_color_alloc(cmap, &fore)) g_error("Could not allocate color for log window");
   gtk_text_freeze(GTK_TEXT(logwdw));
   pos = gtk_text_get_length(GTK_TEXT(logwdw));

   va_start(argp, format);
   vsnprintf(tempstr, sizeof(tempstr), format, argp);
   tempstr[sizeof(tempstr)-1] = '\0';
   gtk_text_insert(GTK_TEXT(logwdw), NULL, &fore, NULL, tempstr, -1);
   
   gtk_text_set_point(GTK_TEXT(logwdw), pos+strlen(tempstr));
   gtk_text_thaw(GTK_TEXT(logwdw));
   if(upd) {
      gtk_adjustment_set_value(GTK_TEXT(logwdw)->vadj, 
         GTK_TEXT(logwdw)->vadj->upper - GTK_TEXT(logwdw)->vadj->page_size);
   }
   fix_display();
}
/*****************************************************************************/
void update_ftp_info(struct ftp_window_data *wdata) {
   if(wdata->local == 1) {
      gtk_label_set(GTK_LABEL(wdata->hoststxt), "Local");
      gtk_entry_set_text(GTK_ENTRY(wdata->diredit), wdata->host->dir);
   }
   else if(wdata->local == 0) {
      gtk_label_set(GTK_LABEL(wdata->hoststxt), wdata->host->host);
      gtk_entry_set_text(GTK_ENTRY(wdata->diredit), wdata->host->dir);
   }
   else {
      gtk_label_set(GTK_LABEL(wdata->hoststxt), "Not connected");
      gtk_entry_set_text(GTK_ENTRY(wdata->diredit), "");
   }
}
/*****************************************************************************/
void change_ascii(GtkWidget *widget, struct ftp_window_data *wdata) {
   wdata->host->type = FTP_ASCII;
}
/*****************************************************************************/
void change_binary(GtkWidget *widget, struct ftp_window_data *wdata) {
   wdata->host->type = FTP_BINARY;
}
/*****************************************************************************/
void selectrow(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data) {
   struct ftp_window_data *wdata;
   
   wdata = (struct ftp_window_data *) data;
   if(!wdata->items[row]) wdata->numselected++;
   wdata->items[row] = 1;
}
/*****************************************************************************/
void unselectrow(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data) {
   struct ftp_window_data *wdata;
   
   wdata = (struct ftp_window_data *) data;
   if(wdata->items[row]) wdata->numselected--;
   wdata->items[row] = 0;
}
/*****************************************************************************/
void selectall(GtkWidget *widget, struct ftp_window_data *wdata) {
   struct ftp_file_data *tempfle;
   int i = 0;
   
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Select All: Not connected to a remote site\n");
      return;
   }
   tempfle = wdata->host->files;
   while(tempfle != NULL) {
      gtk_clist_select_row(GTK_CLIST(wdata->listbox), i++, 0);
      tempfle = tempfle->next;
   }
}
/*****************************************************************************/
void deselectall(GtkWidget *widget, struct ftp_window_data *wdata) {
   struct ftp_file_data *tempfle;
   int i = 0;
   
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Deselect All: Not connected to a remote site\n");
      return;
   }
   tempfle = wdata->host->files;
   while(tempfle != NULL) {
      gtk_clist_unselect_row(GTK_CLIST(wdata->listbox), i++, 0);
      tempfle = tempfle->next;
   }
}
/*****************************************************************************/
void list_dblclick(GtkWidget *widget, GdkEventButton *event, gpointer data) {
   if(event->type == GDK_2BUTTON_PRESS) chdirfunc(widget, data);
}
/*****************************************************************************/
void chdirfunc(GtkWidget *widget, struct ftp_window_data *wdata) {
   struct ftp_file_data *tempfle;
   int pos, i;
   
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Chdir: Not connected to a remote site\n");
      return;
   }
   else if(wdata->numselected != 1) {
      ftp_log(LOG_MISC, "Chdir: You must only have one item selected\n");
      return;
   }
   for(i=0; i<wdata->totalitems; i++) if(wdata->items[i]) break;
   pos = i;
   tempfle = wdata->host->files;
   for(i=0; i<pos; i++) tempfle = tempfle->next;
   if(wdata->local == 0 && ftp_chdir(wdata, wdata->host, tempfle->file)) {
      update_ftp_info(wdata);
      gtk_clist_freeze(GTK_CLIST(wdata->listbox));
      delete_ftp_file_info(wdata);
      ftp_list_files(wdata, wdata->host);
      gtk_clist_thaw(GTK_CLIST(wdata->listbox));
   }
   else if(wdata->local == 1) {
      if(!chdir(tempfle->file)) {
         ftp_log(LOG_MISC, "Successfully changed local directory to %s\n", tempfle->file);
         update_ftp_info(wdata);
         gtk_clist_freeze(GTK_CLIST(wdata->listbox));
         delete_ftp_file_info(wdata);
         add_local_files(wdata);
         gtk_clist_thaw(GTK_CLIST(wdata->listbox));
      }
      else {
         ftp_log(LOG_MISC, "Could not change %s directory to %s: %s\n", 
            wdata->local == 1 ? "local" : "remote", tempfle->file, sys_errlist[errno]);
      }
   }
}
/*****************************************************************************/
void chdiredit(GtkWidget *widget, struct ftp_window_data *wdata) {
   char tempstr[MAXSTR];
   
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Chdir: Not connected to a remote site\n");
      return;
   }
   if(!expand_path(gtk_entry_get_text(GTK_ENTRY(wdata->diredit)), tempstr, sizeof(tempstr))) return;
   if(wdata->local == 0 && ftp_chdir(wdata, wdata->host, tempstr)) {
      update_ftp_info(wdata);
      gtk_clist_freeze(GTK_CLIST(wdata->listbox));
      delete_ftp_file_info(wdata);
      ftp_list_files(wdata, wdata->host);
      gtk_clist_thaw(GTK_CLIST(wdata->listbox));
   }
   else if(wdata->local == 1) {
      if(!chdir(tempstr)) {
         ftp_log(LOG_MISC, "Successfully changed local directory to %s\n", tempstr);
         update_ftp_info(wdata);
         gtk_clist_freeze(GTK_CLIST(wdata->listbox));
         delete_ftp_file_info(wdata);
         add_local_files(wdata);
         gtk_clist_thaw(GTK_CLIST(wdata->listbox));
      }
      else {
         ftp_log(LOG_MISC, "Could not change %s directory to %s: %s\n", 
            wdata->local == 1 ? "local" : "remote", tempstr, sys_errlist[errno]);
      }
   }
}
/*****************************************************************************/
void refresh(GtkWidget *widget, struct ftp_window_data *wdata) {
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Refresh: Not connected to a remote site\n");
      return;
   }
   gtk_clist_freeze(GTK_CLIST(wdata->listbox));
   delete_ftp_file_info(wdata);
   if(wdata->local == 0) ftp_list_files(wdata, wdata->host);
   else add_local_files(wdata);
   gtk_clist_thaw(GTK_CLIST(wdata->listbox));
   update_ftp_info(wdata);
}
/*****************************************************************************/
void exitCB(GtkWidget *widget, gpointer data) {
   gtk_main_quit();
}
/*****************************************************************************/
void disconnect(GtkWidget *widget, struct ftp_window_data *wdata) {
   if(wdata->local == -1) {
      ftp_log(LOG_MISC, "Disconnect: Not connected to a remote site\n");
      return;
   }
   ftp_disconnect(wdata, wdata->host, 1);
}
/*****************************************************************************/
void savelog(GtkWidget *widget, gpointer data) {
   GtkWidget *filew;
   
   filew = gtk_file_selection_new ("Save Log");

   gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filew)->ok_button), "clicked", GTK_SIGNAL_FUNC(dosavelog), filew);
   gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filew)->ok_button), "clicked", GTK_SIGNAL_FUNC(savelog_destroy), filew);
   gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filew)->cancel_button), "clicked", GTK_SIGNAL_FUNC(savelog_destroy), filew);
   gtk_file_selection_set_filename(GTK_FILE_SELECTION(filew), "gftp.log");
   gtk_widget_show(filew);
}
/*****************************************************************************/
void dosavelog(GtkWidget *widget, GtkFileSelection *fs) {
   char *filename, *txt;
   guint textlen;
   FILE *fd;

   filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs));
   fd = fopen(filename, "w");
   if(fd == NULL) {
      ftp_log(LOG_MISC, "Error: Cannot open %s: %s\n", filename, sys_errlist[errno]);
      return;
   }
   textlen = gtk_text_get_length(GTK_TEXT(logwdw));
   txt = gtk_editable_get_chars(GTK_EDITABLE(logwdw), 0, -1);
   if(!fwrite(txt, 1, textlen, fd)) {
      ftp_log(LOG_MISC, "Error: Error writing to %s\n", filename);
   }
   else {
      ftp_log(LOG_MISC, "Successfully wrote the log file to %s\n", filename);
   }
   fclose(fd);
   free(txt);
}
/*****************************************************************************/
void savelog_destroy(GtkWidget *widget, GtkWidget *dialog) {
   gtk_widget_destroy(dialog);
}
/*****************************************************************************/
void clearlog(GtkWidget *widget, gpointer data) {
   guint pos;
   
   pos = gtk_text_get_length(GTK_TEXT(logwdw));
   gtk_text_set_point(GTK_TEXT(logwdw), pos);
   gtk_text_backward_delete(GTK_TEXT(logwdw), pos);
}
/*****************************************************************************/
void fix_display(void) {
   while(gtk_events_pending()) gtk_main_iteration();
}
/*****************************************************************************/
gint update_downloads(gpointer data) {
   char *add_data[3] = {NULL, NULL, NULL};
   struct ftp_transfer_data *temptdata, *prevtdata;
   struct ftp_both_windows *windows;
   struct ftp_file_data *tempfle;
   struct ftp_log_queue *templog;
   char tempstr[MAXSTR];
   gint num;
   
   windows = (struct ftp_both_windows *) data;
   if(file_transfer_logs != NULL) {
      while(file_transfer_logs != NULL) {
         templog = file_transfer_logs;
         file_transfer_logs = file_transfer_logs->next;
         ftp_log(templog->type, templog->msg);
         free(templog);
      }
   }
   if(file_transfers == NULL) {
      gtk_timeout_add(1000, update_downloads, data);
      return(0);
   }
   else {
      temptdata = prevtdata = file_transfers;
      num = 0;
      pthread_mutex_lock(&transfer_mutex);
      while(temptdata != NULL) {
         if(temptdata->action < 0) {
            if(temptdata->direction) {
               refresh(NULL, temptdata->otherwdata);
            }
            else if(compare_hdata_structs(temptdata->hdata, temptdata->otherwdata->host)) {
               refresh(NULL, temptdata->otherwdata);
            }
            if(temptdata->created) {
               /* We have to release the mutex because when the list item gets
                  deleted, it will call my deselect function. This func will
                  also lock the mutex */
               pthread_mutex_unlock(&transfer_mutex);
               gtk_clist_remove(GTK_CLIST(dlwdw), num);
               pthread_mutex_lock(&transfer_mutex);
               num++;
            }
            while(temptdata->hdata->files != NULL) {
               tempfle = temptdata->hdata->files;
               temptdata->hdata->files = temptdata->hdata->files->next;
               free(tempfle);
            }
            free(temptdata->hdata);

            if(temptdata == prevtdata) file_transfers = file_transfers->next;
            else prevtdata->next = temptdata->next;
            free(temptdata);
            temptdata = prevtdata->next;
            continue;
         }
         else if(temptdata->action > 0) {
            gtk_clist_insert(GTK_CLIST(dlwdw), num,
               add_data);
            temptdata->action = 0;
            temptdata->created = 1;
         }
         if(num >= 0) {
            gtk_clist_set_text(GTK_CLIST(dlwdw), num, 0, temptdata->curfle->file);

            gtk_clist_set_text(GTK_CLIST(dlwdw), num, 1, temptdata->progressstr);

            snprintf(tempstr, sizeof(tempstr), "%s%s", temptdata->hdata->host, temptdata->hdata->dir);
            tempstr[sizeof(tempstr)-1] = '\0';
            gtk_clist_set_text(GTK_CLIST(dlwdw), num, 2, tempstr);
            num++;
            temptdata = temptdata->next;
         }
      }
      pthread_mutex_unlock(&transfer_mutex);
   }
   gtk_timeout_add(1000, update_downloads, data);
   return(0);
}
/*****************************************************************************/
void selectdl(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data) {
   struct ftp_transfer_data *tdata;
   int i;
   
   tdata = file_transfers;
   for(i=0; i<row; i++) tdata = tdata->next;
   pthread_mutex_lock(&transfer_mutex);
   tdata->selected = 1;
   pthread_mutex_unlock(&transfer_mutex);
}
/*****************************************************************************/
void unselectdl(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data) {
   struct ftp_transfer_data *tdata;
   int i;
   
   tdata = file_transfers;
   for(i=0; i<row; i++) tdata = tdata->next;
   pthread_mutex_lock(&transfer_mutex);
   tdata->selected = 0;
   pthread_mutex_unlock(&transfer_mutex);
}
/*****************************************************************************/
void stop_transfer(GtkWidget *widget, gpointer data) {
   struct ftp_transfer_data *temptdata;

   if(file_transfer_logs != NULL) return;
   pthread_mutex_lock(&transfer_mutex);
   temptdata = file_transfers;
   while(temptdata != NULL) {
      if(temptdata->selected) {
         temptdata->cancel = 1;
         ftp_log(LOG_MISC, "Stopping the transfer of %s\n", temptdata->curfle->file);
      }
      temptdata = temptdata->next;
   }
   pthread_mutex_unlock(&transfer_mutex);
}
/*****************************************************************************/
int compare_hdata_structs(struct ftp_host_data *hdata1, struct ftp_host_data *hdata2) {
   if(hdata1->port == hdata2->port && strcmp(hdata1->host, hdata2->host) == 0 &&
      strcmp(hdata1->user, hdata2->user) == 0 && strcmp(hdata1->passwd, hdata2->passwd) == 0 &&
      strcmp(hdata1->dir, hdata2->dir) == 0) return(1);
   else return(0);
}
/*****************************************************************************/
