#!/usr/bin/python

import sys

bindconf_dir = '/usr/share/bindconf'

sys.path.append(bindconf_dir)

import gnome
import gnome.ui
import gtk
import dnsdata
import Alchemist
import FileBlackBox
import string
import signal
import os
import gettext
import libglade
import GDK
import re

import FwdZone

from gtk import TRUE
from gtk import FALSE
from FwdZone import generic_error_dialog
from dnsdata import TestError
import GdkImlib
hostname_pattern = '^[a-zA-Z0-9.\-@]+$'

##
## I18N stuff
##
_=gettext.gettext
gettext.bindtextdomain("bindconf", "/usr/share/locale")
gettext.textdomain("bindconf")

##
## Global values
##

gladepath = "bindconf.glade"
if not os.path.exists(gladepath):
    gladepath = bindconf_dir + "/bindconf.glade"
xml = libglade.GladeXML (gladepath, domain="bindconf")

image_domain = FwdZone.load_image("domain.png")
image_reverse = FwdZone.load_image("reverse.png")
image_slave = FwdZone.load_image("slave.png")

cfg = dnsdata.DNS()

##
## JRBs rewrite code.
##


##
## Callbacks
##

def on_alias_edit_button_click (*args):
    xml.get_widget ('a_edit_button').clicked ()


def on_zone_ns_add_button_clicked (clicked):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('zone_ns_clist')

    if clist.selection: selected = clist.selection[0]
    else: selected = 0
    ns = FwdZone.NSProxy ()
    if ns.run_add_dialog (fwdzone, xml, TRUE):
        fwdzone.set_dirty()
        row = clist.insert (selected, ['', ns.host , ns.get_hash ()])
        clist.set_pixmap (row, 0, ns.get_pix ()[0], ns.get_pix ()[1])
        clist.set_row_data (row, ns)
        clist.sort ()
        clist.select_row (row, 0)
    
