Logo Search packages:      
Sourcecode: kde-guidance version File versions  Download package

mountconfig.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-
###########################################################################
# mountconfig.py - description                                            #
# ------------------------------                                          #
# begin     : Fri Nov 30 2003                                             #
# copyright : (C) 2003 by Simon Edwards                                   #
# email     : simon@simonzone.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.                                   #
#                                                                         #
###########################################################################

from qt import *
from kdeui import *
from kdecore import *
from kfile import *
from kio import *
import sys
import os
import os.path
from types import StringType,UnicodeType
import pwd
import grp
import math
import locale
import codecs

import MicroHAL
from SMBShareSelectDialog import *
from SimpleCommandRunner import *
from fuser import *
import sizeview

programname = "Disk & Filesystem Configuration"
version = "0.6.7"

# Are we running as a separate standalone application or in KControl?
standalone = __name__=='__main__'

# Running as the root user or not?
isroot = os.getuid()==0

"""
Universal Options
-----------------

async/sync 
atime/noatime
auto/noauto
dev/nodev
exec/noexec
ro/rw 
suid/nosuid
dirsync 
nouser/user/users 

defaults =>rw, suid, dev, exec, auto, nouser, and async. 

Automatically set
=================
_netdev 
The filesystem resides on a device that requires network access (used to
prevent the system from attempting to mount these filesystems until the
network has been enabled on the system). 

remount 
Attempt to remount an already-mounted file system. This is commonly used
to change the mount flags for a file system, especially to make a readonly
file system writeable. It does not change device or mount point. 

Supported filesystems
---------------------
nfs
ext2
ext3
reiserfs
vfat
ntfs
udf
iso9660
supermount
reiser4
xfs
jfs
hfs 
hfsplus

smbfs

auto

swap

proc
sysfs
usbdevfs

TODO
----
* SMB finished the connection username nad password fields.
* SMB entry: finished writing the config.
* SMBSelector: setting the username and password.

"""

############################################################################
class UserComboBox(KComboBox):
    def __init__(self,parent,name=None):
        KComboBox.__init__(self,parent,name)
        tmplist = []
        users = pwd.getpwall()
        for user in users:
            uid = int(user[2])
            username = user[4]
            tmplist.append( (int(uid),"%s (%s)" % (username,uid)) )
        tmplist.sort(lambda a,b: cmp(a[1],b[1]))
        self.userlist = []
        for user in tmplist:
            self.insertItem(user[1])
            self.userlist.append(user[0])

    ########################################################################
    def setUID(self,uid):
        if uid in self.userlist:
            self.setCurrentItem(self.userlist.index(int(uid)))
            return True
        else:
            return False

    ########################################################################
    def UID(self):
        return self.userlist[self.currentItem()]

############################################################################
class GroupComboBox(KComboBox):
    def __init__(self,parent,name=None):
        KComboBox.__init__(self,parent,name)
        self.grouplist = []
        groups = grp.getgrall()
        tmplist = []
        for group in groups:
            gid = group[2]
            groupname = group[0]
            tmplist.append( (int(gid),"%s (%s)" % (groupname,gid)) )
        tmplist.sort(lambda a,b: cmp(a[1],b[1]))
        self.grouplist = []
        for group in tmplist:
            self.insertItem(group[1])
            self.grouplist.append(group[0])

    ########################################################################
    def setGID(self,gid):
        if gid in self.grouplist:
            self.setCurrentItem(self.grouplist.index(int(gid)))
            return True
        else:
            return False

    ########################################################################
    def GID(self):
        return self.grouplist[self.currentItem()]


############################################################################
class MountEntryExt(object):
    microhal = MicroHAL.MicroHAL()

    ########################################################################
    # Base can be either a fstab format line of text, of another MountEntry
    # object.
    def __init__(self,base=None):
        if base==None:
            self.device = unicode(i18n("<device>"))
            self.mountpoint = unicode(i18n("<mount point>"))
            self.mounttype = 'ext2'
            self.extraoptions = "noauto"
            self.fs_freq = 0
            self.fs_passno = 0
            self.enabled = False
            self.managed = False
        elif isinstance(base,StringType) or isinstance(base,UnicodeType):
            parts = base.split()
            self.device = MountEntry.decodeMountEntryString(parts[0])
            self.mountpoint = MountEntry.decodeMountEntryString(parts[1])
            self.mounttype = MountEntry.decodeMountEntryString(parts[2])
            self.extraoptions = MountEntry.decodeMountEntryString(parts[3])
            self.fs_freq = int(parts[4])
            self.fs_passno = int(parts[5])
            self.enabled = False

            options = self.extraoptions.split(",")
            self.managed = "managed" in options
            try:
                options.remove("managed")
            except ValueError:
                pass
            self.extraoptions = ",".join(options)

        else:
            # This is a new entry, but it's based on another one.
            self.device = base.device
            self.mountpoint = base.mountpoint
            self.mounttype = base.mounttype
            self.extraoptions = base.extraoptions
            self.fs_freq = base.fs_freq
            self.fs_passno = base.fs_passno
            self.enabled = base.enabled
            self.managed = False
        self.iconname = self.getIconName()

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExt()
            # FIXME: use "newobject = self.__class__()" and get rid of the newobject parameter.
        newobject.device = self.device
        newobject.mountpoint = self.mountpoint
        newobject.mounttype = self.mounttype
        newobject.extraoptions = self.extraoptions
        newobject.fs_freq = self.fs_freq
        newobject.fs_passno = self.fs_passno
        newobject.enabled = self.enabled
        return newobject
    ########################################################################
    def cleanup(self):
        # This method is called after the entry has been removed from the
        # mounttable.
        pass

    ########################################################################
    def setMountType(self,mounttypename): self.mounttype = mounttypename

    ########################################################################
    def isFileSystemAvailable(self): 
        return self.microhal.isSupportedFileSystem(self.mounttype)

    def getDevice(self): return self.device
    def setDevice(self,device): self.device = device
    def getMountPoint(self): return self.mountpoint
    def setMountPoint(self,mountpoint): self.mountpoint = mountpoint
    def getExtraOptions(self): return self.extraoptions
    def setExtraOptions(self,extraoptions): self.extraoptions = extraoptions
    def getFSFreq(self): return self.fs_freq
    def setFSFreq(self,fs_freq): self.fs_freq = fs_freq
    def getFSPassno(self): return self.fs_passno
    def setFSPassno(self,fs_passno): self.fs_passno = fs_passno
    def isManaged(self): return self.managed

    ########################################################################
    def getName(self):
        if os.path.basename(self.device).startswith("fd"):
            return "Floppy"
        else:
            return self.mountpoint

    ########################################################################
    def getIconName(self):
        if os.path.basename(self.device).startswith("fd"):
            return "hi16-floppy"
        else:
            return "hi16-blockdevice"

    ########################################################################
    def updateStatus(self,mtablist):
        self.enabled = self.mountpoint in mtablist

    ########################################################################
    def getFstabOptions(self):
        if self.extraoptions!="":
            return self.extraoptions.split(",")
        else:
            return []

    ########################################################################
    def getFstabLine(self):
        # Construct the options field.
        _options = self.getFstabOptions()
        options = []
        # Remove whitespace and dupes
        for o in _options:
            if o.strip() not in options:
                options.append(o.strip())

        return MountEntry.encodeMountEntryString(self.device) + \
            u" " + MountEntry.encodeMountEntryString(self.mountpoint) + \
            u" " + MountEntry.encodeMountEntryString(self.mounttype) + \
            u" " + MountEntry.encodeMountEntryString(u",".join(options)) + \
            u" " + unicode(self.fs_freq) + u" " + unicode(self.fs_passno)

    ########################################################################
    def getCategory(self):
        return self.device

    ########################################################################
    def isEnabled(self): return self.enabled

    ########################################################################
    def enable(self,parentdialog):
        self._setBusy(parentdialog,True)
        try:
            (rc,output) = SimpleCommandRunner().run(["/bin/mount",self.mountpoint])
        finally:
            self._setBusy(parentdialog,False)
        if rc!=0:
            self.handleMountFailure(parentdialog,rc,output,True)

    ########################################################################
    def disable(self,parentdialog):
        self._setBusy(parentdialog,True)
        try:
            (rc,output) = SimpleCommandRunner().run(["/bin/umount",self.mountpoint])
        finally:
            self._setBusy(parentdialog,False)
        if rc!=0:
            self.handleMountFailure(parentdialog,rc,output,False)

    ########################################################################
    def handleMountFailure(self,parentdialog,rc,output,mount_action=True):
        """
        Keyword arguments:
        mount_action - True=enable, False=disable
        """
        global kapp
        if mount_action:
            msg = i18n("An error occurred while enabling %1.\n\nThe system reported: %2").arg(self.mountpoint).arg(output)
            captionmsg = i18n("Unable to enable %1").arg(self.mountpoint)
        else:
            msg = i18n("An error occurred while disabling %1.\n\nThe system reported: %2").arg(self.mountpoint).arg(output)
            captionmsg = i18n("Unable to disable %1").arg(self.mountpoint)

        extramsg = unicode(i18n("Return code from mount was %1.\n").arg(rc))

        if (rc & 1)!=0:
            extramsg += unicode(i18n("\"incorrect invocation or permissions\"\n"))
        if (rc & 2)!=0:
            extramsg += unicode(i18n("\"system error (out of memory, cannot fork, no more loop devices)\"\n"))
        if (rc & 4)!=0:
            extramsg += unicode(i18n("\"internal mount bug or missing nfs support in mount\"\n"))
        if (rc & 8)!=0:
            extramsg += unicode(i18n("\"user interrupt\"\n"))
        if (rc & 16)!=0:
            extramsg += unicode(i18n("\"problems writing or locking /etc/mtab\"\n"))
        if (rc & 32)!=0:
            extramsg += unicode(i18n("\"mount failure\"\n"))
        if (rc & 64)!=0:
            extramsg += unicode(i18n("\"some mount succeeded\"\n"))

        in_use = False
        if not mount_action:
            # Use lsof to find out what is blocking the device.
            # FIXME lsof path ?
            lsof_bin = '/usr/sbin/lsof'
            rc, output = SimpleCommandRunner().run([lsof_bin,'-FncL',self.device])
            if rc==0:
                # Check if there is one or more processes using the device.
                in_use = len(output.split())>3
                if in_use:
                    # Start fuser.py which lists open filedescriptors on device and offers to get
                    # rid of them.
                    fuser = FUser(self.device,None,lsof_bin,kapp)
                    fuser.exec_loop()

                    if fuser.result() != 0:
                        in_use_message =  unicode(i18n("Unmounting %1 failed or was cancelled.").arg(self.device))
                    extramsg += in_use_message
                else:
                    extramsg += unicode(i18n("(none)"))

        if not in_use: 
            KMessageBox.detailedSorry(parentdialog, msg, extramsg, captionmsg)

    ########################################################################
    def _setBusy(self,parentdialog,flag):
        global kapp
        if flag:
            kapp.setOverrideCursor( QCursor(Qt.WaitCursor) )
            parentdialog.setEnabled(False)

            # It is necessary to process some of the events in the event queue.
            # Otherwise the user won't see that the window is disabled.
            # ( setEnabled() here above doesn't redraw the window immediately.
            # Redrawing is done via the event queue.)
            kapp.processEvents()
        else:
            parentdialog.setEnabled(True)
            kapp.restoreOverrideCursor()

