#!/usr/bin/python
# -*- coding: UTF-8 -*-
#    TcosConfigurator version __VERSION__
#
# Copyright (c) 2006-2011 Mario Izquierdo <mariodebian@gmail.com>
#
# This package is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# This package is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import sys
import os
import glob

# for get_ip_address
import socket
import fcntl
import struct
import netifaces
#####################

import pygtk
pygtk.require('2.0')
from gtk import *
import gtk.glade

import time
import getopt
from gettext import gettext as _
from gettext import bindtextdomain, textdomain
from locale import setlocale, LC_ALL

from subprocess import Popen, PIPE, STDOUT
from threading import Thread

import netifaces

import pwd

import gobject

#import threading
gtk.gdk.threads_init()
gobject.threads_init()



########################################################
#
#  Extends python-configobj this class use '=' instead of  ' = '
#
########################################################
from configobj import ConfigObj
class MyConfigObj (ConfigObj):

    def _write_line(self, indent_string, entry, this_entry, comment):
        """Write an individual line, for the write method"""
        # NOTE: the calls to self._quote here handles non-StringType values.
        if not self.unrepr:
            val = self._decode_element(self._quote(this_entry))
        else:
            val = repr(this_entry)
        return '%s%s%s%s%s' % (
            indent_string,
            self._decode_element(self._quote(entry, multiline=False)),
            self._a_to_u('='),
            val,
            self._decode_element(comment))
#############################################################


debug=False
SIMULATE=False
PACKAGE="tcos-configurator"

# if exec from svn or sources dir
if os.path.isfile('./setup.py'):
    LOCALE_DIR = "./po/"
    UI_DIR = "./"
    IMG_DIR = "./images/"
    print "exec in sources dir"
else:
    UI_DIR = "/usr/share/tcos-configurator/"
    IMG_DIR = "/usr/share/tcos-configurator/images/"
    LOCALE_DIR = "/usr/share/locale/"

def print_debug(txt):
    if debug:
        print >> sys.stderr, "%s::%s" %("tcos-configurator", txt)
    return

def usage():
    print "tcos-configurator help:"
    print ""
    print "   tcos-configurator -d [--debug]     (write debug data to stdout)"
    print "   tcos-configurator -s [--simulate]  (dry-run)"
    print "   tcos-configurator -h [--help]      (this help)"


try:
    opts, args = getopt.getopt(sys.argv[1:], ":hds", ["help", "debug", "simulate"])
except getopt.error, msg:
    print msg
    print "for command line options use tcosconfig --help"
    sys.exit(2)

# process options
for o, a in opts:
    if o in ("-d", "--debug"):
        print "DEBUG ACTIVE"
        debug = True
    if o in ("-s", "--simulate"):
        print "SIMULATE"
        SIMULATE = True
    if o in ("-h", "--help"):
        usage()
        sys.exit()

################################################################################
DHCP_CONF=[
['# dhcpd.conf',"# generated by __PROGRAM__ on date __DATE__"] ,

['ddns-update-style ad-hoc;',
    'option subnet-mask __MASK__;',
    '#option domain-name "tcos-domain.org";',
    '# set authorative to be first providing DHCP lease',
    'authorative;',
    'option option-128 code 128 = string;',
    'option option-129 code 129 = text;',
    'get-lease-hostnames true;',
],

['shared-network TCOS {',
    "\t\tsubnet __NET__ netmask __MASK__ {",
    "\t\toption domain-name-servers __DNS__;",
    "\t\toption broadcast-address __BROADCAST__;",
    "\t\toption routers __ROUTERS__;",
    "\t\tnext-server __SERVERIP__;",
    "\t\trange dynamic-bootp __STARTIP__ __ENDIP__;",
    "\t\tfilename \"__BOOTMODE__\";",
  "\t}",
"}"
],
]

TFTPBOOT="/var/lib/tftpboot"
if os.path.isdir("/var/lib/max-netboot"):
    TFTPBOOT="/var/lib/max-netboot"

DNSMASQ_CONF=[
['# dnsmasq.conf for TCOS', '# file generated by __PROGRAM__ on date __DATE__\n'],

['## put your ISP DNS server in this file',
'resolv-file=/etc/resolv.conf.real\n'],

['listen-address=__SERVERIP__,127.0.0.1\n',
    'dhcp-range=__STARTIP__,__ENDIP__,12h\n',
    'dhcp-option=option:router,__ROUTERS__',
    'dhcp-option=option:tftp-server,__SERVERIP__',
    'dhcp-boot=__BOOTMODE__,__SERVERIP__,__SERVERIP__\n\n',
    '# option 49 x-display-manager',
    'dhcp-option-force=49,__SERVERIP__',
    '# option 48 font-servers',
    'dhcp-option-force=48,__SERVERIP__',
    '# option 16 swap-server',
    'dhcp-option-force=16,__SERVERIP__\n\n',
],

['enable-tftp',
'tftp-root=%s'%TFTPBOOT,
],

['dhcp-authoritative\n\n',
'## uncomment this for DHCP debug',
'#log-dhcp',
'## uncomment this for DNS querys debug',
'#log-queries\n\n',
'## example reserved lease',
'#dhcp-host=00:11:22:33:44:55,192.168.0.101'],
]

# this settings could be more precise but work for
# 15-20 thin clients without problems
#Enable=true
#MaxSessions=40
#MaxPending=60
#MaxPendingIndirect=30
#MaxWait=75
#MaxWaitIndirect=80
#PingIntervalSeconds=35
#DisplaysPerHost=4
GDM_CONFIG={
"xdmcp":
    {"Enable":"true",
    "MaxSessions":"30",
    "MaxSessions":"40",
    "MaxPending":"60",
    "MaxPendingIndirect":"30",
    "MaxWait":"75",
    "MaxWaitIndirect":"80",
    "PingIntervalSeconds":"35",
    "DisplaysPerHost":"4"},
"security":
    {"AllowRemoteAutoLogin":"true",
    "DisallowTCP":"false"},
"daemon":
    {"TimedLoginEnable":"__AUTOLOGIN__",
    "TimedLogin":"/usr/sbin/tcos-gdm-autologin|",
    "TimedLoginDelay":"__TIMEOUT__"},
}

