## Copyright (C) 2001-2005 Red Hat, Inc.
## Copyright (C) 2001, 2002 Than Ngo <than@redhat.com>
## Copyright (C) 2001-2005 Harald Hoyer <harald@redhat.com>
## Copyright (C) 2001, 2002 Philipp Knirsch <pknirsch@redhat.com>

## This program 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 program 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., 675 Mass Ave, Cambridge, MA 02139, USA.

import sys
import os
import os.path
import shutil
import string

import NCDeviceList
import NCIPsecList
import NCHardwareList

from NC_functions import *
from netconfpkg import ProfileList_base
from netconfpkg import Profile
from netconfpkg import Host

from netconfpkg.conf import Conf

from types import ListType

class MyFileList(ListType):
    def __setitem__(self, key, value):
        value = os.path.abspath(value)
        log.log(5, "MyFileList.__setitem__(self, %s, %s)" % (str(key),
                                                             str(value)))
        return ListType.__setitem__(self, key, value)

    def __contains__(self, obj):
        obj = os.path.abspath(obj)
        ret = ListType.__contains__(self, os.path.abspath(obj))
        log.log(5, "MyFileList.__contains__(self, %s) == %s" % (str(obj), str(ret)))
        return ret

    def append(self, obj):
        obj = os.path.abspath(obj)
        log.log(5, "MyFileList.append(self, %s)" % str(obj))
        return ListType.append(self, os.path.abspath(obj))

