# $Progeny: main.py,v 1.46 2002/03/05 20:16:49 epg Exp $

import configlet
import gettext
import re
import os

from configlet import debug

TRUE = 1
FALSE = 0

LOCALEDIR="/usr/share/locale"           # FIXME
#_cat = gettext.Catalog("etherconf", LOCALEDIR)
#_ = _cat.gettext
def _(s):
    return s

def _optionmenu_get_selected_item(self):
    return self.get_menu().get_active()

# This will be provided by GTK+ 2.0
def _optionmenu_get_history(self):
    menu = self.get_menu()
    children = menu.children()
    item = menu.get_active()

    for i in range(len(children)):
        if children[i] == item:
            break

    return i

def _get_devices():
    result = []
    netdev = open("/proc/net/dev")

    line = netdev.readline()
    while line:
        line = re.sub(re.compile(r"^\s*"), "", line)
        words = re.split(":", line)
        if len(words) >= 2:
            if words[0][:3] == "eth":
                result.append(words[0])

        line = netdev.readline()

    netdev.close()

    result.sort()
    return result

def _dcstring(var, val, device=None):
    if device:
        s = "etherconf/%s etherconf/%s:%s %s" % (var, var, device, val)
    else:
        s = "etherconf/%s etherconf/%s %s" % (var, var, val)

    debug("etherconf", "Reporting %s" % (s,))
    return s

class _DeviceInfo:
    def __init__(self):
        self.configure = FALSE
        self.removable = FALSE
        self.dhcp = TRUE
        self.dhcphost = ""
        self.ip = ""
        self.netmask = ""
        self.gateway = ""

    def __str__(self):
        str = ("configure: %d, removable: %d, dhcp: %d, dhcphost: \"%s\""
               % (self.configure, self.removable, self.dhcp, self.dhcphost))
        str += (", ip: \"%s\", netmask: \"%s\", gateway: \"%s\""
                % (self.ip, self.netmask, self.gateway))
        return str

