#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <newt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/mtio.h>
#include <unistd.h>

#include "hd.h"
#include "fs.h"
#include "ftp.h"
#include "install.h"
#include "intl.h"
#include "log.h"
#include "methods.h"
#include "net.h"
#include "scsi.h"
#include "windows.h"

struct ftpinfo {
    char * address;
    char * login;
    char * password;
    char * prefix;
    char * proxy;
    int sock;
    struct pkgSet ps;
};

struct hdinfo {
    char * device;
    char * type;
    char * dir;
};

struct tapeCatalogEntry {
    char * filename;
    int size;
};

struct tapeinfo {
    int fd;
    int offset;
    int curr;
    int catalogEntries;
    struct tapeCatalogEntry * catalog;
};

/* This was split into two pieces to keep the initial install program small */

static int ftpinstStartTransfer(struct ftpinfo * fi, char * filename);
static int ftpinstFinishTransfer(struct ftpinfo * fi);
static int ftpinstGetMappedFile(struct installMethod * method, char * name, 
		                char ** realName);
static int imageGetFile(struct installMethod * method, char * name, 
			char ** realName);
static int singleimageSetSymlinks(struct installMethod * method, 
	              		  struct partitionTable table,
	              		  struct netInfo * netc, 
				  struct intfInfo * intf,
				  struct driversLoaded ** dl);
static int hdSetup(struct installMethod * method, struct partitionTable table,
	           struct netInfo * netc, struct intfInfo * intf,
	           struct driversLoaded ** dl);
static int hdPrepareMedia(struct installMethod * method,
				struct fstab * fstab);
static int hdGetPackageSet(struct installMethod * method,
			     struct pkgSet * ps);
static int hdGetComponentSet(struct installMethod * method,
			       struct pkgSet * ps,
			       struct componentSet * cs);
static int ftpSetup(struct installMethod * method, struct partitionTable table,
	            struct netInfo * netc, struct intfInfo * intf,
	            struct driversLoaded ** dl);
static int ftpGetPackageSet(struct installMethod * method,
			     struct pkgSet * ps);
static int ftpGetComponentSet(struct installMethod * method,
			       struct pkgSet * ps,
			       struct componentSet * cs);
static int imageGetPackageSet(struct installMethod * method,
			      struct pkgSet * ps);
static int imageGetComponentSet(struct installMethod * method,
			     struct pkgSet * ps,
			     struct componentSet * cs);
static inline int fileInBase(char * name);

#ifdef __i386__
static int smbSetup(struct installMethod * method, struct partitionTable table,
	           struct netInfo * netc, struct intfInfo * intf,
	           struct driversLoaded ** dl);
static int smbGetSetup(char ** hostptr, char ** dirptr, char ** acctptr,
			char ** pwptr);
static int smbGetPackageSet(struct installMethod * method,
			     struct pkgSet * ps);
static int smbGetComponentSet(struct installMethod * method,
			       struct pkgSet * ps,
			       struct componentSet * cs);
#endif

static struct installMethod methods[] = {
    { "Local CDROM", 		"cdrom", 0, NULL, singleimageSetSymlinks, 
		imageGetFile, 
		imageGetPackageSet, imageGetComponentSet, NULL, NULL },
    { "NFS image", 		"nfs", 0, NULL, singleimageSetSymlinks, 
		imageGetFile, 
		imageGetPackageSet, imageGetComponentSet, NULL, NULL },
    { "hard drive",		"hd",  0, NULL, hdSetup, imageGetFile,
		hdGetPackageSet, hdGetComponentSet, hdPrepareMedia, NULL },
    { "FTP",			"ftp", 1, NULL, ftpSetup, ftpinstGetMappedFile,
		ftpGetPackageSet, ftpGetComponentSet, NULL, NULL },
#ifdef __i386__
    { "SMB image", 		"smb", 0, NULL, smbSetup, 
		imageGetFile, 
		smbGetPackageSet, smbGetComponentSet, NULL, NULL },
#endif
} ;
static int numMethods = sizeof(methods) / sizeof(struct installMethod);

struct installMethod * findInstallMethod(char * argptr) {
    int i;

    for (i = 0; i < numMethods; i++) 
	if (!strcmp(argptr, methods[i].abbrev)) return (methods + i);

    return NULL;
}

static inline int fileInBase(char * name) {
    if (!strcmp(name, "hdlist") || !strcmp(name, "rpmconvert"))
	return 1;
    if (!strncmp(name, "install", 7) && !strcmp(name + 8, ".tr"))
	return 1;
    return 0;
}

static int imageGetFile(struct installMethod * method, char * name, 
			char ** realName) {
    static char buf[300];

    if (fileInBase(name))
	strcpy(buf, "/tmp/rhimage/RedHat/base/");
    else
	strcpy(buf, "/tmp/rhimage/RedHat/RPMS/");

    strcat(buf, name);
    *realName = buf;

    return 0;
}

static int hdSetup(struct installMethod * method, struct partitionTable table,
	           struct netInfo * netc, struct intfInfo * intf,
	           struct driversLoaded ** dl) {
    newtComponent okay, cancel, form, text, listbox, label, answer, dirEntry;
    newtGrid partGrid, buttons, entryGrid, grid;
    int numParts;
    int i;
    char * type;
    char * dir;
    char * dest;
    char * defaultDevice;
    char * defaultDir;
    char * reflowedText;
    int width, height;
    struct partition * part = NULL;
    int done = 0;
    struct hdinfo * hdi;

    if (method->data) {
	hdi = method->data;
	defaultDir = strdup(hdi->dir);
	defaultDevice = hdi->device;
    } else {
	defaultDir = strdup("/");
	defaultDevice = "";
    }

    while (!done) {
	reflowedText = newtReflowText(_("What partition and directory on that "
					"partition hold the RedHat/RPMS and "
					"RedHat/base directories?"),
					62, 5, 5, &width, &height);
	text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
	newtTextboxSetText(text, reflowedText);
	free(reflowedText);

	addPartitionListbox(table, 4, PART_EXT2 | PART_DOS, NULL, &numParts,
			    &partGrid, &listbox);

 	buttons = newtButtonBar(_("Ok"), &okay, _("Cancel"), &cancel, NULL);

	for (i = 0; i < table.count; i++) {
	    if (!strcmp(table.parts[i].device, defaultDevice)) {
		newtListboxSetCurrentByKey(listbox, table.parts + i);
	    }
	}

	label = newtLabel(-1, -1, _("Directory holding Red Hat:"));
	dirEntry = newtEntry(28, 11, defaultDir, 28, &dir, NEWT_ENTRY_SCROLL);

	entryGrid = newtGridHStacked(NEWT_GRID_COMPONENT, label,
				     NEWT_GRID_COMPONENT, dirEntry,
				     NEWT_GRID_EMPTY);

	grid = newtCreateGrid(1, 4);
	newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
			 0, 0, 0, 1, 0, 0);
	newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, partGrid,
			 0, 0, 0, 1, 0, 0);
	newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, entryGrid,
			 0, 0, 0, 1, 0, 0);
	newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons,
			 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

	newtGridWrappedWindow(grid, _("Select Partition"));

	form = newtForm(NULL, NULL, 0);
	newtGridAddComponentsToForm(grid, form, 1);
	newtGridFree(grid, 1);

	answer = newtRunForm(form);

	if (answer != cancel) {
	    part = newtListboxGetCurrent(listbox);
	}

	dir = strdup(dir);

	newtFormDestroy(form);
	newtPopWindow();

	if (answer == cancel) return INST_CANCEL;

	free(defaultDir);
	defaultDir = dir;
	defaultDevice = part->device;

	switch (part->type) {
	  case PART_EXT2:	type = "ext2"; 		break;
	  case PART_DOS:	type = "msdos"; 	break;
	  default:	continue;
	}

	if (doMount(part->device, "/tmp/hdimage", type, 1, 0))
	    continue;

	unlink("/tmp/rhimage");
	
	/* the physical device is mounted on /tmp/hdimage, but all
	   access are through /tmp/rhimage which points to the RedHat
	   directory in /tmp/hdimage */

	dest = alloca(strlen(dir) + 20);
	sprintf(dest, "/tmp/hdimage/%s", dir);

	if (symlink(dest, "/tmp/rhimage")) {
	    newtWinMessage(_("Error"), _("Ok"), 
			   _("Failed to create /tmp/rhimage symlink: %s"), 
			   strerror(errno));
	    umount("/tmp/hdimage");
	    continue;
	} 

	if (access("/tmp/rhimage/RedHat/base/hdlist", R_OK)) {
	    newtWinMessage(_("Error"), _("Ok"), 
			_("Device %s does not appear to contain "
			  "a Red Hat installation tree."), part->device);
	    umount("/tmp/hdimage");
	    continue;
	} 

	if (method->data) {
	    hdi = method->data;
	    free(hdi->dir);
	}
    
	hdi = malloc(sizeof(*hdi));
	hdi->device = part->device;
	hdi->type = type;
	hdi->dir = strdup(dir);
	method->data = hdi;

	done = 1; 

	umount("/tmp/hdimage");
    }

    return 0;
}