GDM_CONF_FILE="/etc/gdm/gdm.conf"
# try to edit correct file (Ubuntu use -custom file)
if os.path.isfile(GDM_CONF_FILE + "-custom"):
    GDM_CONF_FILE=GDM_CONF_FILE+"-custom"

if os.path.isfile("/etc/gdm/gdm-cdd.conf"):
    GDM_CONF_FILE="/etc/gdm/gdm-cdd.conf"

# in new Ubuntu create empty file and use it
if os.path.isfile("/etc/gdm/gdm.schemas") and not os.path.isfile("/etc/gdm/custom.conf"):
    # create empty file
    f=open("/etc/gdm/custom.conf", 'w')
    f.write('\n')
    f.close()

# new GDM in Ubuntu Karmic
if os.path.isfile("/etc/gdm/custom.conf"):
    GDM_CONF_FILE="/etc/gdm/custom.conf"

# in Debian unstable GDM is installed in /etc/gdm3 create empty file and use it
if os.path.isdir("/etc/gdm3") and not os.path.isfile("/etc/gdm3/daemon.conf"):
    # create empty file
    f=open("/etc/gdm3/daemon.conf", 'w')
    f.write('\n')
    f.close()

# new GDM in Debian unstable
if os.path.isfile("/etc/gdm3/daemon.conf"):
    GDM_CONF_FILE="/etc/gdm3/daemon.conf"


KDM_CONFIG={
"Xdmcp":[
         {"Enable":"true"}
        ]
}
KDM_CONF_FILE="/etc/kde4/kdm/kdmrc"
KDM_ACCESS_FILE="/etc/kde4/kdm/Xaccess"
KDM_ACCESS_LINE="#any host can get a login window"

LIGHTDM_CONFIG={
"XDMCPServer":[
         {"enabled":"true"}
              ]
}
LIGHTDM_CONF_FILE="/etc/lightdm/lightdm.conf"


HIDDEN_INTERFACES=['lo', 'pan0', 'sit0']

DNSMASQ_FILE="/etc/dnsmasq.conf"
NETWORK_INTERFACES="/etc/network/interfaces"
RESOLV_CONF_REAL="/etc/resolv.conf.real"

if SIMULATE:
    GDM_CONF_FILE="./" + os.path.basename(GDM_CONF_FILE)
    DNSMASQ_FILE="./dnsmasq.conf"
    NETWORK_INTERFACES="./interfaces"
    RESOLV_CONF_REAL="./resolv.conf.real"

################################################################################