############################################################################
class MountEntryExtCommonUnix(MountEntryExt):

    USERMOUNT_NO = 0
    USERMOUNT_ONE = 1
    USERMOUNT_ANY = 2
    USERMOUNT_OWNER = 3

    ########################################################################
    # Base can be either a fstab format line of text, or another MountEntry
    # object.
    def __init__(self,base=None):
        super(MountEntryExtCommonUnix,self).__init__(base)

        if isinstance(base,MountEntryExtCommonUnix):
            # Being initalised from an existing object.
            # Only mess with objects
            self.atime = base.atime
            self.auto = base.auto
            self.writeable = base.writeable
            self.usedevpoints = base.usedevpoints
            self.allowexecutables = base.allowexecutables
            self.allowsuid = base.allowsuid
            self.allowusermount = base.allowusermount

        elif isinstance(base,StringType) or isinstance(base,UnicodeType):
            options = self.extraoptions.split(",")

            self.atime = True
            if "noatime" in options:
                self.atime = False
            self.auto = True
            if "noauto" in options:
                self.auto = False
            self.writeable = True
            if "ro" in options:
                self.writeable = False
            self.usedevpoints = True
            if "nodev" in options:
                self.usedevpoints = False
            self.allowexecutables = True
            if "noexec" in options:
                self.allowexecutables = False
            self.allowsuid = True
            if "nosuid" in options:
                self.allowsuid = False
            self.allowusermount = self.USERMOUNT_NO
            if "user" in options:
                self.allowusermount = self.USERMOUNT_ONE
            if "users" in options:
                self.allowusermount = self.USERMOUNT_ANY
            if "owner" in options:
                self.allowusermount = self.USERMOUNT_OWNER

            for x in ["noatime","atime","auto","noauto","dev","nodev","nouser", \
                    "owner","users","user","suid","nosuid","exec","noexec","rw","ro"]:
                try:
                    options.remove(x)
                except ValueError:
                    pass

            self.extraoptions = ",".join(options)

        else:
            # Set some sane defaults.
            self.atime = True
            self.auto = False
            self.writeable = True
            self.usedevpoints = False
            self.allowexecutables = False
            self.allowsuid = False
            self.allowusermount = self.USERMOUNT_NO

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtCommonUnix()
        super(MountEntryExtCommonUnix,self).copy(newobject)
        newobject.atime = self.atime
        newobject.auto = self.auto
        newobject.writeable = self.writeable
        newobject.usedevpoints = self.usedevpoints
        newobject.allowexecutables = self.allowexecutables
        newobject.allowsuid = self.allowsuid
        newobject.allowusermount = self.allowusermount
        return newobject

    ########################################################################
    def getFstabOptions(self):
        options = []
        
        # These options must appear before the others. 'user', according to the
        # mount man page implies 'noexec' too, BUT the noexec can be overridden
        # by specifying 'exec' after the 'user' keyword. Therefore 'exec' etc
        # must come after 'user', 'users' and friends.
        options.append(['nouser','user','users','owner'][self.allowusermount])
        
        super_options = super(MountEntryExtCommonUnix,self).getFstabOptions()
        options.extend(super_options)
        
        options.append(['noatime','atime'][self.atime])
        options.append(['noauto','auto'][self.auto])
        options.append(['ro','rw'][self.writeable])
        options.append(['nodev','dev'][self.usedevpoints])
        options.append(['noexec','exec'][self.allowexecutables])
        options.append(['nosuid','suid'][self.allowsuid])
        return options

    ########################################################################
    # atime/noatime
    def getAtime(self): return self.atime
    def setAtime(self,val): self.atime = val
    # auto/noauto
    def getMountAtBoot(self): return self.auto
    def setMountAtBoot(self,val): self.auto = val
    # ro/rw 
    def getWritable(self): return self.writeable
    def setWritable(self,val): self.writeable = val
    # dev, nodev
    def getUseDevPoints(self): return self.usedevpoints
    def setUseDevPoints(self,val): self.usedevpoints = val
    # exec/noexec
    def getAllowExecutables(self): return self.allowexecutables
    def setAllowExecutable(self,val): self.allowexecutables = val
    # suid/nosuid
    def getSUID(self): return self.allowsuid
    def setSUID(self,val): self.allowsuid = val
    # nouser/user/users/owner
    def setAllowUserMount(self,val): self.allowusermount = val
    def getAllowUserMount(self): return self.allowusermount


############################################################################
# Common unix filesystems, but for local hard disks. i.e. partitions.
class MountEntryExtCommonUnixLocal(MountEntryExtCommonUnix):
    ########################################################################
    def __init__(self,base=None):
        super(MountEntryExtCommonUnixLocal,self).__init__(base)
    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtCommonUnixLocal()
        super(MountEntryExtCommonUnixLocal,self).copy(newobject)
        return newobject

############################################################################
class MountEntryExtAlien(MountEntryExt):

    USERMOUNT_NO = 0
    USERMOUNT_ONE = 1
    USERMOUNT_ANY = 2
    USERMOUNT_OWNER = 3

    ########################################################################
    # Base can be either a fstab format line of text, of another MountEntry
    # object.
    def __init__(self,base=None):
        super(MountEntryExtAlien,self).__init__(base)

        if isinstance(base,MountEntryExtAlien):
            self.uid = base.uid
            self.gid = base.gid
            self.writeable = base.writeable
            self.auto = base.auto
            self.allowusermount = base.allowusermount

        elif isinstance(base,StringType) or isinstance(base,UnicodeType):
            self.uid = 0
            self.gid = 0
            options = self.extraoptions.split(",")
            newoptions = []
            for line in options:
                if line.startswith("uid="):
                    try:
                        self.uid = int(line[4:])
                    except ValueError:
                        self.uid = 0
                elif line.startswith("gid="):
                    try:
                        self.gid = int(line[4:])
                    except ValueError:
                        self.gid = 0
                else:
                    # We hang on to unknown options for later.
                    newoptions.append(line)
            options = newoptions

            self.writeable = True
            if "ro" in options:
                self.writeable = False
            self.auto = True
            if "noauto" in options:
                self.auto = False
            self.allowusermount = self.USERMOUNT_NO
            if "user" in options:
                self.allowusermount = self.USERMOUNT_ONE
            if "users" in options:
                self.allowusermount = self.USERMOUNT_ANY
            if "owner" in options:
                self.allowusermount = self.USERMOUNT_OWNER

            for x in ["noatime","atime","auto","noauto","dev","nodev","nouser", \
                    "owner","users","user","suid","nosuid","exec","noexec","rw", \
                    "ro"]:
                try:
                    options.remove(x)
                except ValueError:
                    pass
            self.extraoptions = ",".join(options)

        else:
            self.uid = 0
            self.gid = 0
            self.writeable = False
            self.auto = False
            self.allowusermount = self.USERMOUNT_NO

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtAlien()
        super(MountEntryExtAlien,self).copy(newobject)
        newobject.uid = self.uid
        newobject.gid = self.gid
        newobject.writeable = self.writeable
        newobject.auto = self.auto
        newobject.allowusermount = self.allowusermount
        return newobject

    ########################################################################
    def getFstabOptions(self):
        # Construct the options field.
        options = super(MountEntryExtAlien,self).getFstabOptions()
        options.append('uid='+unicode(self.uid))
        options.append('gid='+unicode(self.gid))
        options.append(['noauto','auto'][self.auto])
        options.append(['ro','rw'][self.writeable])
        options.append(['nouser','user','users','owner'][self.allowusermount])
        return options

    ########################################################################
    def getUID(self): return self.uid
    def setUID(self,val): self.uid = val
    def getGID(self): return self.gid
    def setGID(self,val): self.gid = val

    # ro/rw 
    def getWritable(self): return self.writeable
    def setWritable(self,val): self.writeable = val
    # auto/noauto
    def getMountAtBoot(self): return self.auto
    def setMountAtBoot(self,val): self.auto = val
    # nouser/user/users/owner
    def setAllowUserMount(self,val): self.allowusermount = val
    def getAllowUserMount(self): return self.allowusermount

############################################################################
class MountEntryExtVFAT(MountEntryExtAlien):
    def __init__(self,base=None):
        super(MountEntryExtVFAT,self).__init__(base)

        if isinstance(base,MountEntryExtVFAT):
            self.suppresspermissionerrors = base.suppresspermissionerrors
        elif isinstance(base,StringType) or isinstance(base,UnicodeType):
            options = self.extraoptions.split(",")
            self.suppresspermissionerrors = "quiet" in options
            try:
                options.remove("quiet")
            except ValueError:
                pass
            self.extraoptions = ",".join(options)

        else:
            self.suppresspermissionerrors = False

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtVFAT()
        super(MountEntryExtVFAT,self).copy(newobject)
        newobject.suppresspermissionerrors = self.suppresspermissionerrors
        return newobject

    ########################################################################
    def getFstabOptions(self):
        options = super(MountEntryExtVFAT,self).getFstabOptions()
        if self.suppresspermissionerrors:
            options.append('quiet')
        return options

    def getSuppressPermissionErrors(self): return self.suppresspermissionerrors
    def setSuppressPermissionErrors(self,val): self.suppresspermissionerrors = val

############################################################################
class MountEntryExtSMB(MountEntryExtAlien):
    CREDENTIALSBASENAME = "/etc/fstab_smb_credentials_"
    ########################################################################
    def __init__(self,base=None):
        super(MountEntryExtSMB,self).__init__(base)

        if isinstance(base,MountEntryExtSMB):
            self.username = base.username
            self.password = base.password
            self.credentialsfile = base.credentialsfile

        elif isinstance(base,StringType) or isinstance(base,UnicodeType):
            self.username = None
            self.password = ""
            self.credentialsfile = None

            newoptions = []
            options = self.extraoptions.split(",")
            for line in options:
                if line.startswith("username="):
                    self.username = line[9:]
                elif line.startswith("password="):
                    self.password = line[9:]
                elif line.startswith("credentials="):
                    self.credentialsfile = line[12:]
                    try:
                        fhandle = codecs.open(self.credentialsfile,'r',locale.getpreferredencoding())
                        for line in fhandle.readlines():
                            if line.startswith("username"):
                                self.username = line[8:].strip()[1:].strip()
                            elif line.startswith("password"):
                                self.password = line[8:].strip()[1:].strip()
                        fhandle.close()

                        if not self.credentialsfile.startswith(self.CREDENTIALSBASENAME):
                            self.credentialsfile = None

                    except IOError:
                        self.credentialsfile = None

                elif line=="guest":
                    pass
                else:
                    # We hang on to unknown options for later.
                    newoptions.append(line)
            options = newoptions

            if self.username == "":
                self.username = None

            self.extraoptions = ",".join(options)

        else:
            self.username = None
            self.password = ""
            self.credentialsfile = None

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtSMB()
        super(MountEntryExtSMB,self).copy(newobject)
        newobject.username = self.username
        newobject.password = self.password
        newobject.credentialsfile = self.credentialsfile
        return newobject

    ########################################################################
    def cleanup(self):
        if (self.credentialsfile is not None) and os.path.exists(self.credentialsfile) and os.path.isfile(self.credentialsfile):
            os.remove(self.credentialsfile)

    ########################################################################
    def getIconName(self):
        return "hi16-network"

    ########################################################################
    def getFstabOptions(self):
        options = super(MountEntryExtSMB,self).getFstabOptions()
        if self.username is None:
            if (self.credentialsfile is not None) and os.path.exists(self.credentialsfile) and os.path.isfile(self.credentialsfile):
                os.remove(self.credentialsfile)
            options.append("guest") # This option should stop mount(8) from asking for a password.
        else:
            # Write out the credentials file
            if self.credentialsfile is None:
                i = 1
                while os.path.exists(self.CREDENTIALSBASENAME+unicode(i)):
                    i += 1
                self.credentialsfile = self.CREDENTIALSBASENAME+unicode(i)
            fd = os.open(self.credentialsfile,os.O_WRONLY|os.O_CREAT,0600)
            fhandle = os.fdopen(fd,'w')
            fhandle.write((u"username = %s\npassword = %s\n" % (self.username,self.password))
                .encode(locale.getpreferredencoding(),'replace') )
            fhandle.close()
            options.append(u"credentials="+self.credentialsfile)
        return options

    ########################################################################
    def getUsername(self): return self.username
    def setUsername(self,username): self.username = username
    def getPassword(self): return self.password
    def setPassword(self,password): self.password = password