static int hdGetPackageSet(struct installMethod * method,
			     struct pkgSet * ps) {
    struct hdinfo * hdi = method->data;
    int rc;

    if (doMount(hdi->device, "/tmp/hdimage", hdi->type, 1, 0))
	return INST_ERROR;

    rc = psUsingDirectory("/tmp/rhimage/RedHat/RPMS", ps);
    
    umount("/tmp/hdimage");
 
    return rc;
}

static int hdGetComponentSet(struct installMethod * method,
			       struct pkgSet * ps,
			       struct componentSet * cs) {
    struct hdinfo * hdi = method->data;
    int rc;

    if (doMount(hdi->device, "/tmp/hdimage", hdi->type, 1, 0))
	return INST_ERROR;

    rc = psReadComponentsFile("/tmp/rhimage/RedHat/base/comps", ps, cs);
    
    umount("/tmp/hdimage");
 
    return rc;
}

static int hdPrepareMedia(struct installMethod * method,
				struct fstab * fstab) {
    struct hdinfo * hdi = method->data;
    int i;
    char * buf;

    for (i = 0; i < fstab->numEntries; i++) {
	if (fstab->entries[i].isMounted &&
	    !strcmp(fstab->entries[i].device, hdi->device)) break;
    }

    if (i < fstab->numEntries) {
	logMessage("device %s is already mounted -- using symlink",
			fstab->entries[i].device);
	buf = alloca(strlen(fstab->entries[i].mntpoint) + 10);

	sprintf(buf, "/mnt/%s", fstab->entries[i].mntpoint);
	
	rmdir("/tmp/hdimage");
	if (symlink(buf, "/tmp/hdimage")) {
	    logMessage("failed to create symlink %s: %s\n", 
			buf, strerror(errno));
	    newtWinMessage(_("Error"), _("Ok"), 
			   _("Failed to create symlink for package source."));
	    return INST_ERROR;
	}
    } else {
	logMessage("mounting device which contains packages");
	if (doMount(hdi->device, "/tmp/hdimage", hdi->type, 1, 0))
	    return INST_ERROR;
    }

    return 0;
}

static int imageGetPackageSet(struct installMethod * method,
			      struct pkgSet * ps) {
    return psFromHeaderListFile("/tmp/rhimage/RedHat/base/hdlist", ps);
}

static int imageGetComponentSet(struct installMethod * method,
			        struct pkgSet * ps,
			        struct componentSet * cs) {
    return psReadComponentsFile("/tmp/rhimage/RedHat/base/comps", ps, cs);
}

static int singleimageSetSymlinks(struct installMethod * method, 
	              		  struct partitionTable table,
	              		  struct netInfo * netc, 
				  struct intfInfo * intf,
				  struct driversLoaded ** dl) {
    logMessage("making symlink from /tmp/rhimage to image");
    symlink("rhimage", "/tmp/image");
    return 0;
}

/* returns a socket file descriptor, a ftpFinishTransfer() must occur after 
   this call */