class TcosStandalone:
    def __init__(self):
        print_debug("__init__()")
        
        # vars
        self.v={}
        self.begin_usernumber=1
        gtk.glade.bindtextdomain(PACKAGE, LOCALE_DIR)
        gtk.glade.textdomain(PACKAGE)

        # gettext support
        setlocale( LC_ALL )
        bindtextdomain( PACKAGE, LOCALE_DIR )
        textdomain( PACKAGE )
        
        if os.path.isdir('/usr/share/backharddi') or os.path.isdir('/usr/share/backharddi-ng'):
            self.BOOT_MODES=[
                    [_("TCOS and Backharddi"), "/pxelinux.0" ],
                    [_("TCOS"),           "/tcos/pxelinux.0" ],
                    [_("Backharddi"),     "/backharddi/pxelinux.0"],
                ]
        else:
            self.BOOT_MODES=[
                    [_("TCOS"),           "/tcos/pxelinux.0" ]
                ]
        
        
        # Widgets
        self.ui = gtk.Builder()
        self.ui.set_translation_domain(PACKAGE)
        self.ui.add_from_file(UI_DIR + 'tcos-configurator.ui')
        
        self.mainwindow = self.ui.get_object('mainwindow')
        self.mainwindow.set_icon_from_file(IMG_DIR +'tcos-icon-32x32.png')
        
        # close windows signals
        self.mainwindow.connect('destroy', self.quitapp )
        self.mainwindow.connect('delete_event', self.quitapp)
        
        self.button_quit=self.ui.get_object("btn_quit")
        self.button_quit.connect('clicked', self.quitapp)
        
        self.window_autologin_help=self.ui.get_object('window_autologin_help')
        self.window_autologin_help.set_icon_from_file(IMG_DIR +'tcos-icon-32x32.png')
        self.window_autologin_help.connect('destroy', self.hidehelp )
        self.window_autologin_help.connect('delete_event', self.hidehelp)
        
        # widgets
        self.w={}
        for widget in ['img_logo', 'combo_interfaces', 'txt_serverip', 'txt_startip', 
                       'txt_endip', 'btn_configure_dhcp', 'combo_boot_mode', 'hbox_dynamic',
                       'ck_static', 'btn_hostname_help', 'txt_hostname_prefix', 'lbl_dhcp', 'label_boot_mode']:
            self.w[widget]=self.ui.get_object(widget)
        
        self.w['lbl_dhcp'].set_text("")
        self.w['img_logo'].set_from_file(IMG_DIR + 'tcos-logo.png')
        self.w['combo_interfaces'].connect('changed', self.combo_interface_change )
        self.w['btn_configure_dhcp'].connect('clicked', self.on_btn_configure_dhcp)
        self.w['txt_serverip'].connect('focus-out-event', self.on_serverip_blur )
        self.w['btn_hostname_help'].connect('clicked', self.on_btn_autologin_help)
        
        
        self.populate_select(self.w['combo_boot_mode'], self.BOOT_MODES)
        self.set_active_in_select(self.w['combo_boot_mode'], self.BOOT_MODES[0][0])
        if len(self.BOOT_MODES) < 2:
            self.w['combo_boot_mode'].hide()
            self.w['label_boot_mode'].hide()
        
        self.interfaces=self.getNetInterfaces()
        self.configured_interfaces=self.read_etc_network_interfaces()
        print_debug("self.configured_interfaces=%s len=%s"%(self.configured_interfaces, len(self.configured_interfaces)))
        
        
        self.populate_select(self.w['combo_interfaces'],  self.interfaces )
        if len(self.interfaces) == 1:
            self.set_active_in_select(self.w['combo_interfaces'], self.interfaces[0][0])
        
        for widget in ['txt_serverip', 'txt_startip', 'txt_endip', 'combo_boot_mode']:
            self.w[widget].connect('changed', self.set_dhcp_modified)
        
        if len(self.configured_interfaces) < 2:
            self.w['hbox_dynamic'].show()
        self.dhcp_modified=False
        
        
        print_debug("configured_interfaces %s"%self.configured_interfaces)
        print_debug("detected_interfaces %s"%self.interfaces)
        
        # users
        for widget in ['scale_number_users', 'txt_prefix', 'txt_groups', 'lbl_users', 'btn_commit_users']:
            self.w[widget]=self.ui.get_object(widget)
        
        self.w['txt_prefix'].connect('changed', self.on_scale_number_users)
        
        self.w['scale_number_users'].connect('value_changed', self.on_scale_number_users)
        self.on_scale_number_users(self.w['scale_number_users'])
        
        self.w['btn_commit_users'].set_sensitive(False)
        self.w['btn_commit_users'].connect('clicked', self.on_btn_commit_users)
        
        
        # remote login
        for widget in ['ck_remotelogin', 'ck_autologin', 'scale_autologin', 
                       'btn_commit_login', 'btn_autologin_help', 'lbl_login',
                       'btn_launch_tcosconfig']:
            self.w[widget]=self.ui.get_object(widget)
        
        
        # read GDM_CONF_FILE
        config=MyConfigObj( GDM_CONF_FILE )
        try:
            if config['xdmcp']['Enable'] == 'true':
                self.w['ck_remotelogin'].set_active(True)
        except: pass
        
        try:
            if config['daemon']['TimedLogin'] == '/usr/sbin/tcos-gdm-autologin|':
                self.w['ck_autologin'].set_active(True)
        except: pass
        
        try:
            if config['daemon']['TimedLoginDelay'] != '10':
                self.w['scale_autologin'].set_value( int(config['daemon']['TimedLoginDelay']) )
        except: pass
        
        self.w['ck_remotelogin'].connect('toggled', self.on_ck_remotelogin)
        self.w['ck_autologin'].connect('toggled', self.on_ck_remotelogin)
        self.w['scale_autologin'].connect('value_changed', self.on_ck_remotelogin)
        self.w['btn_commit_login'].connect('clicked', self.on_btn_commit_login)
        
        self.w['btn_autologin_help'].connect('clicked', self.on_btn_autologin_help)
        
        self.w['btn_launch_tcosconfig'].connect('clicked', self.on_btn_launch_tcosconfig)
        
        
    def read_mayor_generated_users(self, prefix):
        if prefix == '': return
        tmpusers=[]
        maxu=0
        # read generated users
        for user in pwd.getpwall():
            if user[0].startswith(prefix):
                u=user[0].replace(prefix,'')
                try:
                    i=int(u)
                    if i> maxu: maxu=i
                except: pass
        self.begin_usernumber=maxu+1
        print_debug("read_mayor_generated_users() begin=%s"%self.begin_usernumber)
        return "%02d"%maxu

    def on_serverip_blur(self, widget, event):
        value=widget.get_text()
        if value == "": return
        self.w['txt_startip'].set_text(".".join(value.split('.')[0:3]) + ".101")
        self.w['txt_endip'].set_text(  ".".join(value.split('.')[0:3]) + ".131")
        self.w['lbl_dhcp'].set_text("")

    def get_ip_from_iface(self, seliface):
        print_debug("get_ip_from_iface(%s) self.configured_interfaces=%s"%(seliface, self.configured_interfaces))
        if not self.configured_interfaces.has_key(seliface):
            return None
        if not self.configured_interfaces[seliface].has_key('address'):
            return None
        return self.configured_interfaces[seliface]['address']

    def combo_interface_change(self, widget):
        configured=False
        seliface=self.read_select_value(widget)
        for iface in self.interfaces:
            if iface[0] != seliface:
                #print_debug("combo_interface_change(%s) iface(%s) != seliface and iface != None"%(seliface, iface[0]))
                continue
            
            iface_ip=self.get_ip_from_iface(iface[0])
            print_debug("combo_interface_change(%s) static iface=%s seliface=%s iface_ip=%s"%(seliface, iface, seliface, iface_ip))
            print_debug("combo_interface_change(%s) self.interfaces=%s"%(seliface, self.interfaces))
            
            print_debug("combo_interface_change() iface[1]=%s iface_ip=%s"%(iface[1], iface_ip))
            
            if iface_ip:
                self.w['lbl_dhcp'].hide()
                self.w['hbox_dynamic'].hide()
                self.w['ck_static'].set_active(False)
                print_debug("combo_interface_change(%s) iface=%s HAVE IP"%(seliface, iface) )
            else:
                self.w['hbox_dynamic'].show()
                self.w['ck_static'].set_active(True)
                self.w['lbl_dhcp'].set_markup( _("WARNING:\n<b>Network interface don't have IP</b>") )
                self.w['lbl_dhcp'].show()
                self.w['txt_serverip'].set_text('')
                self.w['txt_startip'].set_text('')
                self.w['txt_endip'].set_text('')
                print_debug("combo_interface_change(%s) iface=%s DON'T HAVE IP" %(seliface, iface) )
            
            if iface[1]:
                self.w['txt_startip'].set_text(".".join(iface[1].split('.')[0:3]) + ".101")
                self.w['txt_endip'].set_text(  ".".join(iface[1].split('.')[0:3]) + ".131")
                self.w['txt_serverip'].set_text(iface[1])
                self.set_dhcp_modified()

    def set_dhcp_modified(self, *args):
        self.dhcp_modified=True
        self.w['btn_configure_dhcp'].set_sensitive(True)

    def on_btn_configure_dhcp(self, *args):
        if os.path.isfile('/usr/sbin/dnsmasq'):
            if not self.ask_msg( _("Your %s file will be overwritten.\n\nContinue?") %"/etc/dnsmasq.conf" ):
                return
            th=Thread(target=self.configureDNSMASQ)
            th.start()
        else:
            if not self.ask_msg( _("Your %s file will be overwritten.\n\nContinue?") %"/etc/dhcp3/dhcpd.conf" ):
                return
            th=Thread(target=self.configureDHCP)
            th.start()

    def hidehelp(self, *args):
        self.window_autologin_help.hide()
        return True

    def on_btn_autologin_help(self, widget):
        self.window_autologin_help.show()
