/*
 * Copyright 1999-2001 Red Hat, Inc.
 * 
 * All Rights Reserved.
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 * Except as contained in this notice, the name of Red Hat shall not be
 * used in advertising or otherwise to promote the sale, use or other dealings
 * in this Software without prior written authorization from Red Hat.
 *
 */

#include <arpa/inet.h>
#include <errno.h>
#include <popt.h>
#include <resolv.h>
#include <net/if.h>
#include <newt.h>
#include <stdlib.h>
#include <string.h>

# include "../isys/dns.h"

#include "lang.h"
#include "loader.h"
#include "loadermisc.h"
#include "log.h"
#include "net.h"
#include "windows.h"

char *netServerPrompt = \
    N_("Please enter the following information:\n"
       "\n"
       "    o the name or IP number of your %s server\n" 
       "    o the directory on that server containing\n" 
       "      %s for your architecture\n");

struct intfconfig_s {
    newtComponent ipEntry, nmEntry, gwEntry, nsEntry;
    char * ip, * nm, * gw, * ns;
};

typedef int int32;

static void ipCallback(newtComponent co, void * dptr) {
    struct intfconfig_s * data = dptr;
    struct in_addr ipaddr, nmaddr, addr;
    char * ascii;
    int broadcast, network;

    if (co == data->ipEntry) {
        if (strlen(data->ip) && !strlen(data->nm)) {
            if (inet_aton(data->ip, &ipaddr)) {
                ipaddr.s_addr = ntohl(ipaddr.s_addr);
                ascii = "255.255.255.0";
                newtEntrySet(data->nmEntry, ascii, 1);
            }
        }
    } else if (co == data->nmEntry) {
        if (!strlen(data->ip) || !strlen(data->nm)) return;
        if (!inet_aton(data->ip, &ipaddr)) return;
        if (!inet_aton(data->nm, &nmaddr)) return;

        network = ipaddr.s_addr & nmaddr.s_addr;
        broadcast = (ipaddr.s_addr & nmaddr.s_addr) | (~nmaddr.s_addr);

        if (!strlen(data->gw)) {
            addr.s_addr = htonl(ntohl(broadcast) - 1);
            newtEntrySet(data->gwEntry, inet_ntoa(addr), 1);
        }

        if (!strlen(data->ns)) {
            addr.s_addr = htonl(ntohl(network) + 1);
            newtEntrySet(data->nsEntry, inet_ntoa(addr), 1);
        }
    }
}

static void fillInIpInfo(struct networkDeviceConfig * cfg) {
    int32 * i;
    char * nm;
   
    if (!(cfg->dev.set & PUMP_INTFINFO_HAS_NETMASK)) {
        i = (int32 *) &cfg->dev.ip;

        nm = "255.255.255.0";

        inet_aton(nm, &cfg->dev.netmask);
        cfg->dev.set |= PUMP_INTFINFO_HAS_NETMASK;
    }

    if (!(cfg->dev.set & PUMP_INTFINFO_HAS_BROADCAST)) {
        *((int32 *) &cfg->dev.broadcast) = (*((int32 *) &cfg->dev.ip) & 
                           *((int32 *) &cfg->dev.netmask)) | 
                           ~(*((int32 *) &cfg->dev.netmask));
        cfg->dev.set |= PUMP_INTFINFO_HAS_BROADCAST;
    }

    if (!(cfg->dev.set & PUMP_INTFINFO_HAS_NETWORK)) {
        *((int32 *) &cfg->dev.network) = 
                *((int32 *) &cfg->dev.ip) &
                *((int32 *) &cfg->dev.netmask);
        cfg->dev.set |= PUMP_INTFINFO_HAS_NETWORK;
    }
}

void initLoopback(void) {
    struct pumpNetIntf dev;

    strcpy(dev.device, "lo");
    inet_aton("127.0.0.1", &dev.ip);
    inet_aton("255.0.0.0", &dev.netmask);
    inet_aton("127.0.0.0", &dev.network);
    dev.set = PUMP_INTFINFO_HAS_NETMASK | PUMP_INTFINFO_HAS_IP
                | PUMP_INTFINFO_HAS_NETWORK;

    pumpSetupInterface(&dev);
}