static int ftpinstStartTransfer(struct ftpinfo * fi, char * filename) {
    char * buf;
    newtComponent form;
    int fd;

    logMessage("ftping %s to a fd", filename);

    newtCenteredWindow(60, 3, _("Retrieving"));

    buf = alloca(strlen(fi->prefix) + strlen(filename) + 30);
    sprintf(buf, "%s %s...", _("Retrieving"), filename);
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form, newtLabel(1, 1, buf));

    newtDrawForm(form);
    newtRefresh();

    strcpy(buf, fi->prefix);
    strcat(buf, "/RedHat/");
    strcat(buf, filename);
    fd = ftpGetFileDesc(fi->sock, buf);

    return fd;
}

static int ftpinstFinishTransfer(struct ftpinfo * fi) {
    newtPopWindow();

    return ftpGetFileDone(fi->sock);
}

static int ftpinstGetFile(struct ftpinfo * fi, char * filename, char * dest) {
    char * buf;
    newtComponent form;
    int fd, rc;

    logMessage("ftping %s as %s", filename, dest);

    newtCenteredWindow(60, 3, _("Retrieving"));

    buf = alloca(strlen(fi->prefix) + strlen(filename) + 30);
    sprintf(buf, "%s %s...", _("Retrieving"), filename);
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form, newtLabel(1, 1, buf));

    newtDrawForm(form);
    newtRefresh();

    fd = open(dest, O_WRONLY | O_CREAT, 0644);
    if (fd < 0) {
	newtWinMessage(_("Error"), _("Ok"), _("open of %s failed: %s\n"), 
			dest, strerror(errno));
	return INST_ERROR;
    }

    strcpy(buf, fi->prefix);
    strcat(buf, "/RedHat/");
    strcat(buf, filename);
    rc = ftpGetFile(fi->sock, buf, fd);

    close(fd);

    newtFormDestroy(form);
    newtPopWindow();

    if (rc) {
	newtWinMessage("ftp", _("Ok"), _("I cannot get file %s: %s\n"), buf,
			ftpStrerror(rc));
	return INST_ERROR;
    }

    return 0;
}

static int ftpMainSetupPanel(struct ftpinfo * fi, char * doSecondarySetup) {
    newtComponent form, okay, cancel, siteEntry, dirEntry, answer, text, cb;
    char * site, * dir;
    char * reflowedText;
    int width, height;
    newtGrid entryGrid, buttons, grid;

    if (fi->address) {
	site = fi->address;
	dir = fi->prefix;
    } else {
	site = "";
	dir = "";
    }

    if (fi->login || fi->password || fi->login)
	*doSecondarySetup = 'X';
    else
	*doSecondarySetup = ' ';

    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);

    reflowedText = newtReflowText(
	_("Please enter the following information:\n"
	  "\n"
	  "    o the name or IP number of your FTP server\n" 
	  "    o the directory on that server containing\n" 
	  "      Red Hat Linux for your architecure\n"), 
	47, 5, 5, &width, &height);
    text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, reflowedText);
    free(reflowedText);

    siteEntry = newtEntry(22, 8, site, 24, &site, NEWT_ENTRY_SCROLL);
    dirEntry = newtEntry(22, 9, dir, 24, &dir, NEWT_ENTRY_SCROLL);

    entryGrid = newtCreateGrid(2, 2);
    newtGridSetField(entryGrid, 0, 0, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("FTP site name:")),
		     0, 0, 1, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(entryGrid, 0, 1, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("Red Hat directory:")),
		     0, 0, 1, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(entryGrid, 1, 0, NEWT_GRID_COMPONENT, siteEntry,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(entryGrid, 1, 1, NEWT_GRID_COMPONENT, dirEntry,
		     0, 0, 0, 0, 0, 0);

    cb = newtCheckbox(3, 11, _("Use non-anonymous ftp or a proxy server"),
			*doSecondarySetup, NULL, doSecondarySetup);

    grid = newtCreateGrid(1, 4);
    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
		     0, 0, 0, 1, 0, 0);
    newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, entryGrid,
		     0, 0, 0, 1, 0, 0);
    newtGridSetField(grid, 0, 2, NEWT_GRID_COMPONENT, cb,
		     0, 0, 0, 1, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons,
		     0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

    newtGridWrappedWindow(grid, _("FTP Setup"));

    form = newtForm(NULL, NULL, 0);
    newtGridAddComponentsToForm(grid, form, 1);    

    answer = newtRunForm(form);
    if (answer == cancel) {
	newtFormDestroy(form);
	newtPopWindow();
	
	return INST_CANCEL;
    }

    if (fi->address) free(fi->address);
    fi->address = strdup(site);

    if (fi->prefix) free(fi->prefix);
    fi->prefix = strdup(dir);

    newtFormDestroy(form);
    newtPopWindow();

    return 0;
}