##############    login #######################################################

    def on_ck_remotelogin(self, widget):
        if self.w['ck_remotelogin'].get_active():
            self.w['ck_autologin'].set_sensitive(True)
            self.w['scale_autologin'].set_sensitive(True)
        else:
            self.w['ck_autologin'].set_sensitive(False)
            self.w['scale_autologin'].set_sensitive(False)
        self.w['btn_commit_login'].set_sensitive(True)

    def on_btn_commit_login(self, widget):
        if self.w['ck_remotelogin'].get_active():
            self.enable_remotelogin()
        else:
            self.disable_remotelogin()
        self.w['btn_commit_login'].set_sensitive(False)

    def enable_remotelogin(self):
        if os.path.isfile(GDM_CONF_FILE):
            self.SetVar("xdmcp", "Enable", "true")
            
            for gdmvar in GDM_CONFIG['xdmcp']:
                self.SetVar('xdmcp', gdmvar, GDM_CONFIG['xdmcp'][gdmvar])
                
            if os.path.exists("/usr/lib/gdm/gdmgreeter"):
                self.SetVar("daemon", "RemoteGreeter", "/usr/lib/gdm/gdmgreeter")
            self.SetVar("daemon", "TimedLogin", "/usr/sbin/tcos-gdm-autologin|")
            
            if self.w['ck_autologin'].get_active():
                self.SetVar("daemon", "TimedLoginEnable", "true")
                self.SetVar("security", "AllowRemoteAutoLogin", "true")
            else:
                self.SetVar("daemon", "TimedLoginEnable", "false")
                self.SetVar("security", "AllowRemoteAutoLogin", "false")
            self.SetVar("daemon", "TimedLoginDelay", str( int(self.w['scale_autologin'].get_value()) ) )
            
        if os.path.isfile(KDM_CONF_FILE):
            self.SetVar("Xdmcp", "Enable", "true", CONF=KDM_CONF_FILE)
            # configure Xaccess
            f=open(KDM_ACCESS_FILE, 'r')
            data=f.readlines()
            f.close()
            for i in range(len(data)):
                line=data[i]
                if KDM_ACCESS_LINE in line:
                    data[i]=line.replace('#*', '*')
            f=open(KDM_ACCESS_FILE, 'w')
            for line in data:
                print_debug("enable_remotelogin() KDM_ACCESS_FILE: %s"%line.replace('\n','') )
                f.write(line)
            f.close()
        
        if os.path.isfile(LIGHTDM_CONF_FILE):
            self.SetVar("XDMCPServer", "enabled", "true", CONF=LIGHTDM_CONF_FILE)

        # show message to reboot required
        self.w['lbl_login'].set_markup( _("<b>Reboot required (or restart gdm/kdm/lightdm daemon) to enable new GDM/KDM/LightDM settings</b>") )
        self.w['lbl_login'].show()
        return

    def disable_remotelogin(self):
        if os.path.isfile(KDM_CONF_FILE):
            self.SetVar("Xdmcp", "Enable", "false", CONF=KDM_CONF_FILE)
        self.SetVar("xdmcp","Enable","false")

        if os.path.isfile(LIGHTDM_CONF_FILE):
            self.SetVar("XDMCPServer", "enabled", "false", CONF=LIGHTDM_CONF_FILE)

    def SetVar(self, section, key, value, do=True, CONF=GDM_CONF_FILE):
        if not do:
            print_debug("NOACTION: SetVar() gdm.conf=%s section=%s key=%s value=%s" 
                        %(CONF, section,key,value) )
            return
            
        config=MyConfigObj( os.path.realpath(CONF) )
        print_debug("setting conf=%s section=[%s] key=%s value=%s" %(CONF, section, key, value) )
        if not config.has_key(section):
            config[section]={}
        config[section][key] = value
        try:
            config.write()
            return True
        except:
            print_debug("Error, can't write in %s" %(CONF))
            return False