static void dhcpBoxCallback(newtComponent co, void * ptr) {
    struct intfconfig_s * c = ptr;

    newtEntrySetFlags(c->ipEntry, NEWT_FLAG_DISABLED, NEWT_FLAGS_TOGGLE);
    newtEntrySetFlags(c->gwEntry, NEWT_FLAG_DISABLED, NEWT_FLAGS_TOGGLE);
    newtEntrySetFlags(c->nmEntry, NEWT_FLAG_DISABLED, NEWT_FLAGS_TOGGLE);
    newtEntrySetFlags(c->nsEntry, NEWT_FLAG_DISABLED, NEWT_FLAGS_TOGGLE);
}

static int getDnsServers(struct networkDeviceConfig * cfg) {
    int rc;
    char * ns = "";
    struct newtWinEntry entry[] = { { N_("Nameserver IP"), &ns, 0 },
                                      { NULL, NULL, 0 } };

    do {
        rc = newtWinEntries(_("Nameserver"), 
                _("Your dynamic IP request returned IP configuration "
                  "information, but it did not include a DNS nameserver. "
                  "If you know what your nameserver is, please enter it "
                  "now. If you don't have this information, you can leave "
                  "this field blank and the install will continue."),
                40, 5, 10, 25, entry, _("OK"), _("Back"), NULL);

        if (rc == 2) return LOADER_BACK;

        if (ns && *ns && !inet_aton(ns, &cfg->dev.dnsServers[0])) {
            newtWinMessage(_("Invalid IP Information"), _("Retry"),
                        _("You entered an invalid IP address."));
            rc = 2;
        } 

    } while (rc == 2);

    cfg->dev.set |= PUMP_NETINFO_HAS_DNS;
    cfg->dev.numDns = 1;

    return LOADER_OK;
}

void setupNetworkDeviceConfig(struct networkDeviceConfig * cfg, 
                              struct loaderData_s * loaderData) {
    struct in_addr addr;

    if (loaderData->ipinfo_set == 0) {
        return;
    }

    if (loaderData->ip) {
        /* this is how we specify dhcp */
        if (!strncmp(loaderData->ip, "dhcp", 4)) {
            char * chptr;
            /* JKFIXME: this soooo doesn't belong here.  and it needs to
             * be broken out into a function too */
            logMessage("sending dhcp request through device %s", loaderData->netDev);
            winStatus(50, 3, _("Dynamic IP"), 
                      _("Sending request for IP information for %s"), 
                      loaderData->netDev, 0);

            chptr = pumpDhcpRun(loaderData->netDev, 0, 0, NULL, &cfg->dev, NULL);
            newtPopWindow();
            if (chptr) {
                logMessage("pump told us: %s", chptr);
                return;
            }

            cfg->isDynamic = 1;
        } else if (inet_aton(loaderData->ip, &addr)) {
            cfg->dev.ip = addr;
            cfg->dev.set |= PUMP_INTFINFO_HAS_IP;
            cfg->isDynamic = 0;
        }
        cfg->preset = 1;
    }

    if (loaderData->netmask && (inet_aton(loaderData->netmask, &addr))) {
        cfg->dev.netmask = addr;
        cfg->dev.set |= PUMP_INTFINFO_HAS_NETMASK;
    }

    if (loaderData->gateway && (inet_aton(loaderData->gateway, &addr))) {
        cfg->dev.gateway = addr;
        cfg->dev.set |= PUMP_NETINFO_HAS_GATEWAY;
    }

    if (loaderData->dns && (inet_aton(loaderData->dns, &addr))) {
        cfg->dev.dnsServers[0] = addr;
        cfg->dev.numDns = 1;
        cfg->dev.set |= PUMP_NETINFO_HAS_DNS;
    }

    if (loaderData->hostname) {
        logMessage("setting specified hostname of %s", loaderData->hostname);
        cfg->dev.hostname = strdup(loaderData->hostname);
        cfg->dev.set |= PUMP_NETINFO_HAS_HOSTNAME;
    }

    cfg->noDns = loaderData->noDns;

    logMessage("we set up ip information via kickstart");
}