static int ftpSecondarySetupPanel(struct ftpinfo * fi) {
    newtComponent form, okay, cancel, answer, text, accountEntry;
    newtComponent passwordEntry, proxyEntry;
    char * account, * password, * proxy;
    newtGrid buttons, entryGrid, grid;
    char * reflowedText;
    int width, height;

    reflowedText = newtReflowText(
	_("If you are using non anonymous ftp, enter the account name and "
	  "password you wish to use below. If you are using an FTP proxy "
	  "enter the name of the FTP proxy server to use."),
	47, 5, 5, &width, &height);
    text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, reflowedText);
    free(reflowedText);

    accountEntry = newtEntry(-1, -1, fi->login, 24, &account, 
			     NEWT_ENTRY_SCROLL);
    passwordEntry = newtEntry(-1, -1, NULL, 24, &password, 
				NEWT_ENTRY_SCROLL | NEWT_ENTRY_HIDDEN);
    proxyEntry = newtEntry(-1, -1, fi->proxy, 24, &proxy, NEWT_ENTRY_SCROLL);

    entryGrid = newtCreateGrid(2, 3);
    newtGridSetField(entryGrid, 0, 0, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("Account name:")),
		     0, 0, 1, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(entryGrid, 0, 1, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("Password:")),
		     0, 0, 1, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(entryGrid, 0, 2, NEWT_GRID_COMPONENT,
		     newtLabel(-1, -1, _("FTP Proxy:")),
		     0, 1, 1, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(entryGrid, 1, 0, NEWT_GRID_COMPONENT, accountEntry,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(entryGrid, 1, 1, NEWT_GRID_COMPONENT, passwordEntry,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(entryGrid, 1, 2, NEWT_GRID_COMPONENT, proxyEntry,
		     0, 1, 0, 0, 0, 0);

    buttons = newtButtonBar(_("Ok"), &okay, _("Cancel"), &cancel, NULL);

    grid = newtCreateGrid(1, 3);
    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0);
    newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, entryGrid, 
		     0, 1, 0, 0, 0, 0);
    newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, 
		     0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

    newtGridWrappedWindow(grid, "Further FTP Setup");

    form = newtForm(NULL, NULL, 0);
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridFree(grid, 1);

    answer = newtRunForm(form);
    if (answer == cancel) {
	newtFormDestroy(form);
	newtPopWindow();
	
	return INST_CANCEL;
    }

    if (fi->login) free(fi->login);
    if (strlen(account))
	fi->login = strdup(account);
    else
	fi->login = NULL;

    if (fi->password) free(fi->password);
    if (strlen(password))
	fi->password = strdup(password);
    else
	fi->password = NULL;

    if (fi->proxy) free(fi->proxy);
    if (strlen(proxy))
	fi->proxy = strdup(proxy);
    else
	fi->proxy = NULL;

    newtFormDestroy(form);
    newtPopWindow();

    return 0;
}