############## users ##########################################################
    def on_scale_number_users(self, widget):
        num=int(self.w['scale_number_users'].get_value())
        prefix=self.w['txt_prefix'].get_text()
        if prefix=='':
            self.w['btn_commit_users'].set_sensitive(False)
            return
        self.read_mayor_generated_users(prefix)
        init=self.begin_usernumber
        end=self.begin_usernumber+num
        self.w['lbl_users'].set_text(_("Creating from %(prefix)s%(init)02d to %(prefix)s%(end)02d") %{"prefix":prefix, "init":init, "end":end} )
        self.w['lbl_users'].show()
        self.w['btn_commit_users'].set_sensitive(True)

    def on_btn_commit_users(self, widget):
        th=Thread(target=self.createUsers)
        th.start()


    def userExists(self, username):
        try:
            pwd.getpwnam(username)
            return True
        except:
            return False

    def createUsers(self):
        number=int(self.w['scale_number_users'].get_value())
        user_prefix=self.w['txt_prefix'].get_text()
        groups=self.w['txt_groups'].get_text()
        
        gtk.gdk.threads_enter()
        self.w['btn_commit_users'].set_sensitive(False)
        self.w['lbl_users'].set_text("")
        gtk.gdk.threads_leave()
        
        for i in range(self.begin_usernumber, self.begin_usernumber+number):
            # range starts in 1
            i="%02d"%(i)
            username="%s%s"%(user_prefix, i)
            if not self.userExists(username):
                print_debug("Creating username %s"%username)
                gtk.gdk.threads_enter()
                self.w['lbl_users'].set_text( _("Creating user: '%s'") %username )
                gtk.gdk.threads_leave()
                self.exe_cmd("useradd -m %s -p%s -s /bin/bash -d /home/%s" %(username, username, username))
                self.exe_cmd("echo %s:%s | chpasswd" %(username, username))
                self.exe_cmd("adduser %s fuse" %(username) )
            else:
                print_debug("User already exists: %s"%username)
                gtk.gdk.threads_enter()
                self.w['lbl_users'].set_text( _("User already exists: '%s'") %username )
                gtk.gdk.threads_leave()
                time.sleep(1)
        
        self.read_mayor_generated_users(user_prefix)
        gtk.gdk.threads_enter()
        self.w['lbl_users'].set_markup( _("<b>Done</b>") )
        gtk.gdk.threads_leave()

######################network ##########################################


    def get_ip_address(self, ifname):
        print_debug("get_ip_address() ifname=%s" %(ifname) )
        if not ifname in netifaces.interfaces():
            return None
        ip=netifaces.ifaddresses(ifname)
        if ip.has_key(netifaces.AF_INET):
            print_debug("get_ip_address(%s)=%s"%(ifname, ip[netifaces.AF_INET][0]))
            return ip[netifaces.AF_INET][0]['addr']
        return None


    def getNetInterfaces(self):
        interfaces=[]
        for dev in netifaces.interfaces():
            if not dev in HIDDEN_INTERFACES and not dev.startswith("vbox") and not dev.startswith("vmnet") and not dev.startswith("wmaster"):
                ip=self.get_ip_address(dev)
                print_debug("getNetInterfaces() iface=%s data=%s"%(dev,ip))
                interfaces.append( [dev, ip] )
        return interfaces

    def read_etc_network_interfaces(self):
        interfaces={}
        curiface=None
        try:
            f=open(NETWORK_INTERFACES,'r')
        except:
            print_debug("Error, can't read " + NETWORK_INTERFACES)
            return interfaces
        data=f.readlines()
        f.close()
        for line in data:
            line=line.strip()
            if len(line) == 0: continue
            if line.startswith('#'): continue
            if line.startswith("iface"):
                curiface=line.split()[1]
                interfaces[curiface]={}
            if curiface and line.startswith('address'):
                interfaces[curiface]['address']=line.split()[1]
            if curiface and line.startswith('netmask'):
                interfaces[curiface]['netmask']=line.split()[1]
            if curiface and line.startswith('gateway'):
                interfaces[curiface]['gateway']=line.split()[1]
            if curiface and line.startswith('network'):
                interfaces[curiface]['network']=line.split()[1]
            if curiface and line.startswith('broadcast'):
                interfaces[curiface]['broadcast']=line.split()[1]
            if curiface and line.startswith('dns-nameservers'):
                interfaces[curiface]['dns-nameservers']=line.split()[1:]
        print_debug("read_etc_network_interfaces() %s"%interfaces)
        return interfaces
                

    def configure_static(self, data):
        print_debug("configure_static() data=%s"%data)
        newdata=[]
        curiface=None
        added=False
        try:
            f=open(NETWORK_INTERFACES,'r')
        except:
            print_debug("Error, can't read "+NETWORK_INTERFACES)
            return False
        ifile=f.readlines()
        f.close()
        for line in ifile:
            sline=line.strip()
            if sline.startswith('iface'):
                curiface=sline.split()[1]
            if not added and curiface and curiface == data['iface']:
                newdata.append("# added by tcos-configurator")
                newdata.append("auto %s" %curiface)
                newdata.append("iface %s inet static" %curiface)
                for opt in data:
                    if opt == 'iface': continue
                    #if opt == 'gateway': continue
                    newdata.append("\t%s %s" %(opt, data[opt]) )
                newdata.append("\n\n")
                added=True
            if added and curiface == data['iface']:
                continue
            if (sline.startswith('auto') or sline.startswith('allow') ) and sline.split()[1] == data['iface']:
                continue 
                    
            newdata.append(line.replace('\n','') )
        
        if not added:
            newdata.append("\n#added by tcos-configurator")
            newdata.append("auto %s"%data['iface'])
            newdata.append("iface %s inet static"%data['iface'])
            newdata.append("\taddress %s"%data['address'])
            newdata.append("\tnetmask %s"%data['netmask'])
            if data['gateway'] and ".".join(data['gateway'].split('.')[0:3]) == ".".join(data['address'].split('.')[0:3]):
                # only save gateway if is in the same network
                newdata.append("\tgateway %s"%data['gateway'])
            newdata.append("\n\n")
        print_debug(newdata)
        try:
            f=open(NETWORK_INTERFACES, 'w')
        except:
            return False
        for line in newdata:
            f.write(line + "\n")
        f.close()
        return True

################### combo stuff ##############################

    def populate_select(self, widget, values):
        valuelist = gtk.ListStore(str)
        for value in values:
            valuelist.append([value[0]])
        widget.set_model(valuelist)
        #widget.set_text_column(0)
        if widget.get_text_column() != 0:
            widget.set_text_column(0)
        model=widget.get_model()
        return

    def set_active_in_select(self, widget, default):
        model=widget.get_model()
        for i in range(len(model)):
            if model[i][0] == default:
                print_debug ("set_active_in_select() default is '%s', index %d" %( model[i][0] , i ) )
                widget.set_active(i)
        return

    def read_select_value(self, widget):
        selected=-1
        try:
            selected=widget.get_active()
        except:
            print_debug ( "read_select() ERROR reading " )
        model=widget.get_model()
        value=model[selected][0]
        print_debug ( "read_select() reading %s" %(value) )
        return value