int readNetConfig(char * device, struct networkDeviceConfig * cfg, int flags) {
    newtComponent text, f, okay, back, answer, dhcpCheckbox;
    newtGrid grid, subgrid, buttons;
    struct networkDeviceConfig newCfg;
    struct intfconfig_s c;
    int i;
    struct in_addr addr;
    char dhcpChoice;
    char * chptr;

    /* JKFIXME: this is horribly inconsistent -- all of the other loaderData
     * gets acted on even if I'm not in kickstart... */
    if (FL_KICKSTART(flags) && !FL_TESTING(flags) && cfg->preset) {
        logMessage("doing kickstart... setting it up");
        configureNetwork(cfg);
        findHostAndDomain(cfg, flags);

        if (!cfg->noDns)
            writeResolvConf(cfg);
        return 0;
    }        

    /* JKFIXME: I do NOT like this crap */
#if !defined(__s390__) && !defined(__s390x__)
    text = newtTextboxReflowed(-1, -1, 
                _("Please enter the IP configuration for this machine. Each "
                  "item should be entered as an IP address in dotted-decimal "
                  "notation (for example, 1.2.3.4)."), 50, 5, 10, 0);

    subgrid = newtCreateGrid(2, 4);
    newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT,
                     newtLabel(-1, -1, _("IP address:")),
                     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(subgrid, 0, 1, NEWT_GRID_COMPONENT,
                     newtLabel(-1, -1, _("Netmask:")),
                     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(subgrid, 0, 2, NEWT_GRID_COMPONENT,
                     newtLabel(-1, -1, _("Default gateway (IP):")),
                     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);
    newtGridSetField(subgrid, 0, 3, NEWT_GRID_COMPONENT,
                     newtLabel(-1, -1, _("Primary nameserver:")),
                     0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);

    c.ipEntry = newtEntry(-1, -1, NULL, 16, &c.ip, 0);
    c.nmEntry = newtEntry(-1, -1, NULL, 16, &c.nm, 0);
    c.gwEntry = newtEntry(-1, -1, NULL, 16, &c.gw, 0);
    c.nsEntry = newtEntry(-1, -1, NULL, 16, &c.ns, 0);

    if (!cfg->isDynamic) {
        if (cfg->dev.set & PUMP_INTFINFO_HAS_IP)
            newtEntrySet(c.ipEntry, inet_ntoa(cfg->dev.ip), 1);

        if (cfg->dev.set & PUMP_INTFINFO_HAS_NETMASK)
            newtEntrySet(c.nmEntry, inet_ntoa(cfg->dev.netmask), 1);

        if (cfg->dev.set & PUMP_NETINFO_HAS_GATEWAY)
            newtEntrySet(c.gwEntry, inet_ntoa(cfg->dev.gateway), 1);

        if (cfg->dev.numDns)
            newtEntrySet(c.nsEntry, inet_ntoa(cfg->dev.dnsServers[0]), 1);

        dhcpChoice = ' ';
    } else {
        dhcpChoice = '*';
    }

    dhcpCheckbox = newtCheckbox(-1, -1, 
                _("Use dynamic IP configuration (BOOTP/DHCP)"),
                dhcpChoice, NULL, &dhcpChoice);
    newtComponentAddCallback(dhcpCheckbox, dhcpBoxCallback, &c);
    if (dhcpChoice == '*') dhcpBoxCallback(dhcpCheckbox, &c);

    newtGridSetField(subgrid, 1, 0, NEWT_GRID_COMPONENT, c.ipEntry,
                     1, 0, 0, 0, 0, 0);
    newtGridSetField(subgrid, 1, 1, NEWT_GRID_COMPONENT, c.nmEntry,
                     1, 0, 0, 0, 0, 0);
    newtGridSetField(subgrid, 1, 2, NEWT_GRID_COMPONENT, c.gwEntry,
                     1, 0, 0, 0, 0, 0);
    newtGridSetField(subgrid, 1, 3, NEWT_GRID_COMPONENT, c.nsEntry,
                     1, 0, 0, 0, 0, 0);

    buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL);

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

    f = newtForm(NULL, NULL, 0);
    newtGridAddComponentsToForm(grid, f, 1);
    newtGridWrappedWindow(grid, _("Configure TCP/IP"));
    newtGridFree(grid, 1);
   
    newtComponentAddCallback(c.ipEntry, ipCallback, &c);
    newtComponentAddCallback(c.nmEntry, ipCallback, &c);
    
    do {
        answer = newtRunForm(f);

        if (answer == back) {
            newtFormDestroy(f);
            newtPopWindow();
            return LOADER_BACK;
        } 

        if (dhcpChoice == ' ') {
            i = 0;
            memset(&newCfg, 0, sizeof(newCfg));
            if (*c.ip && inet_aton(c.ip, &addr)) {
                i++;
                newCfg.dev.ip = addr;
                newCfg.dev.set |= PUMP_INTFINFO_HAS_IP;
            }

            if (*c.nm && inet_aton(c.nm, &addr)) {
                i++;
                newCfg.dev.netmask = addr;
                newCfg.dev.set |= PUMP_INTFINFO_HAS_NETMASK;
            }

            if (i != 2) {
                newtWinMessage(_("Missing Information"), _("Retry"),
                            _("You must enter both a valid IP address and a "
                              "netmask."));
            }

            strcpy(newCfg.dev.device, device);
            newCfg.isDynamic = 0;
        } else {
            if (!FL_TESTING(flags)) {
                winStatus(50, 3, _("Dynamic IP"), 
                          _("Sending request for IP information..."),
                            0);
                chptr = pumpDhcpRun(device, 0, 0, NULL, &newCfg.dev, NULL);
                newtPopWindow();
            } else {
                chptr = NULL;
            }

            if (!chptr) {
                newCfg.isDynamic = 1;
                if (!(newCfg.dev.set & PUMP_NETINFO_HAS_DNS)) {
                    logMessage("pump worked, but didn't return a DNS server");
                    i = getDnsServers(&newCfg);
                    i = i ? 0 : 2;
                } else {
                    i = 2; 
                }
            } else {
                logMessage("pump told us: %s", chptr);
                i = 0;
            }
        }
    } while (i != 2);

#else /* s390 now */
   char * env;
   /* quick and dirty hack by opaukstadt@millenux.com for s390 */
   /* ctc stores remoteip in broadcast-field until pump.h is changed */
   memset(&newCfg, 0, sizeof(newCfg));
   strcpy(newCfg.dev.device, device);
   newCfg.isDynamic = 0;
   env = getenv("IPADDR");
   if (env && *env) {
     if(inet_aton(env, &newCfg.dev.ip))
      newCfg.dev.set |= PUMP_INTFINFO_HAS_IP;
   }
   env = getenv("NETMASK");
   if (env && *env) {
     if(inet_aton(env, &newCfg.dev.netmask))
      newCfg.dev.set |= PUMP_INTFINFO_HAS_NETMASK;
   }
   env = getenv("GATEWAY");
   if (env && *env) {
     if(inet_aton(env, &newCfg.dev.gateway))
      newCfg.dev.set |= PUMP_NETINFO_HAS_GATEWAY;
   }
   env = getenv("NETWORK");
   if (env && *env) {
     if(inet_aton(env, &newCfg.dev.network))
      newCfg.dev.set |= PUMP_INTFINFO_HAS_NETWORK;
   }
   env = getenv("DNS");
   if (env && *env) {
     char *s = strdup (env);
     char *t = strtok (s, ":");
     if(inet_aton((t? t : s), &newCfg.dev.dnsServers[0]))
      newCfg.dev.set |= PUMP_NETINFO_HAS_DNS;
   }
   if (!strncmp(newCfg.dev.device, "ctc", 3)) {
     env = getenv("REMIP");
     if (env && *env) {
       if(inet_aton(env, &newCfg.dev.gateway))
         newCfg.dev.set |= PUMP_NETINFO_HAS_GATEWAY;
     }
   }
   env = getenv("BROADCAST");
   if (env && *env) {
     if(inet_aton(env, &newCfg.dev.broadcast))
       newCfg.dev.set |= PUMP_INTFINFO_HAS_BROADCAST;     
   }
#endif   /* s390 */

    cfg->isDynamic = newCfg.isDynamic;
    memcpy(&cfg->dev,&newCfg.dev,sizeof(newCfg.dev));

    fillInIpInfo(cfg);

#if !defined(__s390__) && !defined(__s390x__)
    if (!(cfg->dev.set & PUMP_NETINFO_HAS_GATEWAY)) {
        if (*c.gw && inet_aton(c.gw, &addr)) {
            cfg->dev.gateway = addr;
            cfg->dev.set |= PUMP_NETINFO_HAS_GATEWAY;
        }
    }

    if (!(cfg->dev.numDns)) {
        if (*c.ns && inet_aton(c.ns, &addr)) {
            cfg->dev.dnsServers[0] = addr;
            cfg->dev.numDns = 1;
        }
    }

    newtPopWindow();
#endif
    if (!FL_TESTING(flags)) {
        configureNetwork(cfg);
        findHostAndDomain(cfg, flags);
        writeResolvConf(cfg);
    }

    return 0;
}