def on_zone_ns_edit_button_clicked (*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('zone_ns_clist')
    if not clist.selection:
        return
    selected = clist.selection[0]
    
    ns = clist.get_row_data (selected)
    if ns.run_edit_dialog (fwdzone, xml, TRUE):
        fwdzone.set_dirty()
        clist.set_text (selected, 1, ns.host )
        clist.set_text (selected, 2, ns.get_hash ())
        clist.sort ()
        clist.select_row (selected, 0)

def on_zone_ns_delete_button_clicked (clicked):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('zone_ns_clist')
    if not clist.selection:
        return
    selected = clist.selection[0]
    clist.remove (selected)
    clist.select_row (selected, 0)
    fwdzone.set_dirty()

def on_zone_mx_add_button_clicked (clicked):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('zone_mx_clist')

    if clist.selection: selected = clist.selection[0]
    else: selected = 0
    mx = FwdZone.MXProxy ()
    if mx.run_add_dialog (fwdzone, xml):
        fwdzone.set_dirty()
        row = clist.insert (selected, ['', str (mx.priority), mx.get_priority_hash ()])
        clist.set_pixtext (row, 0, mx.host, 4, mx.get_pix ()[0], mx.get_pix ()[1])
        clist.set_row_data (row, mx)
        clist.sort ()
        clist.select_row (row, 0)

def on_zone_mx_edit_button_clicked (*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('zone_mx_clist')
    if not clist.selection:
        return
    selected = clist.selection[0]
    mx = clist.get_row_data (selected)
    if mx.run_edit_dialog (fwdzone, xml):
        fwdzone.set_dirty()
        clist.set_pixtext (selected, 0, mx.host, 4, mx.get_pix ()[0], mx.get_pix ()[1])
        clist.set_text (selected, 1, str (mx.priority) )
        clist.set_text (selected, 2, mx.get_priority_hash ())
        clist.sort ()
        clist.select_row (selected, 0)

def on_zone_mx_delete_button_clicked (clicked):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('zone_mx_clist')
    if not clist.selection:
        return
    selected = clist.selection[0]
    clist.remove (selected)
    clist.select_row (selected, 0)

# FIXME add some GTK parameters here
def on_save_activate():
    cfg.saveLocal()

# FIXME add some GTK parameters here
def on_apply_activate():
    cfg.saveBind()
    os.system("/sbin/service named reload &>/dev/null")

# FIXME add some GTK parameters here
def on_apply_and_save_activate(*args):
    on_save_activate()
    on_apply_activate()

def on_generic_clist_select_row (clist, row, column, event, button1, button2):
    button1.set_sensitive (TRUE)
    button2.set_sensitive (TRUE)

def on_generic_clist_unselect_row (clist, row, column, event, button1, button2):
    button1.set_sensitive (FALSE)
    button2.set_sensitive (FALSE)

def on_generic_clist_button_release_event (clist, event, func):
    id = clist.get_data ("signal_id")
    clist.disconnect (id)
    clist.remove_data ("signal_id")
    apply (func)

def on_generic_clist_button_press_event (clist, event, func):
    if event.type == GDK._2BUTTON_PRESS:
        info = clist.get_selection_info (event.x, event.y)
        if info != None:
            id = clist.signal_connect ("button_release_event", on_generic_clist_button_release_event, func)
            clist.set_data ("signal_id", id)

def on_generic_entry_insert_text (entry, partial_text, length, pos, str):
    text = partial_text[0:length]
    if re.match (str, text):
        return
    entry.emit_stop_by_name ('insert_text')

def on_exit_activate (*args):
    dialog = xml.get_widget("query_save_dialog")
    dialog.set_parent (xml.get_widget ('bindconf'))
    button = dialog.run()
    if button == 0:
        on_apply_and_save_activate()
        gtk.mainquit()
    elif button == 1:
        gtk.mainquit()
    dialog.hide()

def on_bindconf_delete_event(*args):
    on_exit_activate(args)
    return TRUE

def on_about_activate (*args):
    dialog = gnome.ui.GnomeAbout('bindconf',
                          '1.6.3',
                          '(C) 2001 Red Hat, Inc.',
                          [ 'Jonathan Blanford <jrb@redhat.com>', 'Elliot Lee <sopwith@redhat.com>', 'Harald Hoyer <harald@redhat.com>'],
                          _('Configures zone files'), None)
                          
    dialog.set_parent (xml.get_widget ('bindconf'))
    dialog.run ()

def reverse_ip_address (ip):
    foo = string.split(ip, '.')
    foo.reverse()
    return string.join(foo, '.')

def add_dot (str):
    try:
        if str[-1] == '.':
            return str
        return str + '.'
    except:
        return ""

def ip_to_strhash (ip):
    if int (ip) < 10:
        return "00" + str (ip)
    if int (ip) < 100:
        return "0" + str (ip)
    return str (ip)

def on_rev_zone_changed(widget):
    zone = xml.get_widget ('rev_master_dialog').get_data ('zone')
    zone.this_dirty = TRUE

def on_rev_zone_name_entry_changed (entry):
    label = xml.get_widget ('rev_zone_reverse_label')
    text = reverse_ip_address (entry.get_text ())
    label.set_text (text + ".in-addr.arpa")
    clist = xml.get_widget ('rev_zone_clist')

    clist.freeze ()
    for i in xrange (0, clist.rows):
        ptr = clist.get_row_data (i)
        clist.set_text (i, 0, add_dot (entry.get_text ()) + ptr[0])
    clist.thaw ()
    on_rev_zone_changed(0)

def on_rev_zone_pns_changed (entry):
    text = entry.get_text ()
    on_rev_zone_changed(0)

def run_rev_zone_address_dialog (dialog, edit=FALSE):
    clist = xml.get_widget ('rev_zone_clist')

    text = xml.get_widget ('rev_zone_name_entry').get_text ()
    xml.get_widget ('rev_master_address_label').set_text (add_dot (text))

    if clist.selection: selected = clist.selection[0]
    else: selected = 0

    spin = xml.get_widget ('rev_master_address_spin')
    entry = xml.get_widget ('rev_master_address_entry')

    if edit:
        ptr = clist.get_row_data (selected)
        spin.set_text (ptr[0])
        entry.set_text (ptr[1])
    else:
        spin.set_text ("")
        entry.set_text ("")
        ptr = None

    while 1:
        button = dialog.run ()
        if button != 0:
            dialog.hide ()
            return

        ip = spin.get_text ()
        host = entry.get_text ()
        if ip == "":
            generic_error_dialog (_("You must enter an octet."), dialog, broken_widget=spin)
            continue
        if host == "":
            generic_error_dialog (_("You must enter a hostname."), dialog, broken_widget=entry)
            continue

        try:
            ptrg = dnsdata.PTR()
            brw = spin
            ptrg.testIp(ip)
            brw = entry
            ptrg.testHost(host)
        except TestError, e:
            generic_error_dialog (e.args, dialog, broken_widget=brw)
            continue
        
        try:
            for i in xrange (0, clist.rows):
                if edit and i == selected:
                    continue
                tmpptr = clist.get_row_data (i)
                if tmpptr[0] == ip:
                    raise 'Duplicate'
        except 'Duplicate':
            generic_error_dialog (_("A host with that octet already exists"), dialog, broken_widget=spin)
            continue
        dialog.hide ()
        break

    if edit:
        clist.set_text (selected, 0, add_dot (text) + ip)
        clist.set_text (selected, 1, host)
        clist.set_text (selected, 2, ip_to_strhash (ip))
        row = selected
    else:
        row = clist.insert (selected, [add_dot (text) + ip, host, ip_to_strhash (ip)])
    on_rev_zone_changed(0)
    clist.set_row_data (row, (ip, host, ptr))
    clist.select_row (row, 0)

def on_rev_zone_add_button_clicked (*args):
    dialog = xml.get_widget ('rev_master_address_dialog')
    dialog.set_title (_("New Reverse Zone Pointer"))
    run_rev_zone_address_dialog (dialog)

def on_rev_zone_edit_button_clicked (*args):
    dialog = xml.get_widget ('rev_master_address_dialog')
    dialog.set_title (_("Edit Reverse Zone Pointer"))
    run_rev_zone_address_dialog (dialog, TRUE)

def on_rev_zone_delete_button_clicked (button):
    clist = xml.get_widget ('rev_zone_clist')
    selected = clist.selection[0]
    clist.remove (selected)
    clist.select_row (selected, 0)
    

def on_rev_master_ns_add_button_clicked (button):
    clist = xml.get_widget ('rev_master_ns_clist')
    if clist.selection: selected = clist.selection[0]
    else: selected = 0
    ns = FwdZone.NSProxy ()
    host_entry = xml.get_widget ('ns_host_entry')
    applies_to_entry = xml.get_widget ('ns_applies_to_entry')
    dialog = xml.get_widget ('ns_dialog')
    if ns.run_dialog (xml.get_widget ('rev_zone_reverse_label').get (), xml, FALSE, TRUE, TRUE):
        row = clist.insert (selected, ['', ns.host ])
        pix = ns.get_pix ()
        clist.set_pixmap (row, 0, pix[0], pix[1])
        clist.set_row_data (row, ns)
        clist.select_row (row, 0)

def on_rev_master_ns_edit_button_clicked (*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('rev_master_ns_clist')
    selected = clist.selection[0]
    ns = clist.get_row_data (selected)
    host_entry = xml.get_widget ('ns_host_entry')
    applies_to_entry = xml.get_widget ('ns_applies_to_entry')
    dialog = xml.get_widget ('ns_dialog')
    if ns.run_dialog (xml.get_widget ('rev_zone_reverse_label').get (), xml, TRUE, TRUE, TRUE):
        clist.set_text (selected, 1, ns.host)

def on_rev_master_ns_delete_button_clicked (button):
    clist = xml.get_widget ('rev_master_ns_clist')
    selected = clist.selection[0]
    clist.remove (selected)
    clist.select_row (selected, 0)
    # XXX FIXME

def on_fwd_zone_set_button_clicked (button):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    dialog = xml.get_widget ('serial_dialog')
    entry = xml.get_widget ('serial_entry')
    entry.grab_focus ()
    entry.set_text (str (fwdzone.serial))
    button = dialog.run ()
    dialog.hide ()

    if button != 0: return

    serial = entry.get_text ()

    if serial == "": fwdzone.serial = 0
    else: fwdzone.serial = int (serial)

    fwdzone.dirty = TRUE
    fwdzone.zone.dirty = TRUE
    xml.get_widget ('fwd_zone_serial_entry').set_text (str (fwdzone.serial))

def on_rev_zone_soa_button_clicked (button):
    zone = xml.get_widget ('rev_master_dialog').get_data ('zone')
    soa = zone.getSOA()
    xml.get_widget ('soa_refresh_entry').set_text (str (soa.getRefresh()))
    xml.get_widget ('soa_retry_entry').set_text (str (soa.getRetry()))
    xml.get_widget ('soa_expire_entry').set_text (str (soa.getExpire()))
    xml.get_widget ('soa_minimum_entry').set_text (str (soa.getMinimum()))
    dialog = xml.get_widget ('soa_dialog')

    while 1:
        button = dialog.run ()
        dialog.hide ()
        if button != 0:
            return
        break

    def my_int (str):
        try:
            i = int (str)
            return i
        except:
            return 0

    soa.setRefresh(my_int (xml.get_widget ('soa_refresh_entry').get_text ()))
    soa.setRetry(my_int (xml.get_widget ('soa_retry_entry').get_text ()))
    soa.setExpire(my_int (xml.get_widget ('soa_expire_entry').get_text ()))
    soa.setMinimum(my_int (xml.get_widget ('soa_minimum_entry').get_text ()))


def on_fwd_zone_soa_button_clicked (button):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    fwdzone.hydrate_soa ()

    dialog = xml.get_widget ('soa_dialog')
    while 1:
        button = dialog.run ()
        dialog.hide ()
        if button != 0:
            return
        fwdzone.dehydrate_soa ()
        fwdzone.set_dirty()
        break

def on_fwd_zone_name_entry_changed (*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    fwdzone.set_name (xml.get_widget ('fwd_zone_name_entry').get_text ())

def on_fwd_zone_clist_select_row (clist, row, column, event):
    xml.get_widget ('fwd_zone_delete_button').set_sensitive (row != 0)

def on_rr_toggled (toggle):
    label1 = xml.get_widget ('rr_label1')
    label2 = xml.get_widget ('rr_label2')

    if xml.get_widget ('rr_address_rbutton').get_active ():
        label1.set_text (_("Host or Domain Name:"))
        label2.set_text (_("Served By:"))
    elif xml.get_widget ('rr_address_rbutton').get_active ():
        label1.set_text (_("Alias:"))
        label2.set_text (_("Host Name:"))
    else:
        label1.set_text (_("Host Name:"))
        label2.set_text (_("Resolves To:"))

def on_rr_type_menu_selection_done (menu, *args):
    i = menu.children().index(menu.get_active())

    xml.get_widget ('rr_type_notebook').set_page (i)


def on_fwd_zone_add_button_clicked (button):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    dialog = xml.get_widget ('rr_type_dialog')

    omenu = xml.get_widget ('rr_type_omenu')
    menu = omenu.get_menu ()
    omenu.set_history (0)
    xml.get_widget ('rr_type_notebook').set_page (0)

    xml.get_widget ('rr_host_label').set_text ("." + fwdzone.name)
    xml.get_widget ('rr_alias_label').set_text ("." + fwdzone.name)
    xml.get_widget ('rr_nameserver_label').set_text ("." + fwdzone.name)
    xml.get_widget ('rr_host_host_entry').set_text ('')
    xml.get_widget ('rr_host_address_entry').set_text ('')
    xml.get_widget ('rr_alias_alias_entry').set_text ('')
    xml.get_widget ('rr_alias_host_entry').set_text ('')
    xml.get_widget ('rr_nameserver_host_entry').set_text ('')
    xml.get_widget ('rr_nameserver_handled_entry').set_text ('')

    while 1:
        button = dialog.run ()
        if button != 0:
            dialog.hide ()
            return
        clist = xml.get_widget ('fwd_zone_clist')
        i = menu.children().index (menu.get_active ())

        if clist.selection: selected = clist.selection[0]
        else: selected = 0

        try:
            if i == 0:
                ag = dnsdata.A()
                zone = FwdZone.AProxy ()
                broken_widget = xml.get_widget ('rr_host_host_entry')
                zone.host = broken_widget.get_text ()
                ag.testHost(zone.host)
                broken_widget = xml.get_widget ('rr_host_address_entry')
                zone.ip = broken_widget.get_text ()
                ag.testIp(zone.ip)
                fwdzone.a.append(zone)
            elif i == 1:
                cg = dnsdata.CNAME()
                zone = FwdZone.CNAMEProxy ()
                broken_widget = xml.get_widget ('rr_alias_alias_entry')
                zone.alias = broken_widget.get_text ()
                cg.testAlias(zone.alias)
                broken_widget = xml.get_widget ('rr_alias_host_entry')
                zone.host = broken_widget.get_text ()
                cg.testHost(zone.host)
                #FIXE: check this value
                fwdzone.cname.append(zone)
            elif i == 2:
                nsg = dnsdata.NS()
                zone = FwdZone.NSProxy ()
                broken_widget = xml.get_widget ('rr_nameserver_host_entry')
                zone.host = broken_widget.get_text ()
                nsg.testHost(zone.host)
                broken_widget = xml.get_widget ('rr_nameserver_handled_entry')
                zone.applies_to = broken_widget.get_text ()
                nsg.testAppliesTo(zone.applies_to)
                #FIXME: check this value
                fwdzone.ns.append(zone)
        except TestError, message:
            generic_error_dialog (message.args, dialog, broken_widget=broken_widget)
            continue
        break
    pix = zone.get_pix ()
    row = clist.insert (selected, [ '', zone.get_str (), zone.get_hash () ])
    clist.set_pixmap (row, 0, pix[0], pix[1])
    clist.set_row_data (row, zone)
    clist.select_row (row, 0)
    fwdzone.dirty = TRUE
    dialog.hide ()


def on_fwd_zone_edit_button_clicked (*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('fwd_zone_clist')
    selected = clist.selection [0]
    data = clist.get_row_data (selected)

    data.run_edit_dialog (fwdzone, xml)

    str = data.get_str ()
    pix = data.get_pix ()
    hash = data.get_hash ()

    clist.set_text (selected, 1, str)
    clist.set_text (selected, 2, hash)
    if pix:
        clist.set_pixmap (selected, 0, pix[0], pix[1])

def on_fwd_zone_delete_button_clicked (button):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    clist = xml.get_widget ('fwd_zone_clist')
    selected = clist.selection[0]
    data = clist.get_row_data (selected)
    if isinstance (data, FwdZone.AProxy):
        fwdzone.a.remove (data)
    elif isinstance (data, FwdZone.CNAMEProxy):
        fwdzone.cname.remove (data)
    elif isinstance (data, FwdZone.NSProxy):
        fwdzone.ns.remove (data)
    clist.remove (selected)
    clist.select_row (selected, 0)

def zone_changed(*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    if fwdzone:
        on_fwd_zone_changed(0)
    else:
        on_rev_zone_changed(0)

def on_fwd_zone_changed (*args):
    fwdzone = xml.get_widget ('fwd_master_dialog').get_data ('fwdzone')
    fwdzone.set_dirty ()

    zone = xml.get_widget ('rev_master_dialog').get_data ('zone')

def on_mx_button_toggled (toggle):
    active = xml.get_widget ('mx_other_rbutton').get_active ()
    xml.get_widget ('mx_applies_to_entry').set_sensitive (active)

###
### Edit zones
###

def on_edit_forward_zone (zone):
    dialog = xml.get_widget ('fwd_master_dialog')
    fwdzone = FwdZone.FwdZone (zone, xml)
    dialog.set_data ('fwdzone', fwdzone)
    fwdzone.hydrate ()
    while 1:
        button = dialog.run ()
        if button != 0:
            dialog.hide ()
            dialog.remove_data ('fwdzone')
            return

        try:
            fwdzone.check ()
        except TestError, e:
            generic_error_dialog (e.args, dialog)
            continue
        
        break
    dialog.remove_data ('fwdzone')
    dialog.hide ()
    fwdzone.dehydrate ()

def on_edit_reverse_zone (zone):
    if not hasattr(zone, 'dirty'):
	zone.dirty = FALSE
    zone.this_dirty = TRUE # bad hack
    dialog = xml.get_widget ('rev_master_dialog')
    dialog.set_data('zone', zone)
    zone_entry = xml.get_widget ('rev_zone_name_entry')
    file_entry = xml.get_widget ('rev_zone_file_entry')
    pns_entry = xml.get_widget('rev_zone_pns_entry')
    clist = xml.get_widget ('rev_zone_clist')
    ns_clist = xml.get_widget ('rev_master_ns_clist')
    xml.get_widget ('rev_zone_edit_button').set_sensitive (FALSE)
    xml.get_widget ('rev_zone_delete_button').set_sensitive (FALSE)
    xml.get_widget ('rev_master_ns_edit_button').set_sensitive (FALSE)
    xml.get_widget ('rev_master_ns_delete_button').set_sensitive (FALSE)
    xml.get_widget ('rev_master_ok_button').grab_default ()
    zone_entry.grab_focus ()
    match = re.match (r'(.*).in-addr.arpa', zone.getName ())
    if match:
        text = reverse_ip_address (match.group (1))
    else:
        text = ""
    zone_entry.set_text (text)
    file_entry.set_text (zone.getFile ())
    pns_entry.set_text (zone.getSOA().getPNS())

    clist.clear ()
    ptrlist = zone.getPTRList ()
    if ptrlist:
        for i in xrange (0, ptrlist.getNumPTR ()):
            ptr = ptrlist.getPTR (i)
            ip = str(ptr.getIp ())
            host = ptr.getHost ()
            if len (text) > 1 and text[-1] != '.':
                realip = text + '.' + ip
            else:
                realip = text + ip

            row = clist.append ([realip, host, ip_to_strhash (ip)])
            clist.set_row_data (row, (ip, host, ptr))

    clist.select_row (0, 0)
    
    ns_clist.clear ()
    nslist = zone.getNSList ()
    if nslist:
        for i in xrange (0, nslist.getNumNS ()):
            ns = nslist.getNS (i)
            proxy = FwdZone.NSProxy (ns)
            row = ns_clist.append ( ['', proxy.host ] )
            ns_clist.set_row_data (row, proxy)
            pix = proxy.get_pix ()
            ns_clist.set_pixmap (row, 0, pix[0], pix[1])
    ns_clist.select_row (0, 0)

    zone.this_dirty = FALSE # Bad hack, part deux
    while 1:
        button = dialog.run ()
        if button != 0:
            dialog.hide ()
            return

        text = zone_entry.get_text ()
        #FIXME4: fix wording
        if text == "" or not re.match (r'^\d+\.\d+\.\d+$', text):
            generic_error_dialog (_("You must have a valid IP domain."), dialog, broken_widget=zone_entry)
            continue

        # tests
        try:
            broken_widget = zone_entry
            text = reverse_ip_address (text)
            zone.testName (text + '.in-addr.arpa')
            
            broken_widget = pns_entry
            zone.getSOA().testPNS(pns_entry.get_text())

            broken_widget = file_entry
            zone.testFile(file_entry.get_text())
            
        except TestError, message:
            generic_error_dialog (message.args, dialog, broken_widget=broken_widget)
            continue


        if zone.this_dirty and not zone.dirty:
	    soa = zone.getSOA()
	    soa.setSerial(soa.getSerial() + 1)

        zone.setFile(file_entry.get_text())
        zone.setName (text + '.in-addr.arpa')
        zone.getSOA().setPNS(pns_entry.get_text())
        # Delete the existing PTR list
        zone.delPTRList()
        if clist.rows > 0:
            ptrlist = zone.createPTRList ()
            for i in xrange (0, clist.rows):
                (ip, host, ptr) = clist.get_row_data (i)
                # forget about the old ptr, we just deleted it
                ptr = ptrlist.addPTR ()
                ptr.setIp (ip)
                ptr.setHost (host)
                
        zone.delNSList ()
        if ns_clist.rows >0:
            nslist = zone.createNSList ()
            for i in xrange (0, ns_clist.rows):
                proxy = ns_clist.get_row_data (i)
                ns = nslist.addNS ()
                ns.setAppliesTo ('@')
                ns.setHost (proxy.host)
                
        # Overall test
        try:
            broken_widget = None
            zone.test()
        except TestError, message:
            generic_error_dialog (message.args, dialog, broken_widget=broken_widget)
            continue

        # all tests passed and all values are set
        break
    dialog.hide ()

def on_edit_slave_zone (zone):
    dialog = xml.get_widget ('slave_zone_dialog')
    name_widget = xml.get_widget ('slave_zone_name_entry')
    masters_widget = xml.get_widget ('slave_zone_masters_entry')
    file_widget = xml.get_widget ('slave_zone_file_entry')

    masters = zone.getMasters ()
    masters_str = ""
    if masters:
        num = masters.getNumIp ()
        if num >= 1:
            masters_str = masters.getIp (0)
            for i in xrange (1, num):
                masters_str = masters_str + " " + masters.getIp (i)

    name_widget.set_text (zone.getName ())
    masters_widget.set_text (masters_str)
    file_widget.set_text (zone.getFile ())

    while 1:
        button = dialog.run ()
        dialog.hide ()
        if button != 0:
            return

        name_str = name_widget.get_text ()
        masters_str = masters_widget.get_text ()
        masters_list = string.split (masters_str, " ")
        file_str = file_widget.get_text ()

        try:
            broken_widget = name_widget
            zone.testName (name_str)
            broken_widget = file_str
            zone.testFile (file_str)            
            broken_widget = masters_widget
            mg = dnsdata.Masters()
            for ip in masters_list:
                mg.testIp (ip)            
        except TestError, message:
            generic_error_dialog (message.args, dialog, broken_widget=broken_widget)
            continue
            

        zone.setName (name_str)
        zone.setFile (file_str)
        zone.delMasters ()
        masters = zone.createMasters ()
        for ip in masters_list:
            masters.setIp (masters.addIp (), ip)
            
        try:
            zone.test()
        except TestError, message:
            generic_error_dialog (message.args, dialog)
            continue
            
        break
    
def on_zone_type_toggled (button):
    label = xml.get_widget ('zone_type_label')
    if xml.get_widget ('fwd_zone_rbutton').get_active ():
        label.set_text (_("Domain name:"))
    elif xml.get_widget ('rev_zone_rbutton').get_active ():
        label.set_text (_("IP Address (first 3 Octets):"))
    else:
        label.set_text (_("Domain name:"))
    xml.get_widget ('zone_type_entry').set_text ("")

def on_bindconf_add_button_clicked (button):
    clist = xml.get_widget ('bindconf_clist')
    dialog = xml.get_widget ('zone_type_dialog')

    # bit of a hack, but we do this to avoid setting the size
    # on the widget
    xml.get_widget ('fwd_zone_rbutton').set_active (TRUE)
    xml.get_widget ('rev_zone_rbutton').set_active (TRUE)
    xml.get_widget ('slave_zone_rbutton').set_active (TRUE)
    xml.get_widget ('fwd_zone_rbutton').set_active (TRUE)

    if clist.selection: selected = clist.selection[0]
    else: selected = 0

    while 1:
        button = dialog.run ()

        if button != 0:
            dialog.hide ()
            return
        name = xml.get_widget ('zone_type_entry').get_text ()

        if xml.get_widget ('fwd_zone_rbutton').get_active ():
            file = name + ".zone"

            try:
                fwz = dnsdata.MForwardZone ()
                brw = xml.get_widget ('zone_type_entry')
                fwz.testName(name)
                fwz.testFile(file)
            except TestError, e:
                generic_error_dialog (e.args, dialog, broken_widget=brw)
                continue
            
            zone = cfg.getMForwardZoneList ().addMForwardZone ()
            zone.setName (name)
            zone.setFile (file)
            row = clist.append ( [ '' ] )
            clist.set_pixtext (row, 0, name, 4, image_domain[0], image_domain[1])
            
        elif xml.get_widget ('rev_zone_rbutton').get_active ():
            name = reverse_ip_address (name) + ".in-addr.arpa"
            file = name + ".zone"
            
            try:
                rwz = dnsdata.MReverseZone ()
                brw = xml.get_widget ('zone_type_entry')
                rwz.testName(name)
                rwz.testFile(file)
            except TestError, e:
                generic_error_dialog (e.args, dialog, broken_widget=brw)
                continue
            
            zone = cfg.getMReverseZoneList ().addMReverseZone ()
            zone.setName (name)
            zone.setFile (file)
            row = clist.append ( [ '' ] )
            clist.set_pixtext (row, 0, name, 4, image_reverse[0], image_reverse[1])
        else:
            file = name + ".zone"

            try:
                sz = dnsdata.SlaveZone ()
                brw = xml.get_widget ('zone_type_entry')
                sz.testName(name)
                sz.testFile(file)
            except TestError, e:
                generic_error_dialog (e.args, dialog, broken_widget=brw)
                continue
            
            zone = cfg.getSlaveZoneList ().addSlaveZone ()
            zone.setName (name)
            zone.setFile (name + ".zone")
            row = clist.append ( [ '' ] )
            clist.set_pixtext (row, 0, name, 4, image_slave[0], image_slave[1])
        break
    dialog.hide ()
    clist.set_row_data (row, zone)
    clist.select_row (row, 0)
    on_bindconf_edit_button_clicked()

def on_bindconf_edit_button_clicked (*args):
    clist = xml.get_widget ('bindconf_clist')
    selected = clist.selection[0]
    zone = clist.get_row_data (selected)
    if isinstance (zone, dnsdata.MForwardZone):
        on_edit_forward_zone (zone)
        pix = image_domain
    elif isinstance (zone, dnsdata.MReverseZone):
        on_edit_reverse_zone (zone)
        pix = image_reverse
    elif isinstance (zone, dnsdata.SlaveZone):
        on_edit_slave_zone (zone)
        pix = image_slave
    else: #Unrecognized zone.
        return

    name = zone.getName ()
    clist.set_pixtext (selected, 0, name, 4, pix [0], pix [1])

def on_bindconf_delete_button_clicked (button):
    clist = xml.get_widget ('bindconf_clist')
    selected = clist.selection[0]
    zone = clist.get_row_data (selected)
    zone.unlink ()
    clist.remove (selected)
    clist.select_row (selected, 0)

def setup_main_tree ():
    clist = xml.get_widget ('bindconf_clist')

    zlist = cfg.getSlaveZoneList()
    if zlist:
        num = zlist.getNumSlaveZone()
        for i in xrange(0, num):
            zone = zlist.getSlaveZone(i)
            name = zone.getName ()
            row = clist.append ( [ '' ] )
            clist.set_pixtext (row, 0, name, 4, image_slave[0], image_slave[1])
            clist.set_row_data (row, zone)

    zlist = cfg.getMForwardZoneList()
    if zlist:
        num = zlist.getNumMForwardZone()
        for i in xrange(0, num):
            zone = zlist.getMForwardZone(i)
            name = zone.getName ()
            row = clist.append ( [ '' ] )
            clist.set_pixtext (row, 0, name, 4, image_domain[0], image_domain[1])
            clist.set_row_data (row, zone)


    zlist = cfg.getMReverseZoneList()
    if zlist:
        num = zlist.getNumMReverseZone()
        for i in xrange(0, num):
            zone = zlist.getMReverseZone(i)
            name = zone.getName ()
            row = clist.append ( [ '' ] )
            clist.set_pixtext (row, 0, name, 4, image_reverse[0], image_reverse[1])
            clist.set_row_data (row, zone)
    clist.select_row (0, 0)

def setup_dialogs ():
    xml.get_widget ('bindconf_clist').column_titles_passive ()
    clist = xml.get_widget ('rev_zone_clist')
    clist.column_titles_passive ()
    clist.set_column_auto_resize (0, TRUE)
    clist.set_sort_column (2)
    clist.set_auto_sort (TRUE)
    ## BAD HACK!!! (TM)
    clist.set_column_visibility (2, FALSE)

    clist = xml.get_widget ('fwd_zone_clist')
    clist.set_column_width (0, 20)
    clist.set_column_visibility (2, FALSE)
    clist.set_sort_column (2)
    clist.set_auto_sort (TRUE)

    clist = xml.get_widget ('a_clist')
    clist.column_titles_passive ()
    clist.set_column_auto_resize (0, TRUE)
    clist.set_column_visibility (2, FALSE)
    clist.set_sort_column (2)
    clist.set_auto_sort (TRUE)

    clist = xml.get_widget ('zone_mx_clist')
    clist.column_titles_passive ()
    clist.set_column_auto_resize (0, TRUE)
    clist.set_column_visibility (2, FALSE)
    clist.set_auto_sort (TRUE)
    clist.set_sort_column (2)

    clist = xml.get_widget ('zone_ns_clist')
    clist.column_titles_passive ()
    clist.set_column_auto_resize (0, TRUE)
    clist.set_column_visibility (2, FALSE)
    clist.set_sort_column (2)
    clist.set_auto_sort (TRUE)

    clist = xml.get_widget ('rev_master_ns_clist')
    clist.set_column_width (0, 20)
    clist.set_sort_column (1)
    clist.set_auto_sort (TRUE)

    xml.get_widget ('rr_type_omenu').get_menu ().connect ('selection-done', on_rr_type_menu_selection_done)

    xml.get_widget ('zone_dialog_pixmap').load_file ('/usr/share/pixmaps/gnome-question.png')
    xml.get_widget ('soa_dialog_pixmap').load_file ('/usr/share/pixmaps/gnome-question.png')
    xml.get_widget ('serial_dialog_pixmap').load_file ('/usr/share/pixmaps/gnome-question.png')
    xml.get_widget ('rr_type_pixmap').load_file ('/usr/share/pixmaps/gnome-question.png')

    xml.get_widget ('zone_type_dialog').close_hides (TRUE)
    xml.get_widget ('slave_zone_dialog').close_hides (TRUE)
    xml.get_widget ('rev_master_dialog').close_hides (TRUE)
    xml.get_widget ('rev_master_address_dialog').close_hides (TRUE)
    xml.get_widget ('fwd_master_dialog').close_hides (TRUE)
    xml.get_widget ('soa_dialog').close_hides (TRUE)
    xml.get_widget ('serial_dialog').close_hides (TRUE)
    xml.get_widget ('cname_dialog').close_hides (TRUE)
    xml.get_widget ('mx_dialog').close_hides (TRUE)
    xml.get_widget ('a_dialog').close_hides (TRUE)
    xml.get_widget ('zone_dialog').close_hides (TRUE)
    xml.get_widget ('ns_dialog').close_hides (TRUE)
    xml.get_widget ('rr_type_dialog').close_hides (TRUE)
    xml.get_widget ('about').close_hides (TRUE)
    xml.get_widget ('query_save_dialog').close_hides (TRUE)
def main ():
    xml.signal_autoconnect (
        {
        'on_bindconf_delete_event' : on_bindconf_delete_event,
        'on_exit_activate' : on_exit_activate,
        'on_apply_and_save_activate' : on_apply_and_save_activate,
        'on_about_activate' : on_about_activate,
        'on_bindconf_clist_select_row' : (on_generic_clist_select_row, xml.get_widget ('bindconf_edit_button'), xml.get_widget ('bindconf_delete_button')),
        'on_bindconf_clist_unselect_row' : (on_generic_clist_unselect_row, xml.get_widget ('bindconf_edit_button'), xml.get_widget ('bindconf_delete_button')),
        'on_bindconf_clist_button_press_event' : (on_generic_clist_button_press_event, on_bindconf_edit_button_clicked),
        'on_bindconf_add_button_clicked' : on_bindconf_add_button_clicked,
        'on_bindconf_edit_button_clicked' : on_bindconf_edit_button_clicked,
        'on_bindconf_delete_button_clicked' : on_bindconf_delete_button_clicked,

        "on_fwd_zone_rbutton_toggled" : on_zone_type_toggled,
        "on_rev_zone_rbutton_toggled" : on_zone_type_toggled,
        "on_slave_zone_rbutton_toggled" : on_zone_type_toggled,

        #slave
        'on_slave_zone_masters_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9. ]+$'),
        'on_slave_zone_file_entry_insert_text' : (on_generic_entry_insert_text, r'^[^/ ]+$'),

        #rev
        'on_rev_zone_changed':on_rev_zone_changed,
        'on_rev_zone_clist_select_row' : (on_generic_clist_select_row, xml.get_widget ('rev_zone_edit_button'), xml.get_widget ('rev_zone_delete_button')),
        'on_rev_zone_clist_unselect_row' : (on_generic_clist_unselect_row, xml.get_widget ('rev_zone_edit_button'), xml.get_widget ('rev_zone_delete_button')),
        'on_rev_zone_clist_button_press_event' : (on_generic_clist_button_press_event, on_rev_zone_edit_button_clicked),
        'on_rev_zone_name_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9.]+$'),
        'on_rev_zone_name_entry_changed' : on_rev_zone_name_entry_changed,
        'on_rev_zone_pns_changed' : on_rev_zone_pns_changed,
        'on_rev_zone_file_entry_insert_text' : (on_generic_entry_insert_text, r'^[^/ ]+$'),
        'on_rev_zone_soa_button_clicked':on_rev_zone_soa_button_clicked,
        'on_rev_zone_add_button_clicked' : on_rev_zone_add_button_clicked,
        'on_rev_zone_edit_button_clicked' : on_rev_zone_edit_button_clicked,
        'on_rev_zone_delete_button_clicked' : on_rev_zone_delete_button_clicked,

        'on_rev_master_ns_clist_select_row' : (on_generic_clist_select_row, xml.get_widget ('rev_master_ns_edit_button'), xml.get_widget ('rev_master_ns_delete_button')),
        'on_rev_master_ns_clist_unselect_row' : (on_generic_clist_unselect_row, xml.get_widget ('rev_master_ns_edit_button'), xml.get_widget ('rev_master_ns_delete_button')),
        'on_rev_master_ns_clist_button_press_event' : (on_generic_clist_button_press_event, on_rev_master_ns_edit_button_clicked),
        'on_rev_master_ns_add_button_clicked' : on_rev_master_ns_add_button_clicked,
        'on_rev_master_ns_edit_button_clicked' : on_rev_master_ns_edit_button_clicked,
        'on_rev_master_ns_delete_button_clicked' : on_rev_master_ns_delete_button_clicked,

        #fwd
        'on_fwd_zone_set_button_clicked' : on_fwd_zone_set_button_clicked,
        'on_fwd_zone_soa_button_clicked' : on_fwd_zone_soa_button_clicked,
        'on_fwd_zone_name_entry_changed' : on_fwd_zone_name_entry_changed,
        'on_fwd_zone_file_entry_insert_text' : (on_generic_entry_insert_text, r'^[^/ ]+$'),
        'on_fwd_zone_file_entry_changed' : on_fwd_zone_changed,
        'on_fwd_zone_clist_select_row' : on_fwd_zone_clist_select_row,
        'on_fwd_zone_clist_button_press_event' : (on_generic_clist_button_press_event, on_fwd_zone_edit_button_clicked),
        'on_fwd_zone_add_button_clicked' : on_fwd_zone_add_button_clicked,
        'on_fwd_zone_edit_button_clicked' : on_fwd_zone_edit_button_clicked,
        'on_fwd_zone_delete_button_clicked' : on_fwd_zone_delete_button_clicked,
        'on_fwd_zone_contact_entry_changed' : on_fwd_zone_changed,
        'on_soa_refresh_entry_changed' : zone_changed,
        'on_soa_retry_entry_changed' : zone_changed,
        'on_soa_expire_entry_changed' : zone_changed,
        'on_soa_minimum_entry_changed' : zone_changed,
        'on_serial_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9]+$'),
        'on_soa_refresh_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9]+$'),
        'on_soa_retry_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9]+$'),
        'on_soa_expire_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9]+$'),
        'on_soa_minimum_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9]+$'),

        'on_mx_priority_entry_insert_text' : (on_generic_entry_insert_text, r'^[0-9]+$'),
        'on_mx_button_toggled' : on_mx_button_toggled,
        'on_rr_toggled' : on_rr_toggled,

        'on_a_clist_select_row' : (on_generic_clist_select_row, xml.get_widget ('a_edit_button'), xml.get_widget ('a_delete_button')),
        'on_a_clist_unselect_row' : (on_generic_clist_unselect_row, xml.get_widget ('a_edit_button'), xml.get_widget ('a_delete_button')),
        'on_a_clist_button_press_event' : (on_generic_clist_button_press_event, on_alias_edit_button_click),
        'on_zone_ns_clist_select_row' : (on_generic_clist_select_row, xml.get_widget ('zone_ns_edit_button'), xml.get_widget ('zone_ns_delete_button')),
        'on_zone_ns_clist_unselect_row' : (on_generic_clist_unselect_row, xml.get_widget ('zone_ns_edit_button'), xml.get_widget ('zone_ns_delete_button')),
        'on_zone_ns_clist_button_press_event' : (on_generic_clist_button_press_event, on_zone_ns_edit_button_clicked),
        'on_zone_ns_add_button_clicked' : on_zone_ns_add_button_clicked,
        'on_zone_ns_edit_button_clicked' : on_zone_ns_edit_button_clicked,
        'on_zone_ns_delete_button_clicked' : on_zone_ns_delete_button_clicked,
        'on_zone_mx_clist_select_row' : (on_generic_clist_select_row, xml.get_widget ('zone_mx_edit_button'), xml.get_widget ('zone_mx_delete_button')),
        'on_zone_mx_clist_unselect_row' : (on_generic_clist_unselect_row, xml.get_widget ('zone_mx_edit_button'), xml.get_widget ('zone_mx_delete_button')),
        'on_zone_mx_clist_button_press_event' : (on_generic_clist_button_press_event, on_zone_mx_edit_button_clicked),
        'on_zone_mx_add_button_clicked' : on_zone_mx_add_button_clicked,
        'on_zone_mx_edit_button_clicked' : on_zone_mx_edit_button_clicked,
        'on_zone_mx_delete_button_clicked' : on_zone_mx_delete_button_clicked,

        'hostname_check' : (on_generic_entry_insert_text, hostname_pattern)

        })
    setup_main_tree ()
    setup_dialogs ()
    xml.get_widget ('bindconf').show_all ()
    gtk.mainloop()

# make ctrl-C work
if __name__ == "__main__":
    signal.signal (signal.SIGINT, signal.SIG_DFL)
    main ()