class ProfileList(ProfileList_base):
    def __init__(self, list = None, parent = None):
        ProfileList_base.__init__(self, list, parent)        
   	self.error = ""
 
    def load(self):
        self.curr_prof = 'default'
        nwconf = Conf.ConfShellVar(netconfpkg.ROOT + SYSCONFNETWORK)
        if nwconf.has_key('CURRENT_PROFILE'):
            self.curr_prof = nwconf['CURRENT_PROFILE']
            
        if nwconf.has_key('HOSTNAME'):
            self.use_hostname = nwconf['HOSTNAME']
        else:
            self.use_hostname = 'localhost'
            
        if self.curr_prof == None or self.curr_prof == '':
            self.curr_prof = 'default'

        changed = updateNetworkScripts()
        self.__delslice__(0, len(self))

        proflist = []
        if os.path.isdir(netconfpkg.ROOT + SYSCONFPROFILEDIR):
            proflist = os.listdir(netconfpkg.ROOT + SYSCONFPROFILEDIR)        
            for pr in proflist:
                # 60016
                profdir = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + pr
                if not os.path.isdir(profdir):
                    continue
                self.loadprof(pr, profdir)
        else:
            self.loadprof('default', None)

        prof = self.getActiveProfile()
        
        if not prof.DNS.Hostname:
            prof.DNS.Hostname = self.use_hostname
            
        self.commit(changed)

    def loadprof(self, pr, profdir):        
        devicelist = NCDeviceList.getDeviceList()
        ipseclist = NCIPsecList.getIPsecList()

        i = self.addProfile()
        prof = self[i]
        prof.createActiveDevices()
        prof.createActiveIPsecs()
        prof.createDNS()
        prof.createHostsList()
        prof.ProfileName = pr

        if pr == self.curr_prof:
            prof.Active = true
        else:
            prof.Active = false

        if profdir:
            devlist = ConfDevices(profdir)
        else:
            devlist = ConfDevices(netconfpkg.ROOT + OLDSYSCONFDEVICEDIR)
            
        for dev in devlist:
            for d in devicelist:
                if d.DeviceId == dev:
                    prof.ActiveDevices.append(dev)
                    break

        for ipsec in devlist:
            for d in ipseclist:
                if d.IPsecId == ipsec:
                    prof.ActiveIPsecs.append(ipsec)
                    break

        # FIXME: [198898] new backend for /etc/hosts
        # CHECK: [198898] new backend for /etc/hosts
        if profdir:
            try:
                prof.HostsList.load( filename = profdir + '/hosts')
            except ValueError, e:
                if e:
		    self.error = "%s %s\n" % (self.error, e)
        else:
            try:
                prof.HostsList.load( filename = HOSTSCONF )
            except ValueError, e:
                if e:
		    self.error = "%s %s\n" % (self.error, e)

        
        # FIXME: [183338] use SEARCH not resolv.conf
        dnsconf = Conf.ConfEResolv()
        if profdir:
            dnsconf.filename = profdir + '/resolv.conf'
        else:
            dnsconf.filename = netconfpkg.ROOT + RESOLVCONF
        dnsconf.read()
        prof.DNS.Hostname     = self.use_hostname
        prof.DNS.Domainname   = ''
        prof.DNS.PrimaryDNS   = ''
        prof.DNS.SecondaryDNS = ''
        prof.DNS.TertiaryDNS  = ''

        if profdir:
            nwconf = Conf.ConfShellVar(profdir + '/network')
        else:
            nwconf = Conf.ConfShellVar(netconfpkg.ROOT + SYSCONFNETWORK)
            
        if nwconf['HOSTNAME'] != '':
            prof.DNS.Hostname     = nwconf['HOSTNAME']
        if len(dnsconf['domain']) > 0:
            prof.DNS.Domainname   = dnsconf['domain'][0]
        if dnsconf.has_key('nameservers'):
            prof.DNS.PrimaryDNS = dnsconf['nameservers'][0]
            if len(dnsconf['nameservers']) > 1:
                prof.DNS.SecondaryDNS = dnsconf['nameservers'][1]
            if len(dnsconf['nameservers']) > 2:
                prof.DNS.TertiaryDNS = dnsconf['nameservers'][2]
        sl = prof.DNS.createSearchList()
        if dnsconf.has_key('search'):
            for ns in dnsconf['search']:
                sl.append(ns)

    def test(self):
        return
        # Keep that test for later versions
        devmap = {}
        devicelist = NCDeviceList.getDeviceList()
        
        for prof in self:
            if not prof.Active:
                continue
            for devId in prof.ActiveDevices:
                for dev in devicelist:
                    if dev.DeviceId == devId:
                        device = dev
                        break
                else:
                    continue
            
                if devmap.has_key(device.Device) and \
                       device.Alias == devmap[device.Device].Alias:
                    msg = (_('Device %s uses the same Hardware Device '
                             '"%s" like %s!\n') \
                          + _('Please select another Hardware Device or \n'
                              'activate only one of them.')) % \
                              (device.DeviceId, device.Device, \
                               devmap[device.Device].DeviceId)
                    raise TestError(msg)

                devmap[device.Device] = device
            break
        
    def commit(self, changed=true):        
        self.test()
        ProfileList_base.commit(self, changed)
        
    def fixInterfaces(self):
        return
        pppnum = 0
        ipppnum = 0
        isdnnum = 0
        devicelist = NCDeviceList.getDeviceList()
        changed = 0
        for prof in self:
            if not prof.Active:
                continue
            for devid in prof.ActiveDevices:
                for dev in devicelist:
                    if dev.DeviceId != devid:
                        continue
                        
                    if dev.Type == MODEM or dev.Type == DSL:
                        dstr = "ppp"+str(pppnum)
                        if dev.Device != dstr:                            
                            dev.Device = dstr
                            changed = 1
                        pppnum = pppnum + 1
                    elif  dev.Type == ISDN:
                        if dev.Dialup.EncapMode == 'syncppp':
                            dstr = "ippp"+str(ipppnum)
                            if dstr != dev.Device:
                                dev.Device = dstr
                                changed = 1
                            if dev.Dialup.ChannelBundling == true:
                                ipppnum = ipppnum + 1
                                dstr = "ippp"+str(ipppnum)
                                if dstr != dev.Dialup.SlaveDevice:
                                    dev.Dialup.SlaveDevice = dstr
                                    changed = 1
                            ipppnum = ipppnum + 1
                        else:
                            dstr = "isdn"+str(isdnnum)
                            if dstr != dev.Device:
                                dev.Device = dstr
                                changed = 1
                            isdnnum = isdnnum + 1
                    break
            break
        
    def save(self):
        # FIXME: [163040] "Exception Occurred" when saving
        # fail gracefully, with informing, which file, and why
        import socket
        # Just to be safe...
        os.umask(0022)

	    # commit the changes
        self.commit(changed=false)

        devicelist = NCDeviceList.getDeviceList()

        nwconf = Conf.ConfShellVar(netconfpkg.ROOT + SYSCONFNETWORK)
        # FIXME: [183338] use SEARCH not resolv.conf
        dnsconf = Conf.ConfEResolv()

        act_prof = self.getActiveProfile()		
        
        if socket.gethostname() != act_prof.DNS.Hostname and \
               getDebugLevel() < 10:
            if os.getuid() == 0:
                # FIXME: [169733] Renaming machine prevents applications from opening                
                # if the hostname changed, set it system wide (#55746)
                os.system("hostname %s" % act_prof.DNS.Hostname)
                log.log(2, "change hostname to %s" % act_prof.DNS.Hostname)
                
            newip = '127.0.0.1'
            try:
                newip = socket.gethostbyname(act_prof.DNS.Hostname)
            except:
                for host in act_prof.HostsList:
                    if host.IP == '127.0.0.1' or host.IP == "::1":
                        host.Hostname = 'localhost.localdomain'
                        if 'localhost' not in host.AliasList:
                            host.AliasList.append('localhost')

                        # append the hostname to 127.0.0.1, if it does not contain a domain
                        if act_prof.DNS.Hostname.find(".") != -1:
                            hname = act_prof.DNS.Hostname.split(".")[0]
                        else:
                            hname = act_prof.DNS.Hostname

                        if hname not in host.AliasList:
                            host.AliasList.append(hname)
            else:
                if newip != "127.0.0.1" and newip != "::1":
                    # We found an IP for the hostname
                    for host in act_prof.HostsList:
                        if host.IP == '127.0.0.1' or host.IP == "::1":
                            # reset localhost
                            host.Hostname = 'localhost.localdomain'
                            if 'localhost' not in host.AliasList:
                                host.AliasList.append('localhost')
                        if host.IP == newip:
                            # found entry in /etc/hosts with our IP
                            # change the entry
                            host.createAliasList()
                            try:
                                hname = socket.gethostbyaddr(newip)
                                host.Hostname = hname[0]
                                host.AliasList.extend(hname[1])
                            except:
                                host.Hostname = act_prof.DNS.Hostname
                            if host.Hostname != act_prof.DNS.Hostname:
                                host.AliasList.append( act_prof.DNS.Hostname )
                            if act_prof.DNS.Hostname.find(".") != -1:
                                hname = act_prof.DNS.Hostname.split(".")[0]
                                if not hname in host.AliasList:
                                    host.AliasList.append(hname)                

            act_prof.HostsList.commit(changed=true)

        nwconf['HOSTNAME'] = act_prof.DNS.Hostname
              
        if act_prof.ProfileName != 'default':
            nwconf['CURRENT_PROFILE'] = act_prof.ProfileName
        else:
            del nwconf['CURRENT_PROFILE']

        nwconf.write()

        if not os.path.isdir(netconfpkg.ROOT + SYSCONFPROFILEDIR):
            mkdir(netconfpkg.ROOT + SYSCONFPROFILEDIR)

        files_used = MyFileList()
                
        for prof in self:
            if not os.path.isdir(netconfpkg.ROOT + SYSCONFPROFILEDIR + \
                                 '/' + prof.ProfileName):
                mkdir(netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                      prof.ProfileName)
            files_used.append(netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                              prof.ProfileName)

            nwconf = Conf.ConfShellVar(netconfpkg.ROOT + SYSCONFPROFILEDIR + \
                                       '/' + prof.ProfileName + '/network')
            files_used.append(nwconf.filename)

            nwconf['HOSTNAME'] = prof.DNS.Hostname
            nwconf.write()

            # FIXME: [183338] use SEARCH not resolv.conf
            dnsconf.filename = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                               prof.ProfileName + '/resolv.conf'

            files_used.append(dnsconf.filename)
            
            dnsconf['domain'] = ''
            if prof.DNS.Domainname != '':
                dnsconf['domain'] = [prof.DNS.Domainname]
            else:
                del dnsconf['domain']

            dnsconf['search'] = []
            if prof.DNS.SearchList != []:
                dnsconf['search'] = prof.DNS.SearchList
            else:
                del dnsconf['search']

            dnsconf['nameservers'] = []
            nameservers = []
            if prof.DNS.PrimaryDNS != '':
                nameservers.append(prof.DNS.PrimaryDNS)
            if prof.DNS.SecondaryDNS != '':
                nameservers.append(prof.DNS.SecondaryDNS)
            if prof.DNS.TertiaryDNS != '':
                nameservers.append(prof.DNS.TertiaryDNS)

            dnsconf['nameservers'] = nameservers
            # FIXME: [198898] new backend for /etc/hosts
            filename = netconfpkg.ROOT + \
                         SYSCONFPROFILEDIR + '/' + \
                         prof.ProfileName + \
                         '/hosts'
            prof.HostsList.save(filename=filename)
            files_used.append(filename)
            saved = []