int configureNetwork(struct networkDeviceConfig * dev) {
#if !defined(__s390__) && !defined(__s390x__)
    pumpSetupInterface(&dev->dev);

    if (dev->dev.set & PUMP_NETINFO_HAS_GATEWAY)
        pumpSetupDefaultGateway(&dev->dev.gateway);

#endif
    return 0;
}

int writeNetInfo(const char * fn, struct networkDeviceConfig * dev,
                 struct knownDevices * kd) {
    FILE * f;
    int i;

    for (i = 0; i < kd->numKnown; i++)
        if (!strcmp(kd->known[i].name, dev->dev.device)) break;
    
    if (!(f = fopen(fn, "w"))) return -1;

    fprintf(f, "DEVICE=%s\n", dev->dev.device);

    /* JKFIXME: this used kd->known[i].code == CODE_PCMCIA to toggle onboot */
    fprintf(f, "ONBOOT=yes\n");

    if (dev->isDynamic) {
        fprintf(f, "BOOTPROTO=dhcp\n");
    } else {
        fprintf(f, "BOOTPROTO=static\n");
        fprintf(f, "IPADDR=%s\n", inet_ntoa(dev->dev.ip));
        fprintf(f, "NETMASK=%s\n", inet_ntoa(dev->dev.netmask));
        if (dev->dev.set & PUMP_NETINFO_HAS_GATEWAY) {
          fprintf(f, "GATEWAY=%s\n", inet_ntoa(dev->dev.gateway));
          if (!strncmp(dev->dev.device, "ctc", 3) || \
              !strncmp(dev->dev.device, "iucv", 4)) 
            fprintf(f, "REMIP=%s\n", inet_ntoa(dev->dev.gateway));
        }
        if (dev->dev.set & PUMP_INTFINFO_HAS_BROADCAST)
          fprintf(f, "BROADCAST=%s\n", inet_ntoa(dev->dev.broadcast));    
    }

    if (dev->dev.set & PUMP_NETINFO_HAS_HOSTNAME)
        fprintf(f, "HOSTNAME=%s\n", dev->dev.hostname);
    if (dev->dev.set & PUMP_NETINFO_HAS_DOMAIN)
        fprintf(f, "DOMAIN=%s\n", dev->dev.domain);

    fclose(f);

    return 0;
}

int writeResolvConf(struct networkDeviceConfig * net) {
    char * filename = "/etc/resolv.conf";
    FILE * f;
    int i;
#if defined(__s390__) || defined(__s390x__)
    return 0;
#endif

    if (!(net->dev.set & PUMP_NETINFO_HAS_DOMAIN) && !net->dev.numDns)
        return LOADER_ERROR;

    f = fopen(filename, "w");
    if (!f) {
        logMessage("Cannot create %s: %s\n", filename, strerror(errno));
        return LOADER_ERROR;
    }

    if (net->dev.set & PUMP_NETINFO_HAS_DOMAIN)
        fprintf(f, "search %s\n", net->dev.domain);

    for (i = 0; i < net->dev.numDns; i++) 
        fprintf(f, "nameserver %s\n", inet_ntoa(net->dev.dnsServers[i]));

    fclose(f);

    res_init();         /* reinit the resolver so DNS changes take affect */

    return 0;
}

int findHostAndDomain(struct networkDeviceConfig * dev, int flags) {
    char * name, * chptr;

    if (!FL_TESTING(flags)) {
        writeResolvConf(dev);
    }

    if (!(dev->dev.set & PUMP_NETINFO_HAS_HOSTNAME)) {
        winStatus(40, 3, _("Hostname"), 
                  _("Determining host name and domain..."));
        name = mygethostbyaddr(inet_ntoa(dev->dev.ip));
        newtPopWindow();

        if (!name) {
            logMessage("reverse name lookup failed");
            return 1;
        }

        logMessage("reverse name lookup worked");

        dev->dev.hostname = strdup(name);
        dev->dev.set |= PUMP_NETINFO_HAS_HOSTNAME;
    } else {
        name = dev->dev.hostname;
    }

    if (!(dev->dev.set & PUMP_NETINFO_HAS_DOMAIN)) {
        for (chptr = name; *chptr && (*chptr != '.'); chptr++) ;
        if (*chptr == '.') {
            if (dev->dev.domain) free(dev->dev.domain);
            dev->dev.domain = strdup(chptr + 1);
            dev->dev.set |= PUMP_NETINFO_HAS_DOMAIN;
        }
    }

    return 0;
}