############################################################################
class MountEntryExtSystem(MountEntryExt):
    ########################################################################
    def __init__(self,base=None):
        super(MountEntryExtSystem,self).__init__(base)

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtSystem()
        super(MountEntryExtSystem,self).copy(newobject)
        return newobject

    ########################################################################
    def getCategory(self):
        return "system"

    def disable(self,parentdialog):
        """ This shouldn't happen since system entries have the disable button disabled """             
        msg = i18n("Disabling %1 is not supported.").arg(self.mountpoint)
        extramsg = i18n("Some system devices cannot be disabled because they are needed for basic functionality of the operating system.")
        KMessageBox.detailedSorry(parentdialog,msg,extramsg,\
            i18n("Error occurred while disabling %1").arg(self.mountpoint))

############################################################################
class MountEntryExtSwap(MountEntryExt):

    ########################################################################
    # Base can be either a fstab format line of text, of another MountEntry
    # object.
    def __init__(self,base=None):
        super(MountEntryExtSwap,self).__init__(base)

        if isinstance(base,StringType) or isinstance(base,UnicodeType):
            options = self.extraoptions.split(",")
            try:
                options.remove('defaults')
            except ValueError:
                pass
            self.extraoptions = u",".join(options)

    ########################################################################
    def copy(self,newobject=None):
        if newobject is None:
            newobject = MountEntryExtSwap()
        super(MountEntryExtSwap,self).copy(newobject)
        return newobject

    ########################################################################
    def getFstabOptions(self):
        options = super(MountEntryExtSwap,self).getFstabOptions()
        if len(options)==0:
            # Make sure there is at least one option in the list.
            options.append('defaults')
        return options

    ########################################################################
    def updateStatus(self,mtablist):
        ourdevice = self.device
        # If the device is a symlink, then grab the complete target.
        if os.path.islink(ourdevice):
            ourdevice = '/dev/' + os.readlink(ourdevice)
        fhandle = open("/proc/swaps")
        lines = fhandle.readlines()
        fhandle.close()

        try: del lines[0]
        except IndexError: pass

        self.enabled = False
        for line in lines:
            parts = line.split()
            if parts[0]==ourdevice:
                self.enabled = True

    ########################################################################
    # Returns a list of command+arguments
    def enable(self,parentdialog):
        self._setBusy(parentdialog,True)
        try:
            (rc,output) = SimpleCommandRunner().run(['/sbin/swapon',self.device])
            if rc!=0:
                msg = i18n("An error occurred while enabling swap partition %1.\n\nThe system reported: %2").arg(self.device).arg(output)
                KMessageBox.sorry(parentdialog,msg,\
                    i18n("Error occurred while enabling swap partition %1").arg(self.device))
        finally:
            self._setBusy(parentdialog,False)

    ########################################################################
    # Returns a list of command+arguments or None.
    def disable(self,parentdialog):
        self._setBusy(parentdialog,True)
        try:
            (rc,output) = SimpleCommandRunner().run(['/sbin/swapoff',self.device])
            if rc!=0:
                msg = i18n("An error occurred while disabling swap partition %1.\n\nThe system reported: %2").arg(self.device).arg(output)
                KMessageBox.sorry(parentdialog,msg,\
                    i18n("Error occurred while disabling swap partition %1").arg(self.device))
        finally:
            self._setBusy(parentdialog,False)

############################################################################
# This represents a mount entry.
#
# It also does a little trick with the MountEntryExt classes. MountEntry
# objects kind of 'change' class under your nose when they are set to
# different mount types. The handling of the different kinds of mount types
# is handled by MountEntryExt objects and subclasses.

class MountEntry(object):

    MountTypes = {
        'proc' : (MountEntryExtSystem,i18n("proc")),
        'sysfs' : (MountEntryExtSystem,i18n("sysfs")),
        'rootfs' : (MountEntryExtSystem,i18n("rootfs")),
        'bdev' : (MountEntryExtSystem,i18n("bdev")),
        'sockfs' : (MountEntryExtSystem,i18n("sockfs")),
        'tmpfs' : (MountEntryExtSystem,i18n("tmpfs")),
        'shm' : (MountEntryExtSystem,i18n("shm")),
        'pipefs' : (MountEntryExtSystem,i18n("pipefs")),
        'devfs' : (MountEntryExtSystem,i18n("devfs - Device File System")),
        'devpts' : (MountEntryExtSystem,i18n("devpts")),
        'ramfs' : (MountEntryExtSystem,i18n("ramfs")),
        'auto' : (MountEntryExtCommonUnix,i18n("Automatic")),
        'usbdevfs' : (MountEntryExtSystem,i18n("usbdevfs")),
        'usbfs' : (MountEntryExtSystem,i18n("usbfs")),
        'supermount' : (MountEntryExt,i18n("supermount")),
        'swap' : (MountEntryExtSwap,i18n("Swap - Linux Swap Space")),

        'nfs' : (MountEntryExtCommonUnix,i18n("NFS - Network File System")),
        'smbfs' : (MountEntryExtSMB,i18n("Windows File Sharing")),

        'ext2' : (MountEntryExtCommonUnixLocal,i18n("Ext2 - Second Extended FS")),
        'ext3' : (MountEntryExtCommonUnixLocal,i18n("Ext3 - Third Extended FS")),
        'reiserfs' : (MountEntryExtCommonUnixLocal,i18n("ReiserFS")),
        'reiser4' : (MountEntryExtCommonUnixLocal,i18n("Reiser4")),
        'xfs' : (MountEntryExtCommonUnixLocal,i18n("XFS - SGI's journaling filesystem")),
        'hfs' : (MountEntryExtCommonUnixLocal,i18n("HFS - Apple's Hierarchical File System")),
        'hfsplus' : (MountEntryExtVFAT,i18n("HFS+ - Apple's modernized Hierarchical File System")),
        'jfs' : (MountEntryExtCommonUnixLocal,i18n("JFS - IBM's Journaled File System")),
        'vfat' : (MountEntryExtVFAT,i18n("VFAT - Microsoft FAT File Systems")),
        'ntfs' : (MountEntryExtVFAT,i18n("NTFS - NT File System")),
        'udf' : (MountEntryExtSystem,i18n("udf")),
        'iso9660' : (MountEntryExt,i18n("iso9660 - CD-ROM")),
    }

    notInFstab = False
    maydisable = True # Some entries, such as /proc can't be disabled.

    ########################################################################
    # Base can be either a fstab format line of text, of another MountEntry
    # object.
    def __init__(self,base=None):
        try:
            self.extensionObjects = {}
            if base==None:
                self.mounttype = 'ext2'
            elif isinstance(base,StringType) or isinstance(base,UnicodeType):
                parts = base.split()
                self.mounttype = parts[2]
                # 'udf,iso9660' seems default for some devices in fstab,
                # check if all listed filesystems are available, if yes set to 'auto'.
                if len(self.mounttype.split(',')) > 1:
                    """
                    # We could check here, but then we'd need a reference to MicroHAL.
                    #for m in self.mounttype.split(','):
                        #if m not in supported_fs:
                        #    print "Filesystem ", m, "not supported by the kernel"
                        #    break
                    """
                    self.mounttype = "auto"
            else:
                # This is a new entry, but it's based on another one.
                self.mounttype = base.mounttype
            self.extension = self.MountTypes[self.mounttype][0](base)
            self.extensionObjects[self.mounttype] = self.extension
        except (KeyError,IndexError):
            raise InvalidMountEntryError, u"Unable to parse mount entry:"+unicode(base)

    ########################################################################
    def getMountType(self):
        return self.mounttype

    ########################################################################
    def setMountType(self,newtypename):
        if newtypename not in self.extensionObjects:
            try:
                self.extensionObjects[newtypename] = self.MountTypes[newtypename][0](self.extension)
            except KeyError:
                raise NotImplementedError, "Unknown mounttype:"+newtypename
        self.mounttype = newtypename
        self.extension = self.extensionObjects[newtypename]
        self.extension.setMountType(newtypename)

    ########################################################################
    def copy(self):
        newentry = MountEntry()
        newentry.mounttype = self.mounttype
        newext = self.extension.copy()
        newentry.extensionObjects[self.mounttype] = newext
        newentry.extension = newext
        return newentry

    ########################################################################
    def inPlaceCopyFrom(self,sourceentry):
        self.extension.cleanup()

        tmpcopy = sourceentry.copy()
        self.extensionObjects = tmpcopy.extensionObjects
        self.mounttype = tmpcopy.mounttype
        self.extension = tmpcopy.extension

    # Override the attribute lookup, set/get, to use the extension object
    ########################################################################
    def __getattr__(self,name):
        return getattr(self.extension,name)

    ########################################################################
# FIXME
##     def __setattr__(self,name,value):
##         if 'extension' in self.__dict__:
##             if name in self.extension.__dict__:
##                 setattr(self.extension,name,value)
##                 return
##         self.__dict__[name] = value

    ########################################################################
    def getMountTypes():
        return MountEntry.MountTypes.keys()
    getMountTypes = staticmethod(getMountTypes)

    ########################################################################
    def getMountTypeLongName(typename):
        return MountEntry.MountTypes[typename][1]
    getMountTypeLongName = staticmethod(getMountTypeLongName)

    ########################################################################
    def encodeMountEntryString(string):
        newstring = u""
        for c in string:
            if c==' ':
                newstring += "\\040"
            elif c=="\t":
                newstring += "\\012"
            elif c=='\\':
                newstring += "\\134"
            else:
                newstring += c
        return newstring
    encodeMountEntryString = staticmethod(encodeMountEntryString)

    ########################################################################
    def decodeMountEntryString(string):
        newstring = ""
        while string!="":
            if len(string)>=4 and string[0]=='\\' and isoct(string[1]) \
                    and isoct(string[2]) and isoct(string[3]):
                newstring += chr(64*(ord(string[1])-ord('0')) + \
                    8*(ord(string[2])-ord('0')) + (ord(string[3])-ord('0')))
                string = string[4:]
            else:            
                newstring += string[0]
                string = string[1:]
        return newstring    
    decodeMountEntryString = staticmethod(decodeMountEntryString)            

############################################################################
01059 class MountEntryComment(MountEntry):
    """ This represents a comment mount entry or generally something that we don't
        understand (might be comment, might be fstab syntax we don't know, might be
        a faulty line in there). We don't want to wipe that stuff out, but we can't
        deal with it in a sensible way, so we keep it in a MountEntryComment, 
        exclude it from most operations, but will write it back to fstab afterwards

        As a result of that we only define the stuff that's necessary, namely saving
        the fstab line and returning it when writing."""

    ########################################################################
    def __init__(self,base=None):
        self.row = base

    ########################################################################
    def getFstabLine(self): return self.row

############################################################################
def isoct(c): return c in '01234567'

############################################################################
class InvalidMountEntryError(Exception):

    ########################################################################
    def __init__(self,arg=None):
        self.arg = arg

    ########################################################################
    def __str__(self):
        return str(self.arg)