#            for host in prof.HostsList:
#                hoconf[host.Hostname] = [host.IP, host.AliasList]
#                saved.append(host.Hostname)
            # FIXME: check [166855] system-config-network, 'save' erases local loopback entry    

            dnsconf.write()

            for devId in prof.ActiveDevices:
                for prefix in [ 'ifcfg-', 'route-', 'keys-']:
                    devfilename = netconfpkg.ROOT + SYSCONFDEVICEDIR + \
                                  prefix + devId
                    profilename = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                                  prof.ProfileName + '/' + prefix + devId

                    if os.path.isfile(devfilename):
                        if not issamefile(devfilename, profilename):
                            unlink(profilename)
                            link(devfilename, profilename)
                        
                        files_used.append(devfilename)
                        files_used.append(profilename)

                # unlink old .route files
                profilename = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                              prof.ProfileName + '/' + devId + '.route'
                unlink(profilename)

                    
                if prof.Active == false and prof.ProfileName != 'default':
                    continue

                # Active Profile or default profile                
                for prefix in [ 'ifcfg-', 'route-', 'keys-' ]:
                    devfilename = netconfpkg.ROOT + SYSCONFDEVICEDIR + \
                                      '/' + prefix + devId
                    profilename = netconfpkg.ROOT + OLDSYSCONFDEVICEDIR + \
                                  '/' + prefix + devId
                       
                    if os.path.isfile(devfilename):
                        if not issamefile(devfilename, profilename):
                            unlink(profilename)
                            link(devfilename, profilename)
                        files_used.append(profilename)

                # unlink old .route files
                unlink(netconfpkg.ROOT + OLDSYSCONFDEVICEDIR + \
                       '/' + devId + '.route')

            for devId in prof.ActiveIPsecs:
                for prefix in [ 'ifcfg-', 'keys-']:
                    devfilename = netconfpkg.ROOT + SYSCONFDEVICEDIR + \
                                  prefix + devId
                    profilename = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                                  prof.ProfileName + '/' + prefix + devId

                    if os.path.isfile(devfilename):
                        if not issamefile(devfilename, profilename):
                            unlink(profilename)
                            link(devfilename, profilename)
                            
                        files_used.append(devfilename)
                        files_used.append(profilename)

                if prof.Active == false and prof.ProfileName != 'default':
                    continue

                # Active Profile or default profile                
                for prefix in [ 'ifcfg-', 'keys-' ]:
                    devfilename = netconfpkg.ROOT + SYSCONFDEVICEDIR + \
                                      '/' + prefix + devId
                    profilename = netconfpkg.ROOT + OLDSYSCONFDEVICEDIR + \
                                  '/' + prefix + devId
                       
                    if os.path.isfile(devfilename):
                        if not issamefile(devfilename, profilename):
                            unlink(profilename)
                            link(devfilename, profilename)
                            
                        files_used.append(profilename)                

            if prof.Active == false:                
                continue

            # Special actions for the active profile

            for (file, cfile) in { RESOLVCONF : '/resolv.conf', HOSTSCONF : '/hosts' }.items():
                hostfile = netconfpkg.ROOT + file
                conffile = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + \
                           prof.ProfileName + cfile
                if not os.path.isfile(hostfile) or not issamefile(hostfile, conffile):
                    rename(hostfile, hostfile + '.bak')
                    unlink(hostfile)
                    link(conffile, hostfile)

                os.chmod(hostfile, 0644)

        # Remove all unused files that are linked in the device directory
        devlist = os.listdir(netconfpkg.ROOT + OLDSYSCONFDEVICEDIR)
        for dev in devlist:
            if string.split(dev, '-')[0] not in [ 'ifcfg', 'route',
                                                  'keys' ] or \
                                                  (len(dev) > 6 and \
                                                   dev[-6:] == '.route') \
                                                  or dev == 'ifcfg-lo':
                continue
            file = netconfpkg.ROOT + OLDSYSCONFDEVICEDIR+'/'+dev
            if file in files_used:
                # Do not remove used files
                continue
            try:
                stat = os.stat(file)
                if stat[3] > 1:
                    # Check, if it is a device of neat in every profile directory
                    dirlist = os.listdir(netconfpkg.ROOT + SYSCONFPROFILEDIR)
                    for dir in dirlist:
                        dirname = netconfpkg.ROOT + SYSCONFPROFILEDIR + '/' + dir
                        if not os.path.isdir(dirname):
                            continue
                        filelist = os.listdir(dirname)                    
                        for file2 in filelist:
                            stat2 = os.stat(dirname + '/' + file2)
                            if os.path.samestat(stat, stat2):
                                unlink(file)
            except:
                pass
                        

        # Remove all profile directories except default
        proflist = os.listdir(netconfpkg.ROOT + SYSCONFPROFILEDIR)
        for prof in proflist:
            # Remove all files in the profile directory
            filelist = os.listdir(netconfpkg.ROOT + SYSCONFPROFILEDIR + prof)
            for file in filelist:
                filename = netconfpkg.ROOT + SYSCONFPROFILEDIR + prof + '/' + \
                           file
                if filename in files_used:
                    # Do not remove used files
                    log.log(6, "%s not removed" % filename)
                    continue                
                unlink(filename)

            filename = netconfpkg.ROOT + SYSCONFPROFILEDIR + prof
            try:
                if not (filename in files_used):
                    rmdir(filename)
            except:
                pass

    def activateDevice (self, deviceid, profile, state=None):
        devicelist = NCDeviceList.getDeviceList()
        profilelist = getProfileList()

        for prof in profilelist:
            if prof.ProfileName != profile:
                continue
            if state:
                if deviceid not in prof.ActiveDevices:
                    prof.ActiveDevices.append(deviceid)
            else:
                if deviceid in prof.ActiveDevices:
                    del prof.ActiveDevices[prof.ActiveDevices.index(deviceid)]

    def activateIpsec (self, ipsecid, profile, state=None):
        ipseclist = NCIPsecList.getIPsecList()
        profilelist = getProfileList()

        for prof in profilelist:
            if prof.ProfileName != profile:
                continue
            if state:
                if ipsecid not in prof.ActiveIPsecs:
                    prof.ActiveIPsecs.append(ipsecid)
            else:
                if ipsecid in prof.ActiveIPsecs:
                    del prof.ActiveIPsecs[prof.ActiveIPsecs.index(ipsecid)]

    def switchToProfile(self, val, dochange = true):
        found = false
        aprof = None
        for prof in self:
            if (isinstance(val, str) and prof.ProfileName == val) or \
                   (isinstance(val, Profile) and prof == val) :
                    found = true
                    break
        else:
            return None

        modl = self.modified()
        for prof in self:
            mod = prof.modified()
            if (isinstance(val, str) and prof.ProfileName == val) or \
                   (isinstance(val, Profile) and prof == val) :
                prof.Active = true
                aprof = prof
            else:
                prof.Active = false
            if not dochange:
                prof.setChanged(mod)

        if not dochange:
            self.setChanged(modl)
        
        return prof

    def getActiveProfile(self):
        for prof in self:
            if not prof.Active:
                continue
            return prof

        if len(self):
            return self[0]

    def _objToStr(self, parentStr = None):
        retstr = ""
        for profile in self:
            retstr += profile._objToStr("ProfileList.%s" % \
                                        (profile.ProfileName))

        return retstr

    def _parseLine(self, vals, value):
        if len(vals) <= 1:
            return
        if vals[0] == "ProfileList":
            del vals[0]
        else:
            return
        for profile in self:
            if profile.ProfileName == vals[0]:
                profile._parseLine(vals[1:], value)
                return
        
        i = self.addProfile()
        self[i].ProfileName = vals[0]
        self[i]._parseLine(vals[1:], value)
        

__PFList = None
__PFList_root = netconfpkg.ROOT

def getProfileList(refresh=None):
    global __PFList
    global __PFList_root
    
    if __PFList == None or refresh or \
           __PFList_root != netconfpkg.ROOT:
        __PFList = ProfileList()
        __PFList.load()
        __PFList_root = netconfpkg.ROOT
    return __PFList

__author__ = "Harald Hoyer <harald@redhat.com>"