void setKickstartNetwork(struct loaderData_s * loaderData, int argc, 
                         char ** argv, int * flagsPtr) {
    char * arg, * bootProto, * device;
    int noDns = 0, rc;
    poptContext optCon;

    struct poptOption ksOptions[] = {
        { "bootproto", '\0', POPT_ARG_STRING, &bootProto, 0 },
        { "device", '\0', POPT_ARG_STRING, &device, 0 },
        { "gateway", '\0', POPT_ARG_STRING, NULL, 'g' },
        { "ip", '\0', POPT_ARG_STRING, NULL, 'i' },
        { "nameserver", '\0', POPT_ARG_STRING, NULL, 'n' },
        { "netmask", '\0', POPT_ARG_STRING, NULL, 'm' },
        { "nodns", '\0', POPT_ARG_NONE, &noDns, 0 },
        { "hostname", '\0', POPT_ARG_STRING, NULL, 'h'},
        { 0, 0, 0, 0, 0 }
    };
    
    logMessage("kickstartNetwork");
    optCon = poptGetContext(NULL, argc, (const char **) argv, 
                            ksOptions, 0);    
    while ((rc = poptGetNextOpt(optCon)) >= 0) {
        arg = (char *) poptGetOptArg(optCon);

        switch (rc) {
        case 'g':
            loaderData->gateway = strdup(arg);
            break;
        case 'i':
            loaderData->ip = strdup(arg);
            break;
        case 'n':
            loaderData->dns = strdup(arg);
            break;
        case 'm':
            loaderData->netmask = strdup(arg);
            break;
        case 'h':
            if (loaderData->hostname) 
                free(loaderData->hostname);
            loaderData->hostname = strdup(arg);
            break;
        }
    }

    if (rc < -1) {
        newtWinMessage(_("Kickstart Error"), _("OK"),
                       _("Bad argument to kickstart network command %s: %s"),
                       poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
                       poptStrerror(rc));
    } else {
        poptFreeContext(optCon);
    }

    /* if they've specified dhcp/bootp or haven't specified anything, 
     * use dhcp for the interface */
    if (!strncmp(bootProto, "dhcp", 4) || !strncmp(bootProto, "bootp", 4) ||
        (!bootProto && !loaderData->ip)) {
        loaderData->ip = strdup("dhcp");
        loaderData->ipinfo_set = 1;
    } else if (loaderData->ip) {
        /* JKFIXME: this assumes a bit... */
        loaderData->ipinfo_set = 1;
    }

    /* now make sure the specified bootproto is valid */
    if (bootProto && strcmp(bootProto, "dhcp") && strcmp(bootProto, "bootp") &&
        strcmp(bootProto, "static") && strcmp(bootProto, "query")) {
        newtWinMessage(_("Kickstart Error"), _("OK"),
                       _("Bad bootproto %s specified in network command"),
                       bootProto);
    } 

    if (device) {
        loaderData->netDev = strdup(device);
        loaderData->netDev_set = 1;
    }

    if (noDns) {
        loaderData->noDns = 1;
    }
    
}


int chooseNetworkInterface(struct knownDevices * kd, 
                           struct loaderData_s * loaderData,
                           int flags) {
    int i, rc;
    int deviceNums = 0;
    int deviceNum;
    char ** devices;

    /* JKFIXME: this is a lot bigger than it has to be.. */
    devices = alloca((kd->numKnown + 1) * sizeof(*devices));
    for (i = 0; i < kd->numKnown; i++) {
        if (kd->known[i].class != CLASS_NETWORK)
            continue;

        devices[deviceNums++] = kd->known[i].name;

        /* this device has been set and we don't really need to ask 
         * about it again... */
        if (loaderData->netDev && (loaderData->netDev_set == 1) &&
            !strcmp(loaderData->netDev, kd->known[i].name))
            return LOADER_NOOP;
    }

    devices[deviceNums] = NULL;

    /* ASSERT: we should *ALWAYS* have a network device when we get here */
    if (!deviceNums) {
        logMessage("ASSERT: no network device in chooseNetworkInterface");
        return LOADER_ERROR;
    }

    /* JKFIXME: if we only have one interface and it doesn't have link,
     * do we go ahead? */
    if (deviceNums == 1) {
        loaderData->netDev = devices[0];
        return LOADER_NOOP;
    }

    startNewt(flags);

    /* JKFIXME: should display link status */
    deviceNum = 0;
    rc = newtWinMenu(_("Networking Device"), 
		     _("You have multiple network devices on this system. "
		       "Which would you like to install through?"), 40, 10, 10,
		     deviceNums < 6 ? deviceNums : 6, devices,
		     &deviceNum, _("OK"), _("Back"), NULL);
    if (rc == 2)
        return LOADER_BACK;

    loaderData->netDev = devices[deviceNum];

    return LOADER_OK;
}