################################################################################
    def parse_dhcp(self, line, options):
        for option in options:
            print_debug("line %s replace %s with %s"%(line, option, options[option]))
            line=line.replace(option, options[option])
        print_debug(line)
        return line

    def write_into_etc_host(self, newline, noaction=False):
        ip=newline[0]
        hostname=newline[1]
        # check if exists
        f=open("/etc/hosts", "r")
        data=f.readlines()
        f.close()
        for line in data:
            line=line.replace('\n','')
            if ip + " " in line or ip + '\t' in line:
                print_debug ( "IP %s is in /etc/hosts" %(ip) )
                return True
        try:
            print_debug ("Adding %s %s" %(ip, hostname) )
            if noaction:
                print_debug("NOACTION: AddHost() hostname=%s, ip=%s" %(hostname,ip) )
            else:
                f=open("/etc/hosts", "a")
                f.write("%s\t%s\n" %(ip, hostname) )
                f.close()
                return True
        except Exception, err:
            print "Error '%s' editting /etc/hosts, are you root?"%err
            return False

    def configureDNSMASQ(self, *args):
        # get data
        gtk.gdk.threads_enter()
        self.w['btn_configure_dhcp'].set_sensitive(False)
        self.w['lbl_dhcp'].set_text( _("Configuring DNSMASQ service...") )
        self.w['lbl_dhcp'].show()
        gtk.gdk.threads_leave()
        
        boot_mode=self.read_select_value(self.w['combo_boot_mode'])
        boot_filename="/pxelinux.0"
        for boot in self.BOOT_MODES:
            print_debug("boot[0]=%s boot_mode=%s"%(boot[0], boot_mode) )
            if boot[0] == boot_mode:
                boot_filename=boot[1]
                print_debug("configureDNSMASQ() setting boot filename to %s"%boot_filename)
        
        # get server_ip
        false_gateway=False
        server_iface=self.read_select_value(self.w['combo_interfaces'])
        if self.configured_interfaces.has_key(server_iface) and len(self.configured_interfaces[server_iface]) > 1:
            server_ip=self.configured_interfaces[server_iface]['address']
            try:
                gateway=self.configured_interfaces[server_iface]['gateway']
            except:
                gateway=server_ip
            netmask=self.configured_interfaces[server_iface]['netmask']
            if self.configured_interfaces[server_iface].has_key('network'):
                network=self.configured_interfaces[server_iface]['network']
            else:
                network=".".join( server_ip.split('.')[0:-1]) + ".0"
            if self.configured_interfaces[server_iface].has_key('broadcast'):
                broadcast=self.configured_interfaces[server_iface]['broadcast']
            else:
                broadcast=".".join( server_ip.split('.')[0:-1]) + ".255"
        else:
            print_debug("configureDNSMASQ() dynamic IP")
            server_ip=None
            for interface in self.interfaces:
                if interface[0] == server_iface:
                    server_ip=interface[1]
            if self.w['txt_serverip'].get_text() != '':
                server_ip=self.w['txt_serverip'].get_text()
            # gateway
            #gateway=self.exe_cmd("ip route| grep %s| awk '/^default/ {print $3}'"%server_iface)
            gateway=self.exe_cmd("/sbin/route -n| awk '/^0.0.0.0(.*)%s/ {print $2}'| head -1" %server_iface)
            if gateway == [] or gateway == "":
                print_debug("gateway not found, using server_ip")
                gateway=server_ip
                false_gateway=True
            else:
                print_debug("FOUNG gateway =%s"%gateway)
                false_gateway=False
            broadcast=".".join( server_ip.split('.')[0:-1]) + ".255"
            netmask="255.255.255.0"
            network=".".join( server_ip.split('.')[0:-1]) + ".0"
        
        dns=[]
        entry=""
        if os.path.exists('/etc/resolv.conf'):
            # read dns
            f=open("/etc/resolv.conf", 'r')
            for line in f.readlines():
                if line.startswith("nameserver") and len(dns) == 0:
                    entry=line.split(' ')[1].strip()
                    if entry != server_ip and entry != "127.0.0.1":
                        dns.append(entry)
            f.close()
        
        if len(dns) == 0:
            # use server_ip as DNS
            dns.append(server_ip)
        
        
        # generate /etc/resolv.conf.real with real DNS
        # FIXME, use OpenDNS if entry is not found?
        if entry != "" and entry != server_ip:
            f=open(RESOLV_CONF_REAL, 'w')
            f.write("nameserver %s\n"%entry)
            f.close()
        
        options={
        "__PROGRAM__":PACKAGE,
        "__DATE__":time.ctime(),
        "__MASK__":netmask,
        "__NET__":network,
        "__DNS__":dns[0],
        "__BROADCAST__":broadcast,
        "__ROUTERS__":gateway,
        "__SERVERIP__":server_ip,
        "__STARTIP__":self.w['txt_startip'].get_text(),
        "__ENDIP__":self.w['txt_endip'].get_text(),
        "__BOOTMODE__":boot_filename
        }
        # backup old conf file
        
        # generate /etc/dnsmasq.conf
        new_file=[]
        for block in DNSMASQ_CONF:
            for line in block:
                new_file.append(self.parse_dhcp(line, options))
        
        
        try:
            f=open(DNSMASQ_FILE, 'w')
            for line in new_file:
                f.write(line + "\n")
                print_debug(DNSMASQ_FILE  +":: %s"%line)
            f.close()
        except:
            # error writing
            gtk.gdk.threads_enter()
            self.w['lbl_dhcp'].set_markup( _("<b>Error writing /etc/dnsmasq.conf.</b>\nAre you root?") )
            gtk.gdk.threads_leave()
            time.sleep(4)
        
        
        # edit /etc/hosts
        ip_pref=".".join(self.w['txt_startip'].get_text().split('.')[0:3])
        ip_start=int(self.w['txt_startip'].get_text().split('.')[-1])
        ip_end=int(self.w['txt_endip'].get_text().split('.')[-1])
        txt_prefix=self.w['txt_hostname_prefix'].get_text()
        
        for ip in range ( (ip_end - ip_start) + 1 ):
            ip=ip_start + ip
            ipname=ip
            if ip > 100:
                ipname=ip-100
            if not SIMULATE:
                self.write_into_etc_host( ["%s.%d"%(ip_pref, ip), "%s%02d"%(txt_prefix, ipname) ]  )
                print_debug("write_into_etc_host() %s"%["%s.%d"%(ip_pref, ip), "%s%02d"%(txt_prefix, ipname) ] )
        
        
        # read ck_static
        data={
            "iface":server_iface,
            "address":server_ip,
            "gateway":gateway,
            "netmask":netmask,
            "network":network,
            "broadcast":broadcast,
            }
        if false_gateway:
            print_debug("configureDNSMASQ() remove false_gateway")
            data['gateway']=None
        if self.w['ck_static'].get_active():
            if self.configure_static(data):
                time.sleep(1)
                gtk.gdk.threads_enter()
                self.w['lbl_dhcp'].set_text( _("Configured static network") )
                gtk.gdk.threads_leave()
                #self.exe_cmd("/etc/init.d/networking restart")
                self.exe_cmd("ifdown --all --exclude=lo")
                self.exe_cmd("ifdown --all --exclude=lo")
                self.exe_cmd("ifdown --all --exclude=lo")
                time.sleep(2)
                self.exe_cmd("ifup --all --exclude=lo")
                #self.exe_cmd("ifconfig %s"%server_iface)
                gtk.gdk.threads_enter()
                self.w['lbl_dhcp'].set_markup( _("<b>Done</b>") )
                gtk.gdk.threads_leave()
                time.sleep(2)
            else:
                gtk.gdk.threads_enter()
                self.w['lbl_dhcp'].set_markup( _("ERROR:<b>Error configuring static network.</b>") )
                gtk.gdk.threads_leave()
                time.sleep(4)
        
        
        gtk.gdk.threads_enter()
        self.w['lbl_dhcp'].set_text( _("Restarting DNSMASQ service...") )
        gtk.gdk.threads_leave()
        
        fail=False
        if not SIMULATE:
            result=self.exe_cmd("/etc/init.d/dnsmasq restart")
            for line in result:
                if "fail" in line: fail=True
                if "OK" in line: fail=False
                if "done" in line: fail=False
        
        gtk.gdk.threads_enter()
        if not fail:
            self.w['lbl_dhcp'].set_markup( _("<b>Done</b>") )
        else:
            self.w['lbl_dhcp'].set_markup( _("<b>Error restarting DNSMASQ server.</b>") )
        gtk.gdk.threads_leave()


    def configureDHCP(self, *args):
        # get data
        gtk.gdk.threads_enter()
        self.w['btn_configure_dhcp'].set_sensitive(False)
        self.w['lbl_dhcp'].set_text( _("Configuring DHCP service...") )
        self.w['lbl_dhcp'].show()
        gtk.gdk.threads_leave()
        
        boot_mode=self.read_select_value(self.w['combo_boot_mode'])
        boot_filename="/pxelinux.0"
        for boot in self.BOOT_MODES:
            print_debug("boot[0]=%s boot_mode=%s"%(boot[0], boot_mode) )
            if boot[0] == boot_mode:
                boot_filename=boot[1]
                print_debug("configureDHCP() setting boot filename to %s"%boot_filename)
        
        # get server_ip
        false_gateway=False
        server_iface=self.read_select_value(self.w['combo_interfaces'])
        if self.configured_interfaces.has_key(server_iface) and len(self.configured_interfaces[server_iface]) > 1:
            server_ip=self.configured_interfaces[server_iface]['address']
            try:
                gateway=self.configured_interfaces[server_iface]['gateway']
            except:
                gateway=server_ip
            netmask=self.configured_interfaces[server_iface]['netmask']
            if self.configured_interfaces[server_iface].has_key('network'):
                network=self.configured_interfaces[server_iface]['network']
            else:
                network=".".join( server_ip.split('.')[0:-1]) + ".0"
            if self.configured_interfaces[server_iface].has_key('broadcast'):
                broadcast=self.configured_interfaces[server_iface]['broadcast']
            else:
                broadcast=".".join( server_ip.split('.')[0:-1]) + ".255"
        else:
            print_debug("configureDHCP() dynamic IP")
            server_ip=None
            for interface in self.interfaces:
                if interface[0] == server_iface:
                    server_ip=interface[1]
            if self.w['txt_serverip'].get_text() != '':
                server_ip=self.w['txt_serverip'].get_text()
            # gateway
            #gateway=self.exe_cmd("ip route| grep %s| awk '/^default/ {print $3}'"%server_iface)
            gateway=self.exe_cmd("/sbin/route -n| awk '/^0.0.0.0(.*)%s/ {print $2}'" %server_iface)
            if gateway == [] or gateway == "":
                print_debug("gateway not found, using server_ip")
                gateway=server_ip
                false_gateway=True
            else:
                print_debug("FOUNG gateway =%s"%gateway)
                false_gateway=False
            broadcast=".".join( server_ip.split('.')[0:-1]) + ".255"
            netmask="255.255.255.0"
            network=".".join( server_ip.split('.')[0:-1]) + ".0"
        
        dns=[]
        if os.path.exists('/etc/resolv.conf'):
            # read dns
            f=open("/etc/resolv.conf", 'r')
            for line in f.readlines():
                if line.startswith("nameserver") and len(dns) == 0:
                    dns.append(line.split(' ')[1].strip())
            f.close()
        else:
            # use server_ip as DNS
            dns.append(server_ip)
        
        options={
        "__PROGRAM__":PACKAGE,
        "__DATE__":time.ctime(),
        "__MASK__":netmask,
        "__NET__":network,
        "__DNS__":dns[0],
        "__BROADCAST__":broadcast,
        "__ROUTERS__":gateway,
        "__SERVERIP__":server_ip,
        "__STARTIP__":self.w['txt_startip'].get_text(),
        "__ENDIP__":self.w['txt_endip'].get_text(),
        "__BOOTMODE__":boot_filename
        }
        # backup old conf file
        
        # generate /etc/dhcp3/dhcpd.confask_
        new_file=[]
        for block in DHCP_CONF:
            for line in block:
                new_file.append(self.parse_dhcp(line, options))
        
        
        try:
            f=open("/etc/dhcp3/dhcpd.conf", 'w')
            for line in new_file:
                f.write(line + "\n")
            f.close()
        except:
            # error writing
            gtk.gdk.threads_enter()
            self.w['lbl_dhcp'].set_markup( _("<b>Error writing /etc/dhcp3/dhcpd.conf.</b>\nAre you root?") )
            gtk.gdk.threads_leave()
            time.sleep(4)
        
        
        f=open("/etc/default/dhcp3-server", 'r')
        dhcpdata=[]
        for line in f.readlines():
            if line.startswith("INTERFACES"):
                dhcpdata.append("INTERFACES=\"%s\"\n"%server_iface)
            else:
                dhcpdata.append(line)
        f.close()
        f=open("/etc/default/dhcp3-server", 'w')
        for line in dhcpdata:
            f.write(line)
        f.close()
                    

        # edit /etc/hosts
        ip_pref=".".join(self.w['txt_startip'].get_text().split('.')[0:3])
        ip_start=int(self.w['txt_startip'].get_text().split('.')[-1])
        ip_end=int(self.w['txt_endip'].get_text().split('.')[-1])
        txt_prefix=self.w['txt_hostname_prefix'].get_text()
        
        for ip in range ( (ip_end - ip_start) + 1 ):
            ip=ip_start + ip
            ipname=ip
            if ip > 100:
                ipname=ip-100
            self.write_into_etc_host( ["%s.%d"%(ip_pref, ip), "%s%02d"%(txt_prefix, ipname) ]  )
        
        
        # read ck_static
        data={
            "iface":server_iface,
            "address":server_ip,
            "gateway":gateway,
            "netmask":netmask,
            "network":network,
            "broadcast":broadcast,
            }
        if self.w['ck_static'].get_active():
            if self.configure_static(data):
                time.sleep(1)
                gtk.gdk.threads_enter()
                self.w['lbl_dhcp'].set_text( _("Configured static network") )
                gtk.gdk.threads_leave()
                #self.exe_cmd("/etc/init.d/networking restart")
                self.exe_cmd("ifdown --all --exclude=lo")
                self.exe_cmd("ifdown --all --exclude=lo")
                self.exe_cmd("ifdown --all --exclude=lo")
                time.sleep(2)
                self.exe_cmd("ifup --all --exclude=lo")
                #self.exe_cmd("ifconfig %s"%server_iface)
                gtk.gdk.threads_enter()
                self.w['lbl_dhcp'].set_markup( _("<b>Done</b>") )
                gtk.gdk.threads_leave()
                time.sleep(2)
            else:
                gtk.gdk.threads_enter()
                self.w['lbl_dhcp'].set_markup( _("ERROR:<b>Error configuring static network.</b>") )
                gtk.gdk.threads_leave()
                time.sleep(4)
        
        
        gtk.gdk.threads_enter()
        self.w['lbl_dhcp'].set_text( _("Restarting DHCP service...") )
        gtk.gdk.threads_leave()
        
        result=self.exe_cmd("/etc/init.d/dhcp3-server restart")
        fail=False
        for line in result:
            if "fail" in line: fail=True
            if "OK" in line: fail=False
        
        gtk.gdk.threads_enter()
        if not fail:
            self.w['lbl_dhcp'].set_markup( _("<b>Done</b>") )
        else:
            self.w['lbl_dhcp'].set_markup( _("<b>Error restarting DHCP server.</b>") )
        gtk.gdk.threads_leave()
        

    def on_btn_launch_tcosconfig(self, widget):
        th=Thread(target=self.exe_cmd, args=('gksu tcosconfig',) )
        th.setDaemon(1)
        th.start()