static int ftpSetup(struct installMethod * method, struct partitionTable table,
	            struct netInfo * netc, struct intfInfo * intf,
	            struct driversLoaded ** dl) {
    struct ftpinfo fi;
    enum { FTP_SETUP_NET, FTP_SETUP_FTP1, FTP_SETUP_FTP2, FTP_SETUP_CHECK, 
		FTP_SETUP_DONE } step = FTP_SETUP_NET;
    int rc, fd;
    char doMore;

    memset(&fi, 0, sizeof(fi));

    if (method->data)
	memcpy(&fi, method->data, sizeof(fi));
    else
	memset(&fi, 0, sizeof(fi));

    while (step != FTP_SETUP_DONE) {
	switch (step) {

	  case FTP_SETUP_NET:
	    rc = bringUpNetworking(intf, netc, dl, 1);
	    if (rc) return rc;
	    step = FTP_SETUP_FTP1;
	    break;

	  case FTP_SETUP_FTP1:
	    rc = ftpMainSetupPanel(&fi, &doMore);
	    if (rc == INST_ERROR) 
		return rc;
	    else if (rc)
		step = FTP_SETUP_NET;
	    else if (doMore == ' ')
		step = FTP_SETUP_CHECK;
	    else 
		step = FTP_SETUP_FTP2;
	    break;

	  case FTP_SETUP_FTP2:
	    rc = ftpSecondarySetupPanel(&fi);
	    if (rc == INST_ERROR) 
		return rc;
	    else if (rc)
		step = FTP_SETUP_FTP1;
	    else
		step = FTP_SETUP_CHECK;
	    break;

	  case FTP_SETUP_CHECK:
	    if ((fi.sock = ftpOpen(fi.address, fi.login, 
				   fi.password ? fi.password : "rhinstall@", 
				   fi.proxy, -1)) < 0) {
		newtWinMessage(_("ftp"), _("Ok"), 
				_("I cannot log into machine: %s\n"),
				ftpStrerror(fi.sock));
		step = FTP_SETUP_FTP1;
		break;
	    }

	    
	    fd = ftpinstStartTransfer(&fi, "base/hdlist");
	    if (fd < 0) {
		newtPopWindow();
		ftpClose(fi.sock);
		step = FTP_SETUP_FTP1;
		break;
	    }

	    if (psFromHeaderListDesc(fd, &fi.ps, 1)) {
		ftpClose(fi.sock);
		step = FTP_SETUP_FTP1;
		break;
	    }

	    ftpinstFinishTransfer(&fi);

	    if (ftpinstGetFile(&fi, "base/comps", "/tmp/comps")) {
		ftpClose(fi.sock);
		step = FTP_SETUP_FTP1;
		break;
	    }
	    
	    step = FTP_SETUP_DONE;
	    break;

	  case FTP_SETUP_DONE:
	    break;
	}
    }

    if (method->data) free(method->data);
    method->data = malloc(sizeof(fi));
    memcpy(method->data, &fi, sizeof(fi));

    return 0;
}

static int ftpGetPackageSet(struct installMethod * method,
			     struct pkgSet * ps) {
    struct ftpinfo * fi = method->data;

    *ps = fi->ps;
    return 0;
}

static int ftpGetComponentSet(struct installMethod * method,
			       struct pkgSet * ps,
			       struct componentSet * cs) {
    return psReadComponentsFile("/tmp/comps", ps, cs);
}

static int ftpinstGetMappedFile(struct installMethod * method, char * name, 
		                char ** realName) {
    static char sbuf[300];
    char * buf;
    int rc;
    struct ftpinfo * fi = method->data;

    if (access("/mnt/var/tmp", X_OK))
	strcpy(sbuf, "/tmp/");
    else
	strcpy(sbuf, "/mnt/var/tmp/");

    strcat(sbuf, name);
    *realName = sbuf;

    buf = alloca(strlen(name) + 30);
    if (fileInBase(name))
	strcpy(buf, "base/");
    else
	strcpy(buf, "RPMS/");
    strcat(buf, name);

    rc = ftpinstGetFile(fi, buf, *realName);
    if (!rc) return 0;

    /* Try again, and relogin */
    ftpClose(fi->sock);

    if ((fi->sock = ftpOpen(fi->address, fi->login, 
			    fi->password ? fi->password : "rhinstall@", 
			    fi->proxy, -1)) 
		< 0) {
	newtWinMessage(_("ftp"), _("Ok"), _("I cannot log into machine: %s\n"),
			ftpStrerror(fi->sock));
	free(fi);
	return INST_ERROR;
    }

    return ftpinstGetFile(fi, buf, *realName);
}

#ifdef __i386__
static int smbSetup(struct installMethod * method, struct partitionTable table,
	           struct netInfo * netc, struct intfInfo * intf,
	           struct driversLoaded ** dl) {
    char * host = NULL, * dir = NULL, * acct = NULL, * pass = NULL;
    char * buf;
    static int moduleLoaded = 0;
    enum { SMB_STEP_NET, SMB_STEP_INFO, SMB_STEP_MOUNT, SMB_STEP_DONE }
		step = SMB_STEP_NET;
    int rc;

    while (step != SMB_STEP_DONE) {
	switch (step) {
	  case SMB_STEP_NET:
	    rc = bringUpNetworking(intf, netc, dl, 1);
	    if (rc) return rc;
	    step = SMB_STEP_INFO;
	    break;

	  case SMB_STEP_INFO:
	    rc = smbGetSetup(&host, &dir, &acct, &pass);
	    if (rc == INST_CANCEL)
		step = SMB_STEP_NET;
	    else if (rc == INST_ERROR)
		return INST_ERROR;
	    else
		step = SMB_STEP_MOUNT;
	    break;

	  case SMB_STEP_MOUNT:
	    if (!strlen(host) || !strlen(dir))
		rc = INST_ERROR;
	    else {
		buf = malloc(strlen(host) + strlen(dir) + 10);
		strcpy(buf, host);
		strcat(buf, ":");
		strcat(buf, dir);

		if (!moduleLoaded) {
		    rc = loadModule("smbfs", DRIVER_FS, DRIVER_MINOR_NONE, dl);
		    if (rc) return rc;
		    moduleLoaded = 1;
		}

		rc = doPwMount(buf, "/tmp/rhimage", "smb", 1, 0, acct, pass);

		free(buf);
	    }

	    if (rc) {
		step = SMB_STEP_INFO;
		newtWinMessage("Error", "Ok", 
			"I could not mount that directory from the server");
	    } else {
	        if (access("/tmp/rhimage/RedHat", R_OK)) {
		    step = SMB_STEP_INFO;
		    newtWinMessage(_("Error"), _("Ok"), 
				 _("That directory does not seem "
				   "to contain a Red Hat installation tree."));
		    umount("/tmp/rhimage");
		} else
		    step = SMB_STEP_DONE;
	    }

	    break;

	  case SMB_STEP_DONE:
	    break;
	}
    }

    free(host);
    free(dir);

    return 0;
}

static int smbGetSetup(char ** finalHostptr, char ** finalDirptr, 
			char ** finalAcctptr, char ** finalPwptr) {
    char * host = *finalHostptr;
    char * dir = *finalDirptr;
    char * acct = *finalAcctptr;
    char * pass = *finalPwptr;
    int result;
    struct newtWinEntry entries[] = {
	{ NULL, &host, NEWT_ENTRY_SCROLL },
	{ NULL, &dir, NEWT_ENTRY_SCROLL },
	{ NULL, &acct, NEWT_ENTRY_SCROLL },
	{ NULL, &pass, NEWT_ENTRY_SCROLL },
	{ NULL, NULL, 0 } };

    entries[0].text = _("SMB server name  :");
    entries[1].text = _("Share volume     :");
    entries[2].text = _("Account name     :");
    entries[3].text = _("Password         :");

    result = newtWinEntries(_("SMB Setup"), 
		_("Please enter the following information:\n"
		  "\n"
		  "    o the name or IP number of your SMB server\n"
		  "    o the volume to share which contains\n"
		  "      Red Hat Linux for your architecture"),
		50, 5, 5, 24, entries, _("Ok"), _("Cancel"), NULL);

    if (result == 2) {
        free(host);
        free(dir);
        free(acct);
        free(pass);
	
	return INST_CANCEL;
    }

    if (*finalHostptr) free(*finalHostptr);
    if (*finalDirptr) free(*finalDirptr);
    if (*finalAcctptr) free(*finalAcctptr);
    if (*finalPwptr) free(*finalPwptr);

    *finalHostptr = host;
    *finalDirptr = dir;
    *finalAcctptr = acct;
    *finalPwptr = pass;

    return 0;
}

static int smbGetPackageSet(struct installMethod * method,
			     struct pkgSet * ps) {
    return psUsingDirectory("/tmp/rhimage/RedHat/RPMS", ps);
}

static int smbGetComponentSet(struct installMethod * method,
			       struct pkgSet * ps,
			       struct componentSet * cs) {
    return psReadComponentsFile("/tmp/rhimage/RedHat/base/comps", ps, cs);
}


#endif