############################################################################
class MountTable(object):

    ########################################################################
    def __init__(self,fstab_filename,mtab_filename):
        self.fstab_filename = fstab_filename
        self.mtab_filename = mtab_filename

        self.entries = []
        self.allentries = []

        # sysfs does not need an entry in fstab, so we add it even if it's not
        # in there, it's mounted automatically anyway and shows up in mtab
        sysfs_in_fstab = False
        usbdevfs_in_fstab = False

        fhandle = codecs.open(self.fstab_filename,'r',locale.getpreferredencoding())
        for row in fhandle.readlines():
            row = row.strip('\n') # Carefully remove any trailing newline. 
            if row.strip().startswith("#") or row.strip()=="":
                entry = MountEntryComment(row)
            else:
                try:
                    entry = MountEntry(row)
                    self.append(entry)

                    if entry.getMountType() == "sysfs":
                        sysfs_in_fstab = True
                    if entry.getMountType() == "usbdevfs":
                        usbdevfs_in_fstab = True
                    if entry.getMountType() == "proc":
                        entry.maydisable = False
                except InvalidMountEntryError:
                    entry = MountEntryComment(row)
            # We keep a list with references to _all_ entries, also the comments, 
            # this is the  one we'll use to write out our new fstab, only 'real' 
            # entries (real == entries we understand) are added to self to let them 
            # be handled by iterator.
            # allentries includes comments and invalid lines, self doesn't.
            self.allentries.append(entry)
        fhandle.close()

        if not sysfs_in_fstab:
            sysfsentry = MountEntry(u"sysfs /sys sysfs defaults 0 0")
            sysfsentry.notInFstab = True
            sysfsentry.maydisable = False
            self.append(sysfsentry)

        if not usbdevfs_in_fstab:
            usbdevfsentry = MountEntry(u"usbdevfs /proc/bus/usb usbdevfs defaults 0 0")
            usbdevfsentry.notInFstab = True
            usbdevfsentry.maydisable = False
            self.append(usbdevfsentry) 

        self.updateStatus()

    ########################################################################
    def append(self,entry):
        self.entries.append(entry)

    ########################################################################
    def remove(self,entry):
        self.allentries.remove(entry)
        entry.cleanup()

    ########################################################################
    def updateStatus(self,entry=None):
        mtablist = self.getMtabList()
        if entry==None:
            for entry in self.entries:
                entry.updateStatus(mtablist)
        else:
            entry.updateStatus(mtablist)

    ########################################################################
    def getMtabList(self):
        fhandle = open(self.mtab_filename)
        mtablist = []
        for row in fhandle.readlines():
            if row.strip()[0]!='#':
                parts = row.split()
                mtablist.append(parts[1])
        fhandle.close()
        return mtablist

    ########################################################################
    def updateFstabOnDisk(self):
        fhandle = None
        try:
            try:
                fhandle = codecs.open(self.fstab_filename+"~","w",locale.getpreferredencoding(),'replace')
                for entry in self.allentries:
                    if not entry.notInFstab:
                        line = entry.getFstabLine()
                        fhandle.write(line+u"\n")
                fhandle.close()
                fhandle = None

                # Move it over the original    
                os.rename(self.fstab_filename+"~",self.fstab_filename)
                return True
            finally:
                if fhandle:
                    fhandle.close()
        except IOError:
            return False

    ########################################################################
    # We make this class look like a container, and just forward everything
    # on to the entries attribute.
    def __contains__(self,item):
        return self.entries.__contains(item)
    ########################################################################
    def __delitem__(self,key):
        raise NotImplementedError, "No __delitem__ on MountTable."

    ########################################################################
    def __getitem__(self,key):
        return self.entries.__getitem__(key)
    ########################################################################

    def __iter__(self):
        return self.entries.__iter__()
    ########################################################################
    def __len__(self):
        return self.entries.__len__()
    ########################################################################
    def __setitem__(self,key,value):
        raise NotImplementedError, "No __setitem__ on MountTable."

############################################################################
class MountEntryDialogOptions(QWidget):

    deviceexample = i18n("(for example /dev/hdb3)")

    ########################################################################
    def __init__(self,parent,showmountpoint=True,showdevice=True,showfs_freq=True,showfs_passno=True):
        QWidget.__init__(self,parent)
        self.showmountpoint = showmountpoint
        self.showdevice = showdevice
        self.showfs_freq = showfs_freq
        self.showfs_passno = showfs_passno
        self._fillPage()
        # TODO: KDirSelectDialog needs "Create new Folder"
        self.mountpointdialog = KDirSelectDialog("/",True,self,"Select Mount Point",True)

    ########################################################################
    def _fillPage(self):
        grid = QGridLayout(self,6,2)
        grid.setSpacing(KDialog.spacingHint())
        grid.setColStretch(0,0)
        grid.setColStretch(2,0)

        for x in range(0,4):
            grid.setRowStretch(x,0)
        grid.setRowStretch(5,1)

        if self.showmountpoint:
            label = QLabel(i18n("Mount Point:"),self)
            grid.addWidget(label,0,0)

            hbox = QHBox(self)
            hbox.setSpacing(KDialog.spacingHint())
            self.mountpointlineedit = KLineEdit(hbox)
            hbox.setStretchFactor(self.mountpointlineedit,1)
            #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged)
            self.mountpointbutton = KPushButton(i18n("Browse..."),hbox)
            hbox.setStretchFactor(self.mountpointbutton,0)
            self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked)
            grid.addMultiCellWidget(hbox,0,0,1,3)

        if self.showdevice:
            label = QLabel(i18n("Device:"),self)
            grid.addWidget(label,1,0)
            example = QLabel(self.deviceexample,self)
            grid.addWidget(example,2,1)

            self.devicelineedit = KLineEdit(self)
            grid.addMultiCellWidget(self.devicelineedit,1,1,1,3)

        label = QLabel(i18n("Options:"),self)
        grid.addWidget(label,3,0)
        self.optionslineedit = KLineEdit(self)
        grid.addMultiCellWidget(self.optionslineedit,3,3,1,3)

        if self.showfs_freq:
            label = QLabel(i18n("fs_freq:"),self)
            grid.addWidget(label,4,0)
            self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,self)
            grid.addWidget(self.fsfreqspinbox,4,1)

        if self.showfs_passno:
            label = QLabel(i18n("fs_passno:"),self)
            grid.addWidget(label,4,2)
            self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,self)
            grid.addWidget(self.fspassnospinbox,4,3)


    ########################################################################
    def displayMountEntry(self,entry):
        if self.showmountpoint:
            self.mountpointlineedit.setText(entry.getMountPoint())
        if self.showdevice:
            self.devicelineedit.setText(entry.getDevice())
        self.optionslineedit.setText(entry.getExtraOptions())
        if self.showfs_freq:
            self.fsfreqspinbox.setValue(entry.getFSFreq())
        if self.showfs_passno:
            self.fspassnospinbox.setValue(entry.getFSPassno())

    ########################################################################
    def undisplayMountEntry(self,entry):
        if self.showmountpoint:
            entry.setMountPoint( unicode(self.mountpointlineedit.text()) )
        if self.showdevice:
            entry.setDevice( unicode(self.devicelineedit.text()) )
        entry.setExtraOptions( unicode(self.optionslineedit.text()) )
        if self.showfs_freq:
            entry.setFSFreq(self.fsfreqspinbox.value())
        if self.showfs_passno:
            entry.setFSPassno(self.fspassnospinbox.value())

    ########################################################################
    def slotBrowseMountPointClicked(self):
        fileurl = KURL()
        fileurl.setPath(self.mountpointlineedit.text())
        self.mountpointdialog.setCurrentURL(fileurl)
        if self.mountpointdialog.exec_loop()==QDialog.Accepted:
            self.mountpointlineedit.setText(self.mountpointdialog.url().path())

############################################################################
class MountEntryDialogOptionsCommonUnix(MountEntryDialogOptions):

    ########################################################################
    def __init__(self,parent):
        MountEntryDialogOptions.__init__(self,parent)
        self.advanceddialog = MountEntryAdvancedCommonUnixDialog(None)

    ########################################################################
    def _fillPage(self):
        grid = QGridLayout(self,8,2)
        grid.setSpacing(KDialog.spacingHint())
        grid.setColStretch(0,0)
        grid.setRowStretch(6,1)

        label = QLabel(i18n("Mount Point:"),self)
        grid.addWidget(label,0,0)
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        self.mountpointlineedit = KLineEdit(hbox)
        hbox.setStretchFactor(self.mountpointlineedit,1)
        #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged)
        self.mountpointbutton = KPushButton(i18n("Browse..."),hbox)
        hbox.setStretchFactor(self.mountpointbutton,0)
        self.connect(self.mountpointbutton,SIGNAL("clicked()"), \
            self.slotBrowseMountPointClicked)
        grid.addWidget(hbox,0,1)

        label = QLabel(i18n("Device:"),self)
        grid.addWidget(label,1,0)
        example = QLabel(self.deviceexample,self)
        grid.addWidget(example,2,1)
        self.devicelineedit = KLineEdit(self)
        grid.addWidget(self.devicelineedit,1,1)

        self.autocheckbox = QCheckBox(i18n("Enable at start up"),self)
        grid.addWidget(self.autocheckbox,3,1)

        self.writeablecheckbox = QCheckBox(i18n("Writeable"),self)
        grid.addWidget(self.writeablecheckbox,4,1)

        label = QLabel(i18n("Mount Permission:"),self)
        grid.addWidget(label,5,0)
        self.usermountcombobox = KComboBox(self)
        self.usermountcombobox.insertItem(i18n("Root user only may enable/disable"))
        self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable"))
        self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime"))
        self.usermountcombobox.insertItem(i18n("Device owner may enable/disable"))
        grid.addWidget(self.usermountcombobox,5,1)

        #grid.addWidget(,9,0)
        button = KPushButton(i18n("Advanced..."),self)
        button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked)
        grid.addWidget(button,7,1,Qt.AlignRight)

    ########################################################################
    def displayMountEntry(self,entry):
        self.devicelineedit.setText(entry.getDevice())
        self.mountpointlineedit.setText(entry.getMountPoint())
        self.options = entry.getExtraOptions()
        self.fsfreq = entry.getFSFreq()
        self.fspassno = entry.getFSPassno()
        self.autocheckbox.setChecked(entry.getMountAtBoot())
        self.writeablecheckbox.setChecked(entry.getWritable())
        self.accesstime = entry.getAtime()
        self.allowexecutable = entry.getAllowExecutables()
        self.allowsuid = entry.getSUID()
        self.usedevpoints = entry.getUseDevPoints()
        self.usermountcombobox.setCurrentItem(entry.getAllowUserMount())

    ########################################################################
    def undisplayMountEntry(self,entry):
        entry.setDevice( unicode(self.devicelineedit.text()) )
        entry.setMountPoint( unicode(self.mountpointlineedit.text()) )
        entry.setExtraOptions(self.options)
        entry.setFSFreq(self.fsfreq)
        entry.setFSPassno(self.fspassno)
        entry.setAtime(self.accesstime)
        entry.setMountAtBoot(self.autocheckbox.isChecked())
        entry.setWritable(self.writeablecheckbox.isChecked())
        entry.setUseDevPoints(self.usedevpoints)
        entry.setAllowExecutable(self.allowexecutable)
        entry.setSUID(self.allowsuid)
        entry.setAllowUserMount(self.usermountcombobox.currentItem())

    ########################################################################
    def slotAdvancedClicked(self):
        (self.accesstime, self.allowexecutable, self.allowsuid, self.usedevpoints, self.options, self.fsfreq, self.fspassno)\
            = self.advanceddialog.do(self.accesstime, self.allowexecutable, self.allowsuid, self.usedevpoints, self.options, self.fsfreq, self.fspassno)

############################################################################
class MountEntryDialogOptionsSys(MountEntryDialogOptions):
    ########################################################################
    def __init__(self,parent):
        MountEntryDialogOptions.__init__(self,parent,True,False,False,False)

############################################################################
class MountEntryDialogOptionsSwap(MountEntryDialogOptions):
    ########################################################################
    def __init__(self,parent):
        MountEntryDialogOptions.__init__(self,parent,False,True,False,False)

class MountEntryDialogOptionsNfs(MountEntryDialogOptionsCommonUnix):

    deviceexample = i18n("(for example 192.168.1.66:/export)")

############################################################################
class MountEntryDialogOptionsVFAT(MountEntryDialogOptions):
    ########################################################################
    def __init__(self,parent):
        MountEntryDialogOptions.__init__(self,parent)
        self.advanceddialog = MountEntryAdvancedPlainDialog(None)
        self.updatinggui= False

    ########################################################################
    def _fillPage(self):
        grid = QGridLayout(self,11,2)
        grid.setSpacing(KDialog.spacingHint())
        grid.setColStretch(0,0)
        grid.setColStretch(2,0)
        grid.setRowStretch(10,1)

        label = QLabel(i18n("Mount Point:"),self)
        grid.addWidget(label,0,0)
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        self.mountpointlineedit = KLineEdit(hbox)
        hbox.setStretchFactor(self.mountpointlineedit,1)
        #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged)
        self.mountpointbutton = KPushButton(i18n("Browse..."),hbox)
        hbox.setStretchFactor(self.mountpointbutton,0)
        self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked)
        grid.addMultiCellWidget(hbox,0,0,1,3)

        label = QLabel(i18n("Device:"),self)
        grid.addWidget(label,1,0)
        self.devicelineedit = KLineEdit(self)
        grid.addMultiCellWidget(self.devicelineedit,1,1,1,3)
        example = QLabel(self.deviceexample,self)
        grid.addWidget(example,2,1)
        self.autocheckbox = QCheckBox(i18n("Enable at start up"),self)
        grid.addMultiCellWidget(self.autocheckbox,3,3,1,3)

        # Security & Safety line.
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        tmplabel = QLabel(hbox)
        tmplabel.setPixmap(UserIcon("hi16-lock"))
        hbox.setStretchFactor(tmplabel,0)
        tmplabel = QLabel(hbox)
        tmplabel.setText(i18n("Security & Safety"))
        hbox.setStretchFactor(tmplabel,0)
        sep = KSeparator(KSeparator.Horizontal,hbox)
        hbox.setStretchFactor(sep,1)
        grid.addMultiCellWidget(hbox,4,4,0,3)

        self.writeablecheckbox = QCheckBox(i18n("Writeable"),self)
        grid.addMultiCellWidget(self.writeablecheckbox,4,4,1,3)

        label = QLabel(i18n("Files belong to user:"),self)
        grid.addWidget(label,6,0)
        self.uidcombobox = UserComboBox(self)
        grid.addMultiCellWidget(self.uidcombobox,6,6,1,3)

        label = QLabel(i18n("Files belong to group:"),self)
        grid.addWidget(label,7,0)
        self.gidcombobox = GroupComboBox(self)
        grid.addMultiCellWidget(self.gidcombobox,7,7,1,3)

        label = QLabel(i18n("Mount Permission:"),self)
        grid.addWidget(label,8,0)
        self.usermountcombobox = KComboBox(self)
        self.usermountcombobox.insertItem(i18n("Root user only may enable/disable"))
        self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable"))
        self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime"))
        self.usermountcombobox.insertItem(i18n("Device owner may enable/disable"))
        grid.addMultiCellWidget(self.usermountcombobox,8,8,1,3)

        self.suppresspermissionerrorcheckbox = QCheckBox(i18n("Suppress permission errors"),self)
        grid.addMultiCellWidget(self.suppresspermissionerrorcheckbox,9,9,1,3)

        button = KPushButton(i18n("Advanced..."),self)
        button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked)
        grid.addMultiCellWidget(button,11,11,1,3,Qt.AlignRight)

    ########################################################################
    def displayMountEntry(self,entry):
        self.devicelineedit.setText(entry.getDevice())
        self.mountpointlineedit.setText(entry.getMountPoint())
        self.options = entry.getExtraOptions()
        self.fsfreq = entry.getFSFreq()
        self.fspassno = entry.getFSPassno()
        self.writeablecheckbox.setChecked(entry.getWritable())
        self.autocheckbox.setChecked(entry.getMountAtBoot())
        self.usermountcombobox.setCurrentItem(entry.getAllowUserMount())
        self.uidcombobox.setUID(entry.getUID())
        self.gidcombobox.setGID(entry.getGID())
        self.suppresspermissionerrorcheckbox.setChecked(entry.getSuppressPermissionErrors())

    ########################################################################
    def undisplayMountEntry(self,entry):
        entry.setDevice( unicode(self.devicelineedit.text()) )
        entry.setMountPoint( unicode(self.mountpointlineedit.text()) )
        entry.setExtraOptions(self.options)
        entry.setFSFreq(self.fsfreq)
        entry.setFSPassno(self.fspassno)
        entry.setMountAtBoot(self.autocheckbox.isChecked())
        entry.setWritable(self.writeablecheckbox.isChecked())
        entry.setAllowUserMount(self.usermountcombobox.currentItem())
        entry.setUID(self.uidcombobox.UID())
        entry.setGID(self.gidcombobox.GID())
        entry.setSuppressPermissionErrors(self.suppresspermissionerrorcheckbox.isChecked())

    ########################################################################
    def slotAdvancedClicked(self):
        (self.options, self.fsfreq, self.fspassno)\
            = self.advanceddialog.do(self.options, self.fsfreq, self.fspassno)

############################################################################
class MountEntryDialogOptionsSMB(MountEntryDialogOptions):

    ########################################################################
    def __init__(self,parent):
        MountEntryDialogOptions.__init__(self,parent)
        self.advanceddialog = MountEntryAdvancedPlainDialog(None)
        self.updatinggui= False

    ########################################################################
    def _fillPage(self):
        grid = QGridLayout(self,14,4)
        grid.setSpacing(KDialog.spacingHint())
        grid.setColStretch(0,0)
        grid.setColStretch(2,0)
        grid.setRowStretch(12,1)

        label = QLabel(i18n("Mount Point:"),self)
        grid.addWidget(label,0,0)
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        self.mountpointlineedit = KLineEdit(hbox)
        hbox.setStretchFactor(self.mountpointlineedit,1)
        #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged)
        self.mountpointbutton = KPushButton(i18n("Browse..."),hbox)
        hbox.setStretchFactor(self.mountpointbutton,0)
        self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked)
        grid.addMultiCellWidget(hbox,0,0,1,3)

        label = QLabel(i18n("Network Share:"),self)
        grid.addWidget(label,1,0)
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        self.devicelineedit = KLineEdit(hbox)
        hbox.setStretchFactor(self.devicelineedit,1)
        self.devicelinebutton = KPushButton(i18n("Scan..."),hbox)
        hbox.setStretchFactor(self.devicelinebutton,0)
        self.connect(self.devicelinebutton,SIGNAL("clicked()"),self.slotBrowseDeviceLineClicked)
        grid.addMultiCellWidget(hbox,1,1,1,3)

        # Connect as:
        connectaslabel = QLabel(self)
        connectaslabel.setText(i18n("Connect as:"))
        grid.addWidget(connectaslabel,2,0)

        self.guestradio = QRadioButton(self)
        self.guestradio.setChecked(True)
        grid.addWidget(self.guestradio,2,1)
        tmplabel = QLabel(self)
        tmplabel.setText(i18n("Guest"))
        grid.addMultiCellWidget(tmplabel,2,2,2,3)
        self.connect(self.guestradio,SIGNAL("stateChanged(int)"),self.slotGuestRadioClicked)

        self.userradio = QRadioButton(self)
        grid.addWidget(self.userradio,3,1)
        tmplabel = QLabel(self)
        tmplabel.setText(i18n("Username:"))
        grid.addWidget(tmplabel,3,2)
        self.connect(self.userradio,SIGNAL("stateChanged(int)"),self.slotUserRadioClicked)

        self.usernameedit = KLineEdit(self)
        grid.addWidget(self.usernameedit,3,3)

        tmplabel = QLabel(self)
        tmplabel.setText(i18n("Password:"))
        grid.addWidget(tmplabel,4,2)

        self.passwordedit = KLineEdit(self)
        grid.addWidget(self.passwordedit,4,3)

        self.autocheckbox = QCheckBox(i18n("Enable at start up"),self)
        grid.addMultiCellWidget(self.autocheckbox,5,5,1,3)

        # Security & Safety line.
        hbox = QHBox(self)
        hbox.setSpacing(KDialog.spacingHint())
        tmplabel = QLabel(hbox)
        tmplabel.setPixmap(UserIcon("hi16-lock"))
        hbox.setStretchFactor(tmplabel,0)
        tmplabel = QLabel(hbox)
        tmplabel.setText(i18n("Security & Safety"))
        hbox.setStretchFactor(tmplabel,0)
        sep = KSeparator(KSeparator.Horizontal,hbox)
        hbox.setStretchFactor(sep,1)
        grid.addMultiCellWidget(hbox,6,6,0,3)

        self.writeablecheckbox = QCheckBox(i18n("Writeable"),self)
        grid.addMultiCellWidget(self.writeablecheckbox,7,7,1,3)

        label = QLabel(i18n("Files belong to user:"),self)
        grid.addWidget(label,8,0)
        self.uidcombobox = UserComboBox(self)
        grid.addMultiCellWidget(self.uidcombobox,8,8,1,3)

        label = QLabel(i18n("Files belong to group:"),self)
        grid.addWidget(label,9,0)
        self.gidcombobox = GroupComboBox(self)
        grid.addMultiCellWidget(self.gidcombobox,9,9,1,3)

        label = QLabel(i18n("Mount Permission:"),self)
        grid.addWidget(label,10,0)
        self.usermountcombobox = KComboBox(self)
        self.usermountcombobox.insertItem(i18n("Root user only may enable/disable"))
        self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable"))
        self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime"))
        self.usermountcombobox.insertItem(i18n("Device owner may enable/disable"))
        grid.addMultiCellWidget(self.usermountcombobox,10,10,1,3)

        button = KPushButton(i18n("Advanced..."),self)
        button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked)
        grid.addMultiCellWidget(button,13,13,1,3,Qt.AlignRight)

        self.selectsmbdialog = None

    ########################################################################
    def slotBrowseDeviceLineClicked(self):
        if self.updatinggui:
            return
        self.updatinggui = True

        if self.selectsmbdialog is None:
            self.selectsmbdialog = SMBShareSelectDialog(None)

        # This just converts a \\zootv\data\ share name to smb:/zootv/data
        parts = [x.replace("/",'\\') for x in unicode(self.devicelineedit.text()).split('\\') if x!=""]
        oldurl = u"smb:/"+("/".join(parts) )

        urlobj = KURL(oldurl)
        if self.userradio.isChecked():
            urlobj.setUser(self.usernameedit.text())
            urlobj.setPass(self.passwordedit.text())
            
        newurlobj = self.selectsmbdialog.choose(urlobj)

        # This just converts smb:/zootv/data to \\zootv\data.
        plainurl = KURL(newurlobj)
        plainurl.setUser(QString.null)
        plainurl.setPass(QString.null)
        parts = [x.replace('\\',"/") for x in unicode(plainurl.url())[4:].split("/") if x !=""]
        self.devicelineedit.setText(r'\\'+('\\'.join(parts)))

        if not newurlobj.hasUser():
            self.guestradio.setChecked(True)
            self.userradio.setChecked(False)        
            self.passwordedit.setEnabled(False)
            self.usernameedit.setEnabled(False)
            self.usernameedit.setText("")
            self.passwordedit.setText("")
        else:
            self.guestradio.setChecked(False)
            self.userradio.setChecked(True)
            self.passwordedit.setEnabled(True)
            self.usernameedit.setEnabled(True)
            self.usernameedit.setText(newurlobj.user())
            self.passwordedit.setText(newurlobj.pass_())

        self.updatinggui = False

    ########################################################################
    def displayMountEntry(self,entry):
        self.devicelineedit.setText(entry.getDevice())
        self.mountpointlineedit.setText(entry.getMountPoint())
        self.options = entry.getExtraOptions()
        self.fsfreq = entry.getFSFreq()
        self.fspassno = entry.getFSPassno()
        self.writeablecheckbox.setChecked(entry.getWritable())
        self.autocheckbox.setChecked(entry.getMountAtBoot())
        self.usermountcombobox.setCurrentItem(entry.getAllowUserMount())
        self.uidcombobox.setUID(entry.getUID())
        self.gidcombobox.setGID(entry.getGID())

        if entry.getUsername() is None:
            self.guestradio.setChecked(True)
            self.userradio.setChecked(False)        
            self.passwordedit.setEnabled(False)
            self.usernameedit.setEnabled(False)
            self.usernameedit.setText("")
            self.passwordedit.setText("")
        else:
            self.guestradio.setChecked(False)
            self.userradio.setChecked(True)
            self.passwordedit.setEnabled(True)
            self.usernameedit.setEnabled(True)
            self.usernameedit.setText(entry.getUsername())
            self.passwordedit.setText(entry.getPassword())

    ########################################################################
    def undisplayMountEntry(self,entry):
        entry.setDevice( unicode(self.devicelineedit.text()) )
        entry.setMountPoint( unicode(self.mountpointlineedit.text()) )
        entry.setExtraOptions(self.options)
        entry.setFSFreq(self.fsfreq)
        entry.setFSPassno(self.fspassno)
        entry.setMountAtBoot(self.autocheckbox.isChecked())
        entry.setWritable(self.writeablecheckbox.isChecked())
        entry.setAllowUserMount(self.usermountcombobox.currentItem())
        entry.setUID(self.uidcombobox.UID())
        entry.setGID(self.gidcombobox.GID())

        if self.guestradio.isChecked():
            entry.setUsername(None)
            entry.setPassword(None)
        else:
            entry.setUsername( unicode(self.usernameedit.text()) )
            entry.setPassword( unicode(self.passwordedit.text()) )

    ########################################################################
    def slotAdvancedClicked(self):
        (self.options, self.fsfreq, self.fspassno)\
            = self.advanceddialog.do(self.options, self.fsfreq, self.fspassno)

    ########################################################################
    def slotGuestRadioClicked(self,state):
        if self.updatinggui:
            return
        self.updatinggui = True

        if state==QButton.Off:
            self.guestradio.setChecked(True)
        self.userradio.setChecked(False)

        self.passwordedit.setEnabled(False)
        self.usernameedit.setEnabled(False)

        self.updatinggui = False

    ########################################################################
    def slotUserRadioClicked(self,state):
        if self.updatinggui:
            return
        self.updatinggui = True
        if state==QButton.Off:
            self.userradio.setChecked(True)
        self.guestradio.setChecked(False)

        self.passwordedit.setEnabled(True)
        self.usernameedit.setEnabled(True)

        self.updatinggui = False

############################################################################
01782 class ROListBoxItem(QListBoxPixmap):
    """A read-only ListBox item that also uses the 'alternate' background colour
    as background.
    """
    def __init__(self,listbox,pix,text):
        QListBoxPixmap.__init__(self,listbox,pix,text)
        self.setSelectable(False)
    def paint(self,p):
        boldfont = QFont(p.font())
        boldfont.setWeight(QFont.Bold)
        p.setFont(boldfont)
        p.setBackgroundColor(KGlobalSettings.alternateBackgroundColor())
        p.eraseRect(0,0,self.listBox().width(),self.height(self.listBox()))
        QListBoxPixmap.paint(self,p)

############################################################################
class MountEntryDialog(KDialogBase):

    MountTypeEditorsDisk = {
        'ext2' : MountEntryDialogOptionsCommonUnix,
        'ext3' : MountEntryDialogOptionsCommonUnix,
        'reiserfs' : MountEntryDialogOptionsCommonUnix,
        'reiser4' : MountEntryDialogOptionsCommonUnix,
        'xfs' : MountEntryDialogOptionsCommonUnix,
        'jfs' : MountEntryDialogOptionsCommonUnix,
        'vfat' : MountEntryDialogOptionsVFAT,
        'ntfs' : MountEntryDialogOptionsVFAT,
        'hfsplus' : MountEntryDialogOptionsVFAT,
        'udf' : MountEntryDialogOptions,
        'iso9660' : MountEntryDialogOptions,
    }
    MountTypeEditorsNetwork = {
        'nfs' : MountEntryDialogOptionsNfs,
        'smbfs' : MountEntryDialogOptionsSMB,
    }
    MountTypeEditorsSystem = {
        'proc' : MountEntryDialogOptionsSys,
        'sysfs' : MountEntryDialogOptionsSys,
        'rootfs' : MountEntryDialogOptions,
        'bdev' : MountEntryDialogOptions,
        'sockfs' : MountEntryDialogOptions,
        'tmpfs' : MountEntryDialogOptions,
        'shm' : MountEntryDialogOptions,
        'pipefs' : MountEntryDialogOptions,
        'ramfs' : MountEntryDialogOptionsSys,
        'devfs' : MountEntryDialogOptions,
        'devpts' : MountEntryDialogOptionsSys,
        'auto' : MountEntryDialogOptionsCommonUnix,
        'usbdevfs' : MountEntryDialogOptions,
        'usbfs' : MountEntryDialogOptions,
        'supermount' : MountEntryDialogOptions,
        'swap' : MountEntryDialogOptionsSwap
    }

    ########################################################################
    def __init__(self,parent):
        KDialogBase.__init__(self,parent,None,True,"Configuration",KDialogBase.Ok|KDialogBase.Cancel,
            KDialogBase.Cancel)

        self.updatingGUI = True

            # Maps MountEntry classes to MountEntryDialogOptions objects
        self.mountTypeToOptionWidget = {}

        # Maps indexes in the combobox to mounttypes
        self.comboIndexToMountType = []
        self.currentOptionWidget = None

        self.topvbox = QVBox(self)
        self.setMainWidget(self.topvbox)
        self.topvbox.setSpacing(self.spacingHint())

        hb = QHBox(self.topvbox)
        hb.setSpacing(self.spacingHint())
        self.topvbox.setStretchFactor(hb,0)

        label = QLabel(i18n("Type:"),hb)
        hb.setStretchFactor(label,0)
        self.mounttypecombo = KComboBox(hb)

        # Disk types
        ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-hdd"),i18n("Disk Filesystems"))
        self.comboIndexToMountType.append(None)
        items = self.MountTypeEditorsDisk.keys()
        items.sort()
        for mounttype in items:
            self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype))
            self.comboIndexToMountType.append(mounttype)

        # Network types
        ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-network"),i18n("Network Filesystems"))
        self.comboIndexToMountType.append(None)
        items = self.MountTypeEditorsNetwork.keys()
        items.sort()
        for mounttype in items:
            self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype))
            self.comboIndexToMountType.append(mounttype)

        # System types
        ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-blockdevice"),i18n("Operating System"))
        self.comboIndexToMountType.append(None)
        items = self.MountTypeEditorsSystem.keys()
        items.sort()
        for mounttype in items:
            self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype))
            self.comboIndexToMountType.append(mounttype)

        self.MountTypeEditors = self.MountTypeEditorsDisk.copy()
        self.MountTypeEditors.update(self.MountTypeEditorsNetwork)
        self.MountTypeEditors.update(self.MountTypeEditorsSystem)

        #hb.setStretchFactor(self.runlevelcombo,0)
        self.connect(self.mounttypecombo, SIGNAL("activated(int)"), self.slotMountTypeChanged)

        widget = QWidget(hb)
        hb.setStretchFactor(widget,1)

        # Create the stack of option edit widgets.
        gb = QVGroupBox(self.topvbox)
        self.topvbox.setStretchFactor(gb,1)
        self.optionsstack = QWidgetStack(gb)

        for mounttype in self.MountTypeEditors:
            editpage = self.MountTypeEditors[mounttype](self.optionsstack)
            self.mountTypeToOptionWidget[mounttype] = editpage
            self.optionsstack.addWidget(editpage)

        self.microhal = MicroHAL.MicroHAL()

        self.fsunavailablelabel = QHBox(gb)
        self.fsunavailablelabel.setSpacing(KDialog.spacingHint())
        tmplabel = QLabel(self.fsunavailablelabel)
        self.fsunavailablelabel.setStretchFactor(tmplabel,0)
        tmplabel.setPixmap(SmallIcon('info'))
        label = QLabel(i18n("This filesystem type is currently unavailable on the running kernel."),self.fsunavailablelabel)
        self.fsunavailablelabel.setStretchFactor(label,1)
        self.fsunavailablelabel.hide()

        self.updatingGUI = False

    #######################################################################
    def doEditMount(self,mounttable,mountentry):
        self.newEntry = False
        self.mounttable = mounttable
        self.originalMountEntry = mountentry
        self.currentMountEntry = mountentry.copy()

        self.updatingGUI = True
        self.currentOptionWidget = None
        self.selectEntry(self.currentMountEntry.getMountType())
        self.updatingGUI = False

        if self.exec_loop()==QDialog.Accepted:
            # All of the update stuff is in slotOk()
            return True
        return False

    #######################################################################
    def doNewMount(self,mounttable,defaultdevice):
        self.newEntry = True
        self.mounttable = mounttable
        self.currentMountEntry = MountEntry()
        if defaultdevice is not None:
            self.currentMountEntry.setDevice(defaultdevice)
        self.updatingGUI = True
        self.currentOptionWidget = None
        self.selectEntry(self.currentMountEntry.mounttype)
        self.updatingGUI = False
        if self.exec_loop()==QDialog.Accepted:
            self.mounttable.allentries.append(self.currentMountEntry)
            self.mounttable.updateFstabOnDisk()
            return self.currentMountEntry
        return None

    #######################################################################
    def selectEntry(self,mounttype):
        if self.currentOptionWidget!=None:
            # Update the mount entry from the 
            self.currentOptionWidget.undisplayMountEntry(self.currentMountEntry)
        self.currentMountEntry.setMountType(mounttype)
        # Update GUI
        self.mounttypecombo.setCurrentItem(self.comboIndexToMountType.index(mounttype))
        self.currentOptionWidget = self.mountTypeToOptionWidget[mounttype]
        self.currentOptionWidget.displayMountEntry(self.currentMountEntry)
        self.optionsstack.raiseWidget(self.currentOptionWidget)
        if self.microhal.isSupportedFileSystem(mounttype):
            self.fsunavailablelabel.hide()
        else:
            self.fsunavailablelabel.show()

    #######################################################################
    def slotMountTypeChanged(self,index):
        if self.updatingGUI==False:
            self.updatingGUI = True
            self.selectEntry(self.comboIndexToMountType[index])
            self.updatingGUI = False

    #######################################################################
    def slotOk(self):
        self.currentOptionWidget.undisplayMountEntry(self.currentMountEntry)

        conflictentry = None
        if self.newEntry:
            for entry in self.mounttable:
                if entry.getMountPoint()==self.currentMountEntry.getMountPoint():
                    # Conflict found.
                    conflictentry = entry
        else:
            # Check if the mountpoint is already in use elsewhere in the mounttable.
            if self.originalMountEntry.getMountPoint()!=self.currentMountEntry.getMountPoint():
                for entry in self.mounttable:
                    if entry.getMountPoint()==self.currentMountEntry.getMountPoint() and entry is not self.originalMountEntry:
                        # Conflict found.
                        conflictentry = entry
        if conflictentry is not None:
            if KMessageBox.warningContinueCancel(self, \
                    i18n("The mountpoint '%1' is already in use by another entry?\nContinue?").arg(self.currentMountEntry.getMountPoint()), \
                    i18n("Mountpoint already in use"))!=KMessageBox.Continue:
                return 
        
        if self.currentMountEntry.getMountType() in MountEntryDialog.MountTypeEditorsDisk.keys():
            # If device is not in /dev and it's no bind mount, ask if that's meant this way ...
            options = self.currentMountEntry.getFstabOptions()
            if not self.currentMountEntry.getDevice().startswith("/dev/") and not ("loop" in options or "bind" in options):
                ask = KMessageBox.warningYesNoCancel(self,
                    i18n("'%1' does not seem to be a device and the option 'bind' has not been specified in the \"Advanced\" page?\n Should I add the 'loop' option?").arg(self.currentMountEntry.device),
                    i18n("Options may be missing"))
                if ask==KMessageBox.Cancel:
                    return 
                elif ask==KMessageBox.Yes:
                    # Add loop option
                    extraoptions = self.currentMountEntry.getExtraOptions().split(',')
                    extraoptions.append('loop')
                    self.currentMountEntry.setExtraOptions(','.join(extraoptions))

        if not os.path.isdir(self.currentMountEntry.getMountPoint()) and not os.path.isfile(self.currentMountEntry.getMountPoint()):
            ask = KMessageBox.warningYesNoCancel(self,
                    i18n("The mountpoint '%1' does not exist. You will not be able to enable it until it is created.\nShould I create the mountpoint?").arg(self.currentMountEntry.getMountPoint()),
                    i18n("Mountpoint does not exist"))
            if ask==KMessageBox.Cancel:
                return
            elif ask==KMessageBox.Yes:
                os.mkdir(self.currentMountEntry.getMountPoint())
        elif os.path.isfile(self.currentMountEntry.getMountPoint()):
            if KMessageBox.warningContinueCancel(self,
                    i18n("The mountpoint '%1' is a file, but it has to be a directory. You will probably not be able to enable it.\nContinue?").arg(self.currentMountEntry.getMountPoint()),
                    i18n("Invalid mountpoint"))!=KMessageBox.Continue:
                return

        if self.newEntry==False:
            # How to Change a Mount Entry.
            # 1. Disable the exisiting entry (if needed)
            # 2. Update existing entry from the mount table.
            # 3. Enable the updated entry (if needed)
            # 4. Write new fstab file.
            # 5. Enable the new entry (if needed)

            # 1. Disable the exisiting entry (if needed)
            enabled = self.originalMountEntry.isEnabled()
            if enabled:
                self.disablingold = True
                self.originalMountEntry.disable(self)
            self.originalMountEntry.inPlaceCopyFrom(self.currentMountEntry)                
            self.mounttable.updateFstabOnDisk()
            if enabled and self.originalMountEntry.isFileSystemAvailable():
                self.originalMountEntry.enable(self)
        self.accept()

############################################################################
class MountEntryAdvancedCommonUnixDialog(KDialogBase):
    ########################################################################
    def __init__(self,parent,name=None):
        KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel)

        grid = self.makeGridMainWidget(2,Qt.Horizontal)
        grid.setSpacing(self.spacingHint())

        QWidget(grid)
        self.accesstimecheckbox = QCheckBox(i18n("Update file access timestamps"),grid)

        QWidget(grid)
        self.allowexecutablecheckbox = QCheckBox(i18n("Allow Executables"),grid)

        QWidget(grid)
        self.allowsuidcheckbox = QCheckBox(i18n("Allow the SUID and SGID attributes"),grid)

        QWidget(grid)
        self.usedevpointscheckbox = QCheckBox(i18n("Allow device points"),grid)

        label = QLabel(i18n("Options:"),grid)
        self.optionslineedit = KLineEdit(grid)

        label = QLabel(i18n("fs_freq:"),grid)
        self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,grid)

        label = QLabel(i18n("fs_passno:"),grid)
        self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,grid)

    ########################################################################
    def do(self,atime,allowexecutable,allowsuid,usedevpoints,options,fsfreq,fspassno):

        self.accesstimecheckbox.setChecked(atime)
        self.allowexecutablecheckbox.setChecked(allowexecutable)
        self.allowsuidcheckbox.setChecked(allowsuid)
        self.usedevpointscheckbox.setChecked(usedevpoints)
        self.optionslineedit.setText(options)
        self.fsfreqspinbox.setValue(fsfreq)
        self.fspassnospinbox.setValue(fspassno)
        self.exec_loop()
        return ( self.accesstimecheckbox.isChecked(),
            self.allowexecutablecheckbox.isChecked(),
            self.allowsuidcheckbox.isChecked(),
            self.usedevpointscheckbox.isChecked(),
            unicode(self.optionslineedit.text()),
            self.fsfreqspinbox.value(),
            self.fspassnospinbox.value())

############################################################################
class MountEntryAdvancedPlainDialog(KDialogBase):
    ########################################################################
    def __init__(self,parent,name=None):
        KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel)

        grid = self.makeGridMainWidget(2,Qt.Horizontal)
        grid.setSpacing(self.spacingHint())

        label = QLabel(i18n("Options:"),grid)
        self.optionslineedit = KLineEdit(grid)

        label = QLabel(i18n("fs_freq:"),grid)
        self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,grid)

        label = QLabel(i18n("fs_passno:"),grid)
        self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,grid)

    ########################################################################
    def do(self,options,fsfreq,fspassno):
        self.optionslineedit.setText(options)
        self.fsfreqspinbox.setValue(fsfreq)
        self.fspassnospinbox.setValue(fspassno)
        self.exec_loop()
        return (unicode(self.optionslineedit.text()), self.fsfreqspinbox.value(), self.fspassnospinbox.value())

############################################################################
class MountListViewItem(KListViewItem):
    ########################################################################
    def __init__(self,parentitem,mountentry,haldevice=None):
        self.haldevice = haldevice
        self.microhal = MicroHAL.MicroHAL()
        self.mountentry = mountentry
        if self.mountentry is None:
            # There is no mount entry right now. This acts as a place holder
            # for now.
            KListViewItem.__init__(self,parentitem,self.haldevice.getName(),"","",self.haldevice.getDev(),"")
        else:
            if mountentry.isEnabled():
                enabled = i18n("Enabled")
            else:
                enabled = i18n("Disabled")
            if self.haldevice is not None:
                name = self.haldevice.getName()
            else:
                name = self.mountentry.getName()
            KListViewItem.__init__(self, parentitem, \
                name,
                self.mountentry.getMountPoint(), \
                self.mountentry.mounttype, \
                self.mountentry.getDevice(), \
                enabled)

            if self.mountentry.isEnabled():
                self.setPixmap(4,UserIcon("greenled"))
            else:
                self.setPixmap(4,UserIcon("greyled"))
        self.__updateIcon()

    ########################################################################
    def hasHAL(self):
        return self.haldevice is not None

    ########################################################################
    def getHAL(self): return self.haldevice

    ########################################################################
    def updateDisplay(self):
        if self.mountentry is not None:
            if self.mountentry.isEnabled():
                enabled = i18n("Enabled")
                self.setPixmap(4,UserIcon("greenled"))
            else:
                enabled = i18n("Disabled")
                self.setPixmap(4,UserIcon("greyled"))

            if self.haldevice is not None:
                self.setText(0,self.haldevice.getName())
            else:
                self.setText(0,self.mountentry.getMountPoint())

            self.setText(1,self.mountentry.getMountPoint())
            self.setText(2,self.mountentry.mounttype)
            self.setText(3,self.mountentry.getDevice())
            self.setText(4,enabled)
        else:
            self.setText(0,self.haldevice.getName())
            self.setText(1,"")
            self.setText(2,"")
            self.setText(3,self.haldevice.getDev())
            self.setText(4,"")
            self.setPixmap(4,QPixmap())
        self.__updateIcon()

    ########################################################################
    def setMountEntry(self,entry):
        self.mountentry = entry
        self.updateDisplay()

    ########################################################################
    def getMountEntry(self):
        return self.mountentry

    ########################################################################
    def getDevice(self): return self.haldevice.getDev()

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

    def __updateIcon(self):
        if self.haldevice is not None:
            self.setPixmap(0,UserIcon(self.haldevice.iconname))
        else:
            self.setPixmap(0,UserIcon(self.mountentry.getIconName()))

############################################################################
class MountGroupListViewItem(KListViewItem):
    ########################################################################
    def __init__(self,parentitem,haldevice):
        self.haldevice = haldevice
        KListViewItem.__init__(self,parentitem,self.haldevice.getName(),"","","","")

        if self.haldevice is not None:
            iconname = self.haldevice.iconname
        else:
            iconname = self.mountentry.getIconName()
        self.setPixmap(0,UserIcon(iconname))

    ########################################################################
    def getMountEntry(self):
        return None

    ########################################################################
    def updateDisplay(self):
        pass

    def hasHAL(self):
        return False

############################################################################
# Try translating this code to C++. I dare ya!
if standalone:
    programbase = KDialogBase
else:
    programbase = KCModule

class MountConfigApp(programbase):
    ########################################################################
    def __init__(self,parent=None,name=None):
        global standalone,isroot
        KGlobal.locale().insertCatalogue("guidance")

        if standalone:
            KDialogBase.__init__(self,KJanusWidget.Plain,i18n("Disk & Filesystems"),
                KDialogBase.User1|KDialogBase.Close, KDialogBase.Close)
            self.setButtonText(KDialogBase.User1,i18n("About"))
            topwidget = self.plainPage()
        else:
            KCModule.__init__(self,parent,name)
            self.setButtons(0)
            self.aboutdata = MakeAboutData()
            topwidget = self
            
        # Create a configuration object.
        self.config = KConfig("mountconfigrc")
            
        KGlobal.iconLoader().addAppDir("guidance")
        self.updatingGUI = False
        self.mounttable = MountTable('/etc/fstab','/etc/mtab')
        self.selectedentry = None
        self.aboutus = KAboutApplication(self)
        self.sizeviewdialogs = {}
        toplayout = QVBoxLayout(topwidget, 0, KDialog.spacingHint())
        #topwidget.setEnabled(isroot)

        hb = QHBox(topwidget)
        hb.setSpacing(KDialog.spacingHint())
        #if standalone:
        #    hb.setMargin(KDialog.marginHint())

        toplayout.addWidget(hb)

        label = QLabel(hb)
        label.setPixmap(UserIcon("kcmpartitions"))
        hb.setStretchFactor(label,0)

        label = QLabel(i18n("Available Disks and Filesystems:"),hb)
        hb.setStretchFactor(label,1)

        self.mountlist = KListView(topwidget,"Mount list")
        toplayout.addWidget(self.mountlist)
        self.mountlist.addColumn(i18n("Name"))
        self.mountlist.addColumn(i18n("Mount Point"))
        self.mountlist.addColumn(i18n("Type"))
        self.mountlist.addColumn(i18n("Device"))
        self.mountlist.addColumn(i18n("Enabled"))
        self.mountlist.setAllColumnsShowFocus(True)
        self.mountlist.setSelectionMode(QListView.Single)
        self.mountlist.setRootIsDecorated(True)
        self.mountlist.setSorting(-1)
        self.connect(self.mountlist, SIGNAL("selectionChanged(QListViewItem *)"), self.slotListClicked)
        # Doubleclick in item opens modify dialogue.
        self.connect(self.mountlist, SIGNAL("doubleClicked(QListViewItem *)"), self.slotModifyClicked)
        # Rightclick: Open ContextMenu
        self.connect(self.mountlist, SIGNAL("contextMenu(KListView*,QListViewItem*,const QPoint&)"),
                    self.slotContextMenu)

        hbox = QHBox(topwidget)
        toplayout.addWidget(hbox)
        hbox.setSpacing(KDialog.spacingHint())

        toplayout.setStretchFactor(hbox,0)
        self.newbutton = KPushButton(i18n("New..."),hbox)
        hbox.setStretchFactor(self.newbutton,1)
        self.connect(self.newbutton,SIGNAL("clicked()"),self.slotNewClicked)
        self.newbutton.setEnabled(isroot)

        self.modifybutton = KPushButton(i18n("Modify..."),hbox)
        hbox.setStretchFactor(self.modifybutton,1)
        self.connect(self.modifybutton,SIGNAL("clicked()"),self.slotModifyClicked)

        self.deletebutton = KPushButton(i18n("Delete..."),hbox)
        hbox.setStretchFactor(self.deletebutton,1)
        self.connect(self.deletebutton,SIGNAL("clicked()"),self.slotDeleteClicked)

        self.enablebutton = KPushButton(i18n("Enable"),hbox)
        hbox.setStretchFactor(self.enablebutton,1)
        self.connect(self.enablebutton,SIGNAL("clicked()"),self.slotEnableClicked)

        self.disablebutton = KPushButton(i18n("Disable"),hbox)
        hbox.setStretchFactor(self.disablebutton,1)
        self.connect(self.disablebutton,SIGNAL("clicked()"),self.slotDisableClicked)

        self.detailsbutton = KPushButton(i18n("Details..."),hbox)
        hbox.setStretchFactor(self.detailsbutton,1)
        self.connect(self.detailsbutton,SIGNAL("clicked()"),self.slotDetailsClicked)
        
        self.microhal = None
        self.devstolistitems = None
        self.mountentriestolistitems = None
        self.__updateMountList()
        self.__selectEntry(self.mounttable[0])

        self.configuredialog = MountEntryDialog(None)

    ########################################################################
    def exec_loop(self):
        global programbase
        self.__loadOptions()
        programbase.exec_loop(self)
        self.__saveOptions()

    ########################################################################    
    def slotContextMenu(self,lv,lv_item,p):

        hal_device = lv_item.haldevice
        if hal_device is not None and not isinstance(hal_device,MicroHAL.FakeSystemDevice):
                
            self.cmenu = KPopupMenu(self,"MyActions")
            if isinstance(hal_device,MicroHAL.RemovableDisk) or isinstance(lv_item,MountListViewItem):
                self.cmenu.insertItem(i18n("Modify..."), self.slotModifyClicked, 0, 0)
                self.cmenu.insertItem(i18n("Delete..."), self.slotDeleteClicked, 0, 1)
                if not isroot:
                    self.cmenu.setItemEnabled(1,False)
            elif isinstance(hal_device,MicroHAL.Disk):
                self.cmenu.insertItem(i18n("Show details..."), self.slotDetailsClicked, 0, 0)
                self.cmenu.insertItem(i18n("New..."), self.slotNewClicked, 0, 1)
                if not isroot:
                    self.cmenu.setItemEnabled(1,False)

            self.cmenu.exec_loop(p)
            
    ########################################################################
    def slotUser1(self):
        self.aboutus.show()

    ########################################################################
    def slotEnableClicked(self):
        if self.selectedentry!=None:
            self.selectedentry.enable(self)
            self.mounttable.updateStatus(self.selectedentry)
            self.__updateEntry(self.selectedentry)
            self.enablebutton.setEnabled(not self.selectedentry.isEnabled())
            self.disablebutton.setEnabled(self.selectedentry.isEnabled())

    ########################################################################
    def slotDisableClicked(self):
        if self.selectedentry!=None:
            self.__disableEntry()

    ########################################################################
    def slotModifyClicked(self):
        if self.selectedentry!=None:
            self.configuredialog.doEditMount(self.mounttable,self.selectedentry)

            lvi = self.mountentriestolistitems[self.selectedentry]
            if lvi.hasHAL():
                if lvi.getHAL().getDev()!=self.selectedentry.getDevice():
                    # The (device-)item in the listview no longer matches this mount entry.
                    del self.mountentriestolistitems[self.selectedentry]
                    lvi.setMountEntry(None)
                    lvi.updateDisplay()
                    # Reinsert this mount entry into the list view.
                    self.__insertMountEntryIntoListView(self.selectedentry)
            elif self.selectedentry.getDevice() in self.devstolistitems:
                # The mount entry can now merged with any existing (HAL-)item.
                # Remove the existing lose item.
                self.mountlist.takeItem(lvi)
                del lvi
                del self.mountentriestolistitems[self.selectedentry]
                # Reinsert this mount entry into the list view.
                self.__insertMountEntryIntoListView(self.selectedentry)
            self.__updateEntry(self.selectedentry)
            self.__selectEntry(self.selectedentry)

    ########################################################################
    def slotNewClicked(self):
        defaultdevice = None
        if self.selectedentry is None:
            lvi = self.mountlist.selectedItem()
            if lvi is not None and lvi.hasHAL() and (lvi.getMountEntry() is None):
                defaultdevice = lvi.getDevice()
        newentry = self.configuredialog.doNewMount(self.mounttable,defaultdevice)
        if newentry!=None:
            self.updatingGUI = True
            self.__insertMountEntryIntoListView(newentry)
            self.__selectEntry(newentry)
            self.updatingGUI = False

    ########################################################################
    def slotDeleteClicked(self):
        if self.selectedentry!=None:
            if self.selectedentry.isEnabled():
                if not self.__disableEntry():
                    return # If we couldn't disable it, then we can't continue.
            message = i18n("Are you sure you want to delete mount '%1' of type %2 at '%3'?\n(This will only remove the mount, no data will be deleted.)") \
                .arg(self.selectedentry.getMountPoint()).arg(self.selectedentry.mounttype).arg(self.selectedentry.getDevice())
            if KMessageBox.warningYesNo(self,message,i18n("Delete Mount?"))==KMessageBox.Yes:
                lvi = self.mountentriestolistitems[self.selectedentry]
                if not lvi.hasHAL():
                    self.mountlist.takeItem(lvi)
                    del lvi
                    del self.mountentriestolistitems[self.selectedentry]
                else:
                    lvi.setMountEntry(None)
                self.mounttable.remove(self.selectedentry)
                self.mounttable.updateFstabOnDisk()
                self.__selectEntry(None)
                
    ########################################################################
    def slotDetailsClicked(self):
        # Popup a dialog showing disklayout and a graphical represenation of 'df'
        hal_device = self.mountlist.selectedItem().haldevice
        if isinstance(hal_device,MicroHAL.Disk):
            blk = hal_device.getDev()
            devicepath, devicename = ('/'.join(blk.split('/')[0:-1])+'/', blk.split('/')[-1])
            # We keep a dict with those widgets, that saves us some time reading out all the values.
            if devicename not in self.sizeviewdialogs.keys():
                self.sizeviewdialogs[devicename] = sizeview.SizeView(self,devicename,devicepath)
                self.sizeviewdialogs[devicename].exec_loop()
            else:
                self.sizeviewdialogs[devicename].exec_loop()
        else:
            print "Sizeview doesn't support",blk.__class__," yet."
    
    ########################################################################
    def slotListClicked(self,item):
        if self.updatingGUI==False:
            self.__selectEntry(item.getMountEntry())

    ########################################################################
    def __disableEntry(self):
        self.selectedentry.disable(self)
        self.mounttable.updateStatus(self.selectedentry)
        self.__updateEntry(self.selectedentry)
        self.enablebutton.setEnabled(not self.selectedentry.isEnabled() and self.selectedentry.isFileSystemAvailable())
        self.disablebutton.setEnabled(self.selectedentry.isEnabled())
        return not self.selectedentry.isEnabled()

    ########################################################################
    def __updateEntry(self,selectedentry):
        # Update the display.
        lvi = self.mountentriestolistitems[selectedentry]
        lvi.updateDisplay()

    ########################################################################
    def __loadOptions(self):
        self.config.setGroup("General")
        size = self.config.readSizeEntry("Geometry")
        if size.isEmpty()==False:
            self.resize(size)

    #######################################################################
    def __saveOptions(self):
        global isroot
        if isroot:
            return
        self.config.setGroup("General")
        self.config.writeEntry("Geometry", self.size())
        self.config.sync()

    ########################################################################
    def __updateMountList(self):
        self.mountentriestolistitems = {}

        self.mountlist.clear()

        self.listgroups = {}
        self.devstolistitems = {}

        if self.microhal is None:
            self.microhal = MicroHAL.MicroHAL()

        lasttopitem = None

        # Scan through /sys/block for devices. Find out which disks are
        # installed and should be shown in the listview. For real
        # disks we put a 'group' in the listview and under the group
        # we list the partitions, whether they are mounted or not.
        for blockdevice in self.microhal.getDevices():
            # We are looking for block devices that represent hard disks or
            # things that have partitions and are not removable
            if blockdevice.major in self.microhal.partitionblockdevs and not blockdevice.removable:
                # We have a not removable block device.
                # We want to create a listitem for the device and subitems
                # for each partition.
                groupitem = MountGroupListViewItem(self.mountlist,blockdevice)
                groupitem.setOpen(True)
                lasttopitem = groupitem
                lvi = None
                for partition in blockdevice.getPartitions():
                    # Try to find a matching mount entry for this partition.
                    lastlvi = lvi
                    lvi = MountListViewItem(groupitem,None,partition)
                    self.devstolistitems[partition.getDev()] = lvi
                    if lastlvi is not None:
                        lvi.moveItem(lastlvi)
            elif blockdevice.major in self.microhal.cdromsdevs or blockdevice.removable:
                # Removable block device, assume CDROM (even if it's a partitionblockdevice)
                lvi = MountListViewItem(self.mountlist,None,blockdevice)
                self.devstolistitems[blockdevice.getDev()] = lvi
                lasttopitem = lvi


        systemdevice = MicroHAL.FakeSystemDevice()
        systemdevice.iconname = systemdevice.getIconName()
        groupitem = MountGroupListViewItem(self.mountlist,systemdevice)

        if lasttopitem is not None:
            groupitem.moveItem(lasttopitem)
            lasttopitem = groupitem

        self.listgroups["system"] = groupitem

        self.mountentriestolistitems = {}
        for entry in self.mounttable:
            self.__insertMountEntryIntoListView(entry)

    ########################################################################
    def __insertMountEntryIntoListView(self,mountentry):
        if mountentry.getDevice() in self.devstolistitems:
            lvi = self.devstolistitems[mountentry.getDevice()]
            lvi.setMountEntry(mountentry)
        else:
            cat = mountentry.getCategory()   # Place it under a special node?
            if cat not in self.listgroups:
                lvi = MountListViewItem(self.mountlist,mountentry)
                item = self.mountlist.firstChild()
            else:
                lvi = MountListViewItem(self.listgroups[cat],mountentry)
                item = self.listgroups[cat].firstChild()

            # Move the item to the end of this (sub-list).
            while item.nextSibling() is not None:
                item = item.nextSibling()
            lvi.moveItem(item)

        self.mountentriestolistitems[mountentry] = lvi

    ########################################################################
    def __selectEntry(self,mountentry):
        if mountentry is not None and isroot:
            lvi = self.mountentriestolistitems[mountentry]
            self.mountlist.setSelected(lvi,True)
            self.enablebutton.setEnabled(not mountentry.isEnabled() and mountentry.isFileSystemAvailable())
            self.selectedentry = mountentry
            # disable unsupported stuff, such as SystemEntries that canot be disabled and modified
            if not mountentry.maydisable:
                disable = False
            else:
                disable = mountentry.isEnabled()
            if mountentry.notInFstab:
                delete = False
                modify = False
            else:
                delete = True
                modify = True

            self.disablebutton.setEnabled(disable)
            self.deletebutton.setEnabled(delete)
            self.modifybutton.setEnabled(modify)
            
        else:
            self.enablebutton.setEnabled(False)
            self.disablebutton.setEnabled(False)
            self.deletebutton.setEnabled(False)
            self.modifybutton.setEnabled(False)
            self.detailsbutton.setEnabled(False)
            self.selectedentry = None
            
        selected_item = self.mountlist.selectedItem()
        if selected_item is not None:
            self.detailsbutton.setEnabled(isinstance(selected_item.haldevice,MicroHAL.Disk) \
                and not isinstance(selected_item.haldevice,MicroHAL.RemovableDisk))
        else:
            self.detailsbutton.setEnabled(False)
            
    #######################################################################
    # KControl virtual void methods
    def load(self):
        pass
    def save(self):
        pass
    def defaults(self):
        pass        
    def sysdefaults(self):
        pass

    def aboutData(self):
        # Return the KAboutData object which we created during initialisation.
        return self.aboutdata
    def buttons(self):
        # Only supply a Help button. Other choices are Default and Apply.
        return KCModule.Help

############################################################################
# Factory function for KControl
def create_mountconfig(parent,name):
    global kapp
    kapp = KApplication.kApplication()    
    return MountConfigApp(parent, name)

############################################################################
def MakeAboutData():
    aboutdata = KAboutData("mountconfig",programname,version,"Disk & Filesystem Configuration Tool", 
                    KAboutData.License_GPL, "Copyright (C) 2003-2006 Simon Edwards")
    aboutdata.addAuthor("Simon Edwards","Developer","simon@simonzone.com","http://www.simonzone.com/software/guidance")
    aboutdata.addAuthor("Sebastian K├╝gler","Developer","sebas@kde.org","http://vizZzion.org");
    return aboutdata

if standalone:
    aboutdata = MakeAboutData()
    KCmdLineArgs.init(sys.argv,aboutdata)

    kapp = KApplication()
    sysvapp = MountConfigApp()
    sysvapp.exec_loop()

Generated by  Doxygen 1.6.0   Back to index