################################################################################

    def ask_msg(self, txt):
        response="yes"
        d = gtk.MessageDialog(None,
            gtk.DIALOG_MODAL |
            gtk.DIALOG_DESTROY_WITH_PARENT,
            gtk.MESSAGE_QUESTION,
            gtk.BUTTONS_YES_NO,
            txt)
        if d.run() == gtk.RESPONSE_YES:
            response=True
        else:
            response=False
        d.destroy()
        return response

    def exe_cmd(self, cmd, verbose=1):
        self.p = Popen(cmd, shell=True, bufsize=0, stdout=PIPE, stderr=STDOUT, close_fds=True)
        
        output=[]
        stdout = self.p.stdout
        for line in stdout.readlines():
            if line != '\n':
                line=line.replace('\n', '')
                output.append(line)
        if len(output) == 1:
            return output[0]
        elif len(output) > 1:
            if verbose==1:
                print_debug ( "exe_cmd(%s) %s" %(cmd, output) )
            return output
        else:
            if verbose == 1:
                print_debug ( "exe_cmd(%s)=None" %(cmd) )
            return []

    def quitapp(self,*args):
        print_debug ( "Exiting" )
        self.mainloop.quit()

    def run (self):
        self.mainloop = gobject.MainLoop()
        try:
            self.mainloop.run()
        except KeyboardInterrupt: # Press Ctrl+C
            self.quitapp()
   


if __name__ == '__main__':
    app = TcosStandalone ()
    # Run app
    app.run ()