class Etherconf(configlet.Configlet):
    # Methods the Configlet system will call

    def gnome_setup(self):
        configlet.Configlet.gnome_setup(self)

        global gnome_ui
        import gnome.ui
        gnome_ui = gnome.ui
        global gtk
        import gtk

        self.device_info = {}
        self.devices = _get_devices()
        if self.devices:
            self.current = self.devices[0]

        self.device_optionmenu = self.wtree.get_widget("device_optionmenu")
        self.hostname_entry = self.wtree.get_widget("hostname_entry")
        self.domainname_entry = self.wtree.get_widget("domainname_entry")
        self.unconfigured_radiobutton = self.wtree.get_widget("unconfigured_radiobutton")
        self.dhcp_radiobutton = self.wtree.get_widget("dhcp_radiobutton")
        self.manual_radiobutton = self.wtree.get_widget("manual_radiobutton")
        self.configuration_frame = self.wtree.get_widget("configuration_frame")
        self.removable_checkbutton = self.wtree.get_widget("removable_checkbutton")
        self.dhcp_frame = self.wtree.get_widget("dhcp_frame")
        self.dhcphost_entry = self.wtree.get_widget("dhcphost_entry")
        self.manual_frame = self.wtree.get_widget("manual_frame")
        self.ip_entry = self.wtree.get_widget("ip_entry")
        self.gateway_entry = self.wtree.get_widget("gateway_entry")
        self.netmask_entry = self.wtree.get_widget("netmask_entry")
        self.nameservers_entry = self.wtree.get_widget("nameservers_entry")
        self.mailhub_label = self.wtree.get_widget("mailhub_label")
        self.mailhub_entry = self.wtree.get_widget("mailhub_entry")

        # Disable the mailhub entry (we reenable it later if needed).
        self.debug("disabling mailhub")
        self.mailhub_label.set_sensitive(gtk.FALSE)
        self.mailhub_entry.set_sensitive(gtk.FALSE)
        self.mailhub_label.hide()
        self.mailhub_entry.hide()
        self.do_mailhub = gtk.FALSE

        # Connect signals
        dict = {}
        # This should work but does not; self.__class__ is a string
        # rather than a class.  Bug in Python?
        #for key in dir(self.__class__):
        global Etherconf
        for key in dir(Etherconf):
            dict[key] = getattr(self, key)
        self.wtree.signal_autoconnect(dict)

    def load_debconf(self, dcdata):
        self.mail_config = []

        for i in dcdata:
            (template, question, value) = re.split(r"\s+", i, 2)
            (package, varname) = re.split("/", question, 1)

            if value == '""' or value == "none":
                value = ""

            if package == "postfix":
                self.debug("postfix debconf information found: %s"
                           % varname)
                self.mail_config.append(i)
                if varname == "relayhost" \
                   and "postfix" in self.get_packages():
                    self.debug("enabling mailhub")
                    self.mailhub_label.set_sensitive(gtk.TRUE)
                    self.mailhub_entry.set_sensitive(gtk.TRUE)
                    self.mailhub_label.show()
                    self.mailhub_entry.show()
                    self.do_mailhub = gtk.TRUE
                    if value:
                        self.mailhub_entry.set_text(value)

            if package != "etherconf":
                continue

            if varname == "INT-devices":
                # We don't need to read this one, but we do need to
                # set it.  See debconf() below.
                pass
            elif varname == "hostname":
                if value:
                    self.hostname_entry.set_text(value)
            elif varname == "domainname":
                if value:
                    self.domainname_entry.set_text(value)
            elif varname == "nameservers":
                if value:
                    self.nameservers_entry.set_text(value)

            else:
                # If a ValueError occurs, this is not a dhcp-p:eth0
                # type question (which is what we're after), so it
                # doesn't matter what it is, we're simply not
                # interested.
                try:
                    (varname, device) = re.split(":", varname, 1)
                except ValueError:
                    continue

                info = self.get_device_info(device)
                if varname == "configure":
                    if value == "true":
                        info.configure = TRUE
                    else:
                        info.configure = FALSE
                elif varname == "removable":
                    if value == "true":
                        info.removable = TRUE
                    else:
                        info.removable = FALSE
                elif varname == "dhcp-p":
                    if value == "true":
                        info.dhcp = TRUE
                    else:
                        info.dhcp = FALSE
                elif varname == "dhcphost":
                    if value:
                        info.dhcphost = value
                elif varname == "ipaddr":
                    if value:
                        info.ip = value
                elif varname == "netmask":
                    if value:
                        info.netmask = value
                elif varname == "gateway":
                    if value:
                        info.gateway = value

        self.setup_menu(self.devices)
        self.change_device(self.devices[0])

    def validate(self):
        if len(self.hostname_entry.get_chars(0, -1)) == 0 \
           or len(self.domainname_entry.get_chars(0, -1)) == 0:
            dialog = gnome_ui.GnomeMessageBox("""\
The hostname and domain name are both required.""", "error",
                                              gnome_ui.STOCK_BUTTON_OK)
            dialog.set_position(gtk.WIN_POS_CENTER)
            discard_garbage = dialog.run_and_close()
            return FALSE

        # Check for illegal characters in hostname
        attempted_hostname = self.hostname_entry.get_chars(0, -1)
        if re.match("[^A-Za-z0-9-]", attempted_hostname):
            dialog = gnome_ui.GnomeMessageBox("""\
Only letters, digits, and the hyphen/minus symbol are legal
characters in a hostname.""", "error", gnome_ui.STOCK_BUTTON_OK)
            dialog.set_position(gtk.WIN_POS_CENTER)
            discard_garbage = dialog.run_and_close()
            return FALSE

        need_nameservers = TRUE
        have_gateway = TRUE
        self.debug("start of validate loop")
        self.save_device(self.current)
        for i in self.devices:
            info = self.get_device_info(i)

            # Nothing to do if the device is disabled
            if not info.configure:
                continue

            # Nothing to get wrong for DHCP
            if info.dhcp:
                need_nameservers = FALSE
                continue

            if len(info.ip) == 0 or len(info.netmask) == 0:
                dialog = gnome_ui.GnomeMessageBox("""\
The IP address and netmask of an interface are required to
configure the interface in the absence of DHCP.""", "error",
                                                  gnome_ui.STOCK_BUTTON_OK)
                dialog.set_position(gtk.WIN_POS_CENTER)
                discard_garbage = dialog.run_and_close()
                return FALSE

            if len(info.gateway) == 0:
                have_gateway = FALSE

        # these are merely highly recommended
        if not have_gateway:
            dialog = gnome_ui.GnomeMessageBox("""\
In most situations, the IP address of a gateway is necessary
for the machine to reach, e.g., the Internet.

Without a gateway, your machine will likely only be able to
communicate with machines that share its network segment.

Are you sure you want to proceed without a gateway?""",
                "warning", gnome_ui.STOCK_BUTTON_YES, gnome_ui.STOCK_BUTTON_NO)
            dialog.set_position(gtk.WIN_POS_CENTER)
            discard_garbage = dialog.run_and_close()
            if discard_garbage == 1: # no, fix the gateway
                return FALSE

        if need_nameservers \
           and len(self.nameservers_entry.get_chars(0, -1)) == 0:
            dialog = gnome_ui.GnomeMessageBox("""\
In most situations, the IP address of one or more domain
name servers is necessary for the machine to be able to
resolve Internet domain names (e.g., "quux.example.com") to
IP addresses ("192.168.106.42").

Without a name server, your machine will only be able to
communicate by hostname with machines listed in the
/etc/hosts file.

Are you sure you want to proceed without any name servers?""",
                "warning", gnome_ui.STOCK_BUTTON_YES, gnome_ui.STOCK_BUTTON_NO)
            dialog.set_position(gtk.WIN_POS_CENTER)
            discard_garbage = dialog.run_and_close()
            if discard_garbage == 1: # no, fix the name servers
                return FALSE

        return TRUE

    def on_gnome_close(self):
        self.hostname = self.hostname_entry.get_chars(0, -1)
        self.domainname = self.domainname_entry.get_chars(0, -1)
        self.nameservers = self.nameservers_entry.get_chars(0, -1)

        if self.do_mailhub:
            self.relayhost = self.mailhub_entry.get_chars(0, -1)

        if not self.nameservers:
            self.nameservers = "none"

        self.save_device(self.current)

    def report_debconf(self):
        results = []
        int_devices = ""

        if self.do_mailhub:
            results.extend(self.mail_config)
            if self.relayhost:
                results.append("postfix/main_mailer_type postfix/main_mailer_type FLAGS:changed Internet with smarthost")
                results.append("postfix/relayhost postfix/relayhost FLAGS:changed %s"
                               % (self.relayhost,))
            else:
                results.append("postfix/main_mailer_type postfix/main_mailer_type FLAGS:changed Internet Site")
                results.append("postfix/relayhost postfix/relayhost FLAGS:changed ")

            results.append("postfix/mailname postfix/mailname FLAGS:changed %s.%s"
                           % (self.hostname, self.domainname))

        results.append(_dcstring("replace-existing-files", "true"))
        results.append(_dcstring("hostname", self.hostname))
        results.append(_dcstring("domainname", self.domainname))
        results.append(_dcstring("nameservers", self.nameservers))

        for i in self.devices:
            int_devices = "%s:%s" % (int_devices, i)
            info = self.get_device_info(i)

            if info.configure:
                results.append(_dcstring("configure", "true", i))
            else:
                results.append(_dcstring("configure", "false", i))

            if info.removable:
                results.append(_dcstring("removable", "true", i))
            else:
                results.append(_dcstring("removable", "false", i))

            if info.dhcp:
                results.append(_dcstring("dhcp-p", "true", i))
            else:
                results.append(_dcstring("dhcp-p", "false", i))

            results.append(_dcstring("dhcphost", info.dhcphost, i))
            results.append(_dcstring("ipaddr", info.ip, i))
            results.append(_dcstring("netmask", info.netmask, i))
            results.append(_dcstring("gateway", info.gateway, i))

        results.append(_dcstring("INT-devices", int_devices[1:]))

        return results

    # GTK+ signal handlers

    def on_unconfigured_radiobutton_toggled(self, togglebutton):
        self.debug("on_unconfigured_radiobutton_toggled")
        unconfigured = togglebutton.get_active()
        self.configuration_frame.set_sensitive(not unconfigured)
        if unconfigured:
            self.dhcp_frame.hide()
            self.manual_frame.hide()

    def on_dhcp_radiobutton_toggled(self, togglebutton):
        self.debug("on_dhcp_radiobutton_toggled")
        dhcp = togglebutton.get_active()
        self.dhcp_frame.set_sensitive(dhcp)
        if dhcp:
            self.dhcp_frame.show()
        else:
            self.dhcp_frame.hide()
    
    def on_manual_radiobutton_toggled(self, togglebutton):
        self.debug("on_manual_radiobutton_toggled")
        manual = togglebutton.get_active()
        self.manual_frame.set_sensitive(manual)
        if manual:
            self.manual_frame.show()
        else:
            self.manual_frame.hide()

    def on_device_menuitem_activate(self, menuitem):
        self.save_device(self.current)
        device = self.devices[_optionmenu_get_history(self.device_optionmenu)]
        self.change_device(device)

    # Internal methods

    def get_device_info(self, device):
        try:
            info = self.device_info[device]
        except KeyError:
            info = _DeviceInfo()
            self.device_info[device] = info

        return info

    def setup_menu(self, devices):
        menu = gtk.GtkMenu()
        for i in devices:
            item = gtk.GtkMenuItem(i)
            item.connect("activate", self.on_device_menuitem_activate)
            menu.append(item)
        menu.show_all()
        self.device_optionmenu.set_menu(menu)

    def change_device(self, device):
        # Sanity check
        if not device in self.devices:
            self.debug("change_device called with %s which is not in list!"
                       % (device,))
            return

        # The "configuration" string is marked in etherconf-strings.h,
        # call _ on it here to pick up that translation.
        self.configuration_frame.set_label("%s %s"
                                           % (device, _("configuration")))
        info = self.get_device_info(device)

        self.unconfigured_radiobutton.set_active(not info.configure)
        self.configuration_frame.set_sensitive(info.configure)
        if info.configure:
            self.removable_checkbutton.set_active(info.removable)
            if info.dhcp:
                self.dhcp_radiobutton.set_active(TRUE)
                self.dhcp_frame.set_sensitive(TRUE)
                self.dhcphost_entry.set_text(info.dhcphost)

                self.manual_frame.set_sensitive(FALSE)
                self.ip_entry.set_text("")
                self.gateway_entry.set_text("")
                self.netmask_entry.set_text("")

            else:
                self.manual_radiobutton.set_active(TRUE)
                self.manual_frame.set_sensitive(TRUE)
                self.ip_entry.set_text(info.ip)
                self.gateway_entry.set_text(info.gateway)
                self.netmask_entry.set_text(info.netmask)

                self.dhcphost_entry.set_text("")
                self.dhcp_frame.set_sensitive(FALSE)

        self.current = device

    def save_device(self, device):
        info = self.get_device_info(device)
        info.configure = not self.unconfigured_radiobutton.get_active()
        info.removable = self.removable_checkbutton.get_active()
        info.dhcp = self.dhcp_radiobutton.get_active()
        info.dhcphost = self.dhcphost_entry.get_chars(0, -1)
        info.ip = self.ip_entry.get_chars(0, -1)
        info.netmask = self.netmask_entry.get_chars(0, -1)
        info.gateway = self.gateway_entry.get_chars(0, -1)

        self.debug("save_device: %s" % (device,))
        #self.debug(info)

_attrs = { "name": "etherconf",
           "display_title": _("Configure Network Interfaces"),
           "description": _("Select this option to configure the devices your system uses to access networks such as the Internet."),
           "packages": ["etherconf"]
}

if os.path.exists("/usr/sbin/postfix"):
    configlet.debug("etherconf", "adding postfix to list of packages")
    _attrs["packages"].append("postfix")
else:
    configlet.debug("etherconf", "didn't find postfix - not adding it")

configlet.register_configlet(Etherconf, _attrs)

# vim:ai:et:sts=4:sw=4:tw=0:
