[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kfm-devel
Subject:    [PATCH] Support for POSIX ACL in kioslave/file and kio/kio
From:       Till Adam <adam () kde ! org>
Date:       2005-08-07 9:56:36
Message-ID: 200508071156.44379.adam () kde ! org
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


Heya folks,

please find attached two patches, one against kdelibs/kioslave/file and one 
against kdelibs/kio, which together implement support for managing POSIX ACLs 
on filesystems that support it. Please review, I'll then merge this into 3.5.

The backend is based off of patches by György Szombathelyi and the frontend 
(kio/kfile/kacleditwidget) makes liberal use of work done by Sean Harmer in 
playground/base/acldlg. All bugs are mine, of course.

As for the details of the patches, I guess the kioslave bits should be pretty 
self-explanatory. When reading, ACLs are shipped to the user via three new 
atom types, while writing is done by setting two metadata strings when 
issuing a chmod job (ACL_STRING and DEFAULT_ACL_STRING). There is a special 
value for deleting a previously present ACLs. The format of these strings is 
that of the POSIX ACL string representation, as defined in POSIX draft 
1003.1e/1003.2c.

To encapsulate all of the acl string parsing and manipulation, and to localize 
the libacl and sys/acl.h includes I've done a KACL class, which offers a nice 
interface to ACL munging and all kinds of convenience. User code (such as the 
stuff in kio/kfile only needs kacl.h. KACLs can easily be created from the 
abovementioned POSIX strings.

There's a full test suite for KACL and an extension of KioslaveTest for the 
three new atoms.

There's extensions to KFileItem for setting and getting ACL and defaultACL.

The frontend aims to integrate seamlessly with the existing interface. If the 
underlying filesystem supports setting ACLs the group of checkboxes in the 
"Advanced properties" is replaced (not really replaced, the special bits 
stay) by a listview, which allows adding, removing and editing of entries and 
inline toggling of permission bits. This widget takes much care to not allow 
illegal (combinations of) entries, it automatically adds entries when the 
underlying acl lib would add them as well (properly autocalculated mask 
entries appear when you add the first extended entry, for example), you can't 
add an entry for a user or group more than once, etc. The Advanced 
Permissions keeps state between invocations, the main "Ok" does the actual 
chmod, as before. The state of the three newbie combos, as I affectionately 
call them, is kept in sync with the acl editing widget.

Recursive applying of ACL changes to directories and all files/dirs in them 
works.

Limitations/TODO:
 o it currently only works for a single file (no support for "partial" 	   
   handling), that's the next thing I'd like to tackle once this is in
 o the users and groups are offered as comboboxes, which has scalability
   issues, I'm planing to offer a lineedit with completion when the number
   of available users/groups exceeds a certain limit
 o there might be BIC issues, I haven't checked carefully yet, if there are,
   I'll fix them, of course
 o the configure checks aren't done yet, I guess this needs to be a compile 
   time option
 o there is a set of trivial kdebase patches to make the various konqueror
   views show a little "+" next to the permissions string, if there's an
   extended ACL on a file, I'll merge that once these patches are in

Longer term plans:
 o extend it to samba, nfs, etc


I'd be grateful for any testing and feedback that you might be able to spare.

Cheerio,

Till

["POSIX-ACL-support-kioslave.diff" (text/x-diff)]

Index: file/file.cc
===================================================================
--- file/file.cc	(.../KDE/3.5/kdelibs/kioslave)	(revision 443749)
+++ file/file.cc	(.../work/posix-acl-support/kdelibs/kioslave)	(revision 443749)
@@ -41,6 +41,11 @@
 #include <sys/sendfile.h>
 #endif
 
+#ifdef USE_POSIX_ACL
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#endif
+
 #include <assert.h>
 #include <dirent.h>
 #include <errno.h>
@@ -91,6 +96,12 @@ using namespace KIO;
 #define MAX_IPC_SIZE (1024*32)
 
 static QString testLogFile( const char *_filename );
+#ifdef USE_POSIX_ACL
+static bool isExtendedACL(  acl_t p_acl );
+static void appendACLAtoms( const QCString & path, UDSEntry& entry, 
+                            mode_t type, bool withACL );
+#endif
+
 
 extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); }
 
@@ -122,12 +133,74 @@ FileProtocol::FileProtocol( const QCStri
     groupcache.setAutoDelete( true );
 }
 
+
+int FileProtocol::setACL( const char *path, mode_t perm, bool directoryDefault )
+{
+    int ret = 0;
+#ifdef USE_POSIX_ACL
+
+    const QString ACLString = metaData( "ACL_STRING" );
+    const QString defaultACLString = metaData( "DEFAULT_ACL_STRING" );
+    // Empty strings mean leave as is
+    if ( !ACLString.isEmpty() ) {
+        acl_t acl = 0;
+        if ( ACLString == "ACL_DELETE" ) {
+            // user told us to delete the extended ACL, so let's write only 
+            // the minimal (UNIX permission bits) part
+            acl = acl_from_mode( perm );
+        }
+        acl = acl_from_text( ACLString.latin1() );
+        if ( acl_valid( acl ) == 0 ) { // let's be safe
+            ret == acl_set_file( path, ACL_TYPE_ACCESS, acl );
+            ssize_t size = acl_size( acl );
+            kdDebug(7101) << "Set ACL on: " << path << " to: " << acl_to_text( acl, \
&size ) << endl; +        }
+        acl_free( acl );
+        if ( ret != 0 ) return ret; // better stop trying right away
+    }
+
+    if ( directoryDefault && !defaultACLString.isEmpty() ) {
+        if ( defaultACLString == "ACL_DELETE" ) {
+            // user told us to delete the default ACL, do so
+            ret += acl_delete_def_file( path );
+        } else {
+            acl_t acl = acl_from_text( defaultACLString.latin1() );
+            if ( acl_valid( acl ) == 0 ) { // let's be safe
+                ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl );
+                ssize_t size = acl_size( acl );
+                kdDebug(7101) << "Set Default ACL on: " << path << " to: " << \
acl_to_text( acl, &size ) << endl; +            }
+            acl_free( acl );
+        }
+    }
+#endif
+    return ret;
+}
+
 void FileProtocol::chmod( const KURL& url, int permissions )
 {
-    QCString _path( QFile::encodeName(url.path()));
-    if ( ::chmod( _path.data(), permissions ) == -1 )
+    QCString _path( QFile::encodeName(url.path()) );
+    /* FIXME: Should be atomic */
+    if ( ::chmod( _path.data(), permissions ) == -1 || 
+        ( setACL( _path.data(), permissions, false ) == -1 ) ||
+        /* if not a directory, cannot set default ACLs */
+        ( setACL( _path.data(), permissions, true ) == -1 && errno != ENOTDIR ) ) {
+
+        switch (errno) {
+            case EPERM:
+            case EACCES:
+                error( KIO::ERR_ACCESS_DENIED, url.path() );
+                break;
+            case ENOTSUP:
+                error( KIO::ERR_UNSUPPORTED_ACTION, url.path() );
+                break;
+            case ENOSPC:
+                error( KIO::ERR_DISK_FULL, url.path() );
+                break;
+            default:
         error( KIO::ERR_CANNOT_CHMOD, url.path() );
-    else
+        }
+    } else
         finished();
 }
 
@@ -493,6 +566,10 @@ void FileProtocol::copy( const KURL &src
     QCString _src( QFile::encodeName(src.path()));
     QCString _dest( QFile::encodeName(dest.path()));
     KDE_struct_stat buff_src;
+#ifdef USE_POSIX_ACL
+    acl_t acl;
+#endif
+
     if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
         if ( errno == EACCES )
            error( KIO::ERR_ACCESS_DENIED, src.path() );
@@ -568,6 +645,15 @@ void FileProtocol::copy( const KURL &src
 #ifdef HAVE_FADVISE
     posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
 #endif
+
+#ifdef USE_POSIX_ACL
+    acl = acl_get_fd(src_fd);
+    if ( acl && !isExtendedACL( acl ) ) {
+        kdDebug(7101) << _dest.data() << " doesn't have extended ACL" << endl;
+        acl_free( acl );
+        acl = NULL;
+    }
+#endif
     totalSize( buff_src.st_size );
 
     KIO::filesize_t processed_size = 0;
@@ -614,6 +700,9 @@ void FileProtocol::copy( const KURL &src
           error( KIO::ERR_COULD_NOT_READ, src.path());
           close(src_fd);
           close(dest_fd);
+#ifdef USE_POSIX_ACL
+          if (acl) acl_free(acl);
+#endif
           return;
        }
        if (n == 0)
@@ -636,6 +725,9 @@ void FileProtocol::copy( const KURL &src
               kdWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno) << \
endl;  error( KIO::ERR_COULD_NOT_WRITE, dest.path());
            }
+#ifdef USE_POSIX_ACL
+           if (acl) acl_free(acl);
+#endif
            return;
          }
          processed_size += n;
@@ -651,19 +743,29 @@ void FileProtocol::copy( const KURL &src
     {
         kdWarning(7101) << "Error when closing file descriptor[2]:" << \
strerror(errno) << endl;  error( KIO::ERR_COULD_NOT_WRITE, dest.path());
+#ifdef USE_POSIX_ACL
+        if (acl) acl_free(acl);
+#endif
         return;
     }
 
     // set final permissions
     if ( _mode != -1 )
     {
-       if (::chmod(_dest.data(), _mode) != 0)
+        if ( (::chmod(_dest.data(), _mode) != 0)
+#ifdef USE_POSIX_ACL
+          || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
+#endif
+        )
        {
         // Eat the error if the filesystem apparently doesn't support chmod.
         if ( KIO::testFileSystemFlag( _dest, KIO::SupportsChmod ) )
             warning( i18n( "Could not change permissions for\n%1" ).arg( dest.path() \
) );  }
     }
+#ifdef USE_POSIX_ACL
+    if (acl) acl_free(acl);
+#endif
 
     // copy access and modification time
     struct utimbuf ut;
@@ -812,7 +914,45 @@ void FileProtocol::del( const KURL& url,
     finished();
 }
 
-bool FileProtocol::createUDSEntry( const QString & filename, const QCString & path, \
UDSEntry & entry, short int details ) +
+QString FileProtocol::getUserName( uid_t uid ) 
+{
+    QString *temp;
+    temp = usercache.find( uid );
+    if ( !temp ) {
+        struct passwd *user = getpwuid( uid );
+        if ( user ) {
+            usercache.insert( uid, new QString(QString::fromLatin1(user->pw_name)) \
); +            return QString::fromLatin1( user->pw_name );
+        }
+        else
+            return QString::number( uid );
+    }
+    else
+        return *temp;
+}
+
+QString FileProtocol::getGroupName( gid_t gid ) 
+{
+    QString *temp;
+    temp = groupcache.find( gid );
+    if ( !temp ) {
+        struct group *grp = getgrgid( gid );
+        if ( grp ) {
+            groupcache.insert( gid, new QString(QString::fromLatin1(grp->gr_name)) \
); +            return QString::fromLatin1( grp->gr_name );
+        }
+        else
+            return QString::number( gid );
+    }
+    else
+        return *temp;
+}
+
+
+
+bool FileProtocol::createUDSEntry( const QString & filename, const QCString & path, \
UDSEntry & entry,  +                                   short int details, bool \
withACL )  {
     assert(entry.count() == 0); // by contract :-)
     // Note: details = 0 (only "file or directory or symlink or doesn't exist") \
isn't implemented @@ -883,42 +1023,24 @@ bool FileProtocol::createUDSEntry( const
     atom.m_long = buff.st_size;
     entry.append( atom );
 
+#ifdef USE_POSIX_ACL
+    /* Append an atom indicating whether the file has extended acl information
+     * and if withACL is specified also one with the acl itself. If it's a directory
+     * and it has a default ACL, also append that. */
+    appendACLAtoms( path, entry, type, withACL );
+#endif
+
  notype:
     atom.m_uds = KIO::UDS_MODIFICATION_TIME;
     atom.m_long = buff.st_mtime;
     entry.append( atom );
 
     atom.m_uds = KIO::UDS_USER;
-    uid_t uid = buff.st_uid;
-    QString *temp = usercache.find( uid );
-
-    if ( !temp ) {
-        struct passwd *user = getpwuid( uid );
-        if ( user ) {
-            usercache.insert( uid, new QString(QString::fromLatin1(user->pw_name)) \
                );
-            atom.m_str = user->pw_name;
-        }
-        else
-            atom.m_str = QString::number( uid );
-    }
-    else
-        atom.m_str = *temp;
+    atom.m_str = getUserName( buff.st_uid );
     entry.append( atom );
 
     atom.m_uds = KIO::UDS_GROUP;
-    gid_t gid = buff.st_gid;
-    temp = groupcache.find( gid );
-    if ( !temp ) {
-        struct group *grp = getgrgid( gid );
-        if ( grp ) {
-            groupcache.insert( gid, new QString(QString::fromLatin1(grp->gr_name)) \
                );
-            atom.m_str = grp->gr_name;
-        }
-        else
-            atom.m_str = QString::number( gid );
-    }
-    else
-        atom.m_str = *temp;
+    atom.m_str = getGroupName( buff.st_gid );
     entry.append( atom );
 
     atom.m_uds = KIO::UDS_ACCESS_TIME;
@@ -957,11 +1079,12 @@ void FileProtocol::stat( const KURL & ur
     kdDebug(7101) << "FileProtocol::stat details=" << details << endl;
 
     UDSEntry entry;
-    if ( !createUDSEntry( url.fileName(), _path, entry, details ) )
+    if ( !createUDSEntry( url.fileName(), _path, entry, details, true /*with acls*/ \
) )  {
         error( KIO::ERR_DOES_NOT_EXIST, url.path(-1) );
         return;
     }
+
 #if 0
 ///////// debug code
     KIO::UDSEntry::ConstIterator it = entry.begin();
@@ -992,8 +1115,15 @@ void FileProtocol::stat( const KURL & ur
             case KIO::UDS_LINK_DEST:
                 kdDebug(7101) << "LinkDest : " << ((*it).m_str.ascii() ) << endl;
                 break;
+            case KIO::UDS_EXTENDED_ACL:
+                kdDebug(7101) << "Contains extended ACL " << endl;
+                break;
         }
     }
+    MetaData::iterator it1 = mOutgoingMetaData.begin();
+    for ( ; it1 != mOutgoingMetaData.end(); it1++ ) {
+        kdDebug(7101) << it1.key() << " = " << it1.data() << endl;
+    }
 /////////
 #endif
     statEntry( entry );
@@ -1078,7 +1208,9 @@ void FileProtocol::listDir( const KURL& 
     QStrListIterator it(entryNames);
     for (; it.current(); ++it) {
         entry.clear();
-        if ( createUDSEntry( QFile::decodeName(*it), *it /* we can use the filename \
as relative path*/, entry, 2 ) ) +        if ( createUDSEntry( \
QFile::decodeName(*it),  +                             *it /* we can use the filename \
as relative path*/,  +                             entry, 2, true ) )
           listEntry( entry, false);
         else
           ;//Well, this should never happen... but with wrong encoding names
@@ -1550,4 +1682,67 @@ static QString testLogFile( const char *
     return result;
 }
 
+
+/*************************************
+ *
+ * ACL handling helpers
+ *
+ *************************************/
+#ifdef USE_POSIX_ACL
+
+static bool isExtendedACL( acl_t acl )
+{
+    return ( acl_equiv_mode( acl, 0 ) != 0 );
+}
+
+static void appendACLAtoms( const QCString & path, UDSEntry& entry, mode_t type, \
bool withACL ) +{
+    // first check for a noop
+    if ( acl_extended_file( path.data() ) == 0 ) return;
+
+    acl_t acl = 0;
+    acl_t defaultAcl = 0;
+    UDSAtom atom;
+    bool isDir = S_ISDIR( type );
+    // do we have an acl for the file, and/or a default acl for the dir, if it is \
one? +    acl = acl_get_file( path.data(), ACL_TYPE_ACCESS );
+    /* Sadly libacl does not provided a means of checking for extended ACL and \
default +     * ACL separately. Since a directory can have both, we need to check \
again. */ +    if ( isDir ) {
+        if ( acl ) { 
+            if ( !isExtendedACL( acl ) ) {
+                acl_free( acl ); 
+                acl = 0;
+            }
+        }
+        defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT );
+    }
+    if ( acl || defaultAcl ) {
+      kdDebug(7101) << path.data() << " has extended ACL entries " << endl;
+      atom.m_uds = KIO::UDS_EXTENDED_ACL;
+      atom.m_long = 1;
+      entry.append( atom );
+    }
+    if ( withACL ) {
+        if ( acl ) {
+            ssize_t size = acl_size( acl );
+            atom.m_uds = KIO::UDS_ACL_STRING;
+            atom.m_str = QString::fromLatin1( acl_to_text( acl, &size ) );
+            entry.append( atom );
+            kdDebug(7101) << path.data() << "ACL: " << atom.m_str << endl;
+        }
+        if ( defaultAcl ) {
+            ssize_t size = acl_size( defaultAcl );
+            atom.m_uds = KIO::UDS_DEFAULT_ACL_STRING;
+            atom.m_str = QString::fromLatin1( acl_to_text( defaultAcl, &size ) );
+            entry.append( atom );
+            kdDebug(7101) << path.data() << "DEFAULT ACL: " << atom.m_str << endl;
+        }
+    }
+    if ( acl ) acl_free( acl );
+    if ( defaultAcl ) acl_free( defaultAcl );
+}
+#endif
+
+
 #include "file.moc"
Index: file/file.h
===================================================================
--- file/file.h	(.../KDE/3.5/kdelibs/kioslave)	(revision 443749)
+++ file/file.h	(.../work/posix-acl-support/kdelibs/kioslave)	(revision 443749)
@@ -81,7 +81,12 @@ protected slots:
 
 protected:
 
-  bool createUDSEntry( const QString & filename, const QCString & path, \
KIO::UDSEntry & entry, short int details ); +  bool createUDSEntry( const QString & \
filename, const QCString & path, KIO::UDSEntry & entry,  +                       \
short int details, bool withACL ); +  int setACL( const char *path, mode_t perm, bool \
_directoryDefault ); +  
+  QString getUserName( uid_t uid );
+  QString getGroupName( gid_t gid );
 
   QIntDict<QString> usercache;      // maps long ==> QString *
   QIntDict<QString> groupcache;
Index: file/Makefile.am
===================================================================
--- file/Makefile.am	(.../KDE/3.5/kdelibs/kioslave)	(revision 443749)
+++ file/Makefile.am	(.../work/posix-acl-support/kdelibs/kioslave)	(revision 443749)
@@ -9,7 +9,7 @@ INCLUDES = $(all_includes)
 kde_module_LTLIBRARIES = kio_file.la
 
 kio_file_la_SOURCES = file.cc
-kio_file_la_LIBADD = $(LIB_KIO)
+kio_file_la_LIBADD = $(LIB_KIO) $(ACL_LIBS)
 kio_file_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) 
 noinst_HEADERS = file.h
 


["POSIX-ACL-support-kio.diff" (text/x-diff)]

Index: kio/kfileitem.cpp
===================================================================
--- kio/kfileitem.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kio/kfileitem.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -45,6 +45,7 @@
 #include <klocale.h>
 #include <kmimetype.h>
 #include <krun.h>
+#include <kacl.h>
 
 class KFileItem::KFileItemPrivate {
 	public:
@@ -344,6 +345,39 @@ KIO::filesize_t KFileItem::size() const
   return 0L;
 }
 
+bool KFileItem::hasExtendedACL() const
+{
+  KIO::UDSEntry::ConstIterator it = m_entry.begin();
+  for( ; it != m_entry.end(); it++ )
+    if ( (*it).m_uds == KIO::UDS_EXTENDED_ACL ) {
+      return true;
+    }
+  return false;
+}
+
+KACL KFileItem::ACL() const
+{
+  if ( hasExtendedACL() ) {
+    // Extract it from the KIO::UDSEntry
+    KIO::UDSEntry::ConstIterator it = m_entry.begin();
+    for( ; it != m_entry.end(); ++it )
+      if ( (*it).m_uds == KIO::UDS_ACL_STRING )
+        return KACL((*it).m_str);
+  }
+  // create one from the basic permissions
+  return KACL( m_permissions );
+}
+
+KACL KFileItem::defaultACL() const
+{
+  // Extract it from the KIO::UDSEntry
+  KIO::UDSEntry::ConstIterator it = m_entry.begin();
+  for( ; it != m_entry.end(); ++it )
+    if ( (*it).m_uds == KIO::UDS_DEFAULT_ACL_STRING )
+      return KACL((*it).m_str);
+  return KACL();
+}
+
 time_t KFileItem::time( unsigned int which ) const
 {
   unsigned int mappedWhich = 0;
@@ -890,7 +924,7 @@ QString KFileItem::permissionsString() c
 
 QString KFileItem::parsePermissions(mode_t perm) const
 {
-    char p[] = "----------";
+    char p[] = "---------- ";
 
     if (isDir())
 	p[0]='d';
@@ -901,22 +935,28 @@ QString KFileItem::parsePermissions(mode
 	p[1]='r';
     if (perm & QFileInfo::WriteUser)
 	p[2]='w';
-    if (perm & QFileInfo::ExeUser)
-	p[3]='x';
+    if ((perm & QFileInfo::ExeUser) && !(perm & S_ISUID)) p[3]='x';
+    else if ((perm & QFileInfo::ExeUser) && (perm & S_ISUID)) p[3]='s';
+    else if (!(perm & QFileInfo::ExeUser) && (perm & S_ISUID)) p[3]='S';
 
     if (perm & QFileInfo::ReadGroup)
 	p[4]='r';
     if (perm & QFileInfo::WriteGroup)
 	p[5]='w';
-    if (perm & QFileInfo::ExeGroup)
-	p[6]='x';
+    if ((perm & QFileInfo::ExeGroup) && !(perm & S_ISGID)) p[6]='x';
+    else if ((perm & QFileInfo::ExeGroup) && (perm & S_ISGID)) p[6]='s';
+    else if (!(perm & QFileInfo::ExeGroup) && (perm & S_ISGID)) p[6]='S';
 
     if (perm & QFileInfo::ReadOther)
 	p[7]='r';
     if (perm & QFileInfo::WriteOther)
 	p[8]='w';
-    if (perm & QFileInfo::ExeOther)
-	p[9]='x';
+    if ((perm & QFileInfo::ExeOther) && !(perm & S_ISVTX)) p[9]='x';
+    else if ((perm & QFileInfo::ExeOther) && (perm & S_ISVTX)) p[9]='t';
+    else if (!(perm & QFileInfo::ExeOther) && (perm & S_ISVTX)) p[9]='T';
+
+    if (hasExtendedACL())
+        p[10]='+';
 
     return QString::fromLatin1(p);
 }
Index: kio/kacl.cpp
===================================================================
--- kio/kacl.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ kio/kacl.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -0,0 +1,524 @@
+/* This file is part of the KDE project
+   Copyright (C) 2005 Till Adam <adam@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+// $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/acl.h>
+#include <sys/stat.h>
+#include <acl/libacl.h>
+
+#include <kdebug.h>
+
+#include "kacl.h"
+
+class KACL::KACLPrivate {
+    public:
+        bool dummy;
+};
+
+KACL::KACL( const QString &aclString ) 
+    : m_acl( 0 ),
+      d(0)
+{
+    setACL( aclString );
+    init();
+}
+
+KACL::KACL( mode_t basePermissions ) 
+    : m_acl( acl_from_mode( basePermissions ) ),
+      d(0)
+{
+    init();
+}
+
+KACL::KACL()
+    : m_acl( 0 ),
+      d(0)
+{
+}
+
+KACL::KACL( const KACL& rhs )
+    : m_acl( 0 ),
+      d(0)
+{
+    setACL( rhs.asString() );
+}
+
+void KACL::init()
+{
+  m_usercache.setAutoDelete( true );
+  m_groupcache.setAutoDelete( true );
+}
+
+KACL::~KACL()
+{
+    if ( m_acl ) acl_free( m_acl );
+}
+
+bool KACL::isValid() const
+{
+    bool valid = false;
+    if ( m_acl ) {
+        valid = ( acl_valid( m_acl ) == 0 );
+    }
+    return valid;
+}
+
+bool KACL::isExtended() const
+{
+    return ( acl_equiv_mode( m_acl, NULL ) != 0 );
+}
+
+static acl_entry_t entryForTag( acl_t acl, acl_tag_t tag )
+{
+    acl_entry_t entry;
+    int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag == tag )
+            return entry;
+        ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry );
+    }
+    return 0;
+}
+
+static unsigned short entryToPermissions( acl_entry_t entry )
+{
+    if ( entry == 0 ) return 0;
+    acl_permset_t permset;
+    if ( acl_get_permset( entry, &permset ) != 0 ) return 0;
+    return( acl_get_perm( permset, ACL_READ ) << 2 |
+            acl_get_perm( permset, ACL_WRITE ) << 1 |
+            acl_get_perm( permset, ACL_EXECUTE ) );
+}
+
+static void permissionsToEntry( acl_entry_t entry, unsigned short v )
+{
+    if ( entry == 0 ) return;
+    acl_permset_t permset;
+    if ( acl_get_permset( entry, &permset ) != 0 ) return;
+    acl_clear_perms( permset );
+    if ( v & 4 ) acl_add_perm( permset, ACL_READ );
+    if ( v & 2 ) acl_add_perm( permset, ACL_WRITE );
+    if ( v & 1 ) acl_add_perm( permset, ACL_EXECUTE );
+}
+
+static void printACL( acl_t acl, const QString &comment )
+{
+    ssize_t size = acl_size( acl );
+    kdDebug() << comment << acl_to_text( acl, &size ) << endl;
+}
+
+static int getUidForName( const QString& name )
+{
+    struct passwd *user = getpwnam( name.latin1() );
+    if ( user )
+        return user->pw_uid;
+    else
+        return -1;
+}
+
+static int getGidForName( const QString& name )
+{
+    struct group *group = getgrnam( name.latin1() );
+    if ( group )
+        return group->gr_gid;
+    else
+        return -1;
+}
+
+// ------------------ begin API implementation ------------
+
+unsigned short KACL::ownerPermissions() const
+{
+    return entryToPermissions( entryForTag( m_acl, ACL_USER_OBJ ) );
+}
+
+bool KACL::setOwnerPermissions( unsigned short v )
+{
+    permissionsToEntry( entryForTag( m_acl, ACL_USER_OBJ ), v );
+    return true;
+}
+
+unsigned short KACL::owningGroupPermissions() const
+{
+    return entryToPermissions( entryForTag( m_acl, ACL_GROUP_OBJ ) );
+}
+
+bool KACL::setOwningGroupPermissions( unsigned short v )
+{
+    permissionsToEntry( entryForTag( m_acl, ACL_GROUP_OBJ ), v );
+    return true;
+}
+
+unsigned short KACL::othersPermissions() const
+{
+    return entryToPermissions( entryForTag( m_acl, ACL_OTHER ) );
+}
+
+bool KACL::setOthersPermissions( unsigned short v )
+{
+    permissionsToEntry( entryForTag( m_acl, ACL_OTHER ), v );
+    return true;
+}
+
+mode_t KACL::basePermissions() const
+{
+    mode_t perms( 0 );
+    if ( ownerPermissions() & ACL_READ ) perms |= S_IRUSR;
+    if ( ownerPermissions() & ACL_WRITE ) perms |= S_IWUSR;
+    if ( ownerPermissions() & ACL_EXECUTE ) perms |= S_IXUSR;
+    if ( owningGroupPermissions() & ACL_READ ) perms |= S_IRGRP;
+    if ( owningGroupPermissions() & ACL_WRITE ) perms |= S_IWGRP;
+    if ( owningGroupPermissions() & ACL_EXECUTE ) perms |= S_IXGRP;
+    if ( othersPermissions() & ACL_READ ) perms |= S_IROTH;
+    if ( othersPermissions() & ACL_WRITE ) perms |= S_IWOTH;
+    if ( othersPermissions() & ACL_EXECUTE ) perms |= S_IXOTH;
+
+   return perms;
+}
+
+unsigned short KACL::maskPermissions( bool &exists ) const
+{
+    exists = true;
+    acl_entry_t entry = entryForTag( m_acl, ACL_MASK );
+    if ( entry == 0 ) {
+        exists = false;
+        return 0;
+    }
+    return entryToPermissions( entry );
+}
+
+bool KACL::setMaskPermissions( unsigned short v )
+{
+    permissionsToEntry( entryForTag( m_acl, ACL_MASK ), v );
+    return true;
+}
+
+
+/**************************
+ * Deal with named users  *
+ **************************/
+unsigned short KACL::namedUserPermissions( const QString& name, bool *exists ) const
+{
+    acl_entry_t entry;
+    uid_t id;
+    *exists = false;
+    int ret = acl_get_entry( m_acl, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag ==  ACL_USER ) {
+            id = *( (uid_t*) acl_get_qualifier( entry ) );
+            if ( getUserName( id ) == name ) {
+                *exists = true;
+                return entryToPermissions( entry );
+            }
+        }
+        ret = acl_get_entry( m_acl, ACL_NEXT_ENTRY, &entry );
+    }
+    return 0;
+}
+
+bool KACL::setNamedUserOrGroupPermissions( const QString& name, unsigned short \
permissions, acl_tag_t type ) +{
+    acl_t newACL = acl_dup( m_acl );
+    acl_entry_t entry;
+    bool allIsWell = true;
+    bool createdNewEntry = false;
+    bool found = false;
+    int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag == type ) {
+            int id = * (int*)acl_get_qualifier( entry );
+            const QString entryName = type == ACL_USER? getUserName( id ): \
getGroupName( id ); +            if ( entryName == name ) {
+              // found him, update
+              permissionsToEntry( entry, permissions );
+              found = true;
+              break;
+            }
+        }
+        ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry );
+    }
+    if ( !found ) {
+        acl_create_entry( &newACL, &entry );
+        acl_set_tag_type(  entry, type );
+        int id = type == ACL_USER? getUidForName( name ): getGidForName( name );
+        if ( id == -1 ||  acl_set_qualifier( entry, &id ) != 0 ) {
+            acl_delete_entry( newACL, entry );
+            allIsWell = false;
+        } else {
+            permissionsToEntry( entry, permissions );
+            createdNewEntry = true;
+        }
+    }
+    if ( allIsWell && createdNewEntry ) {
+        // 23.1.1 of 1003.1e states that as soon as there is a named user or
+        // named group entry, there needs to be a mask entry as well, so add 
+        // one.
+        setMaskPermissions( acl_calc_mask( &newACL ) );
+    }
+
+    if ( !allIsWell || acl_valid( newACL ) != 0 ) {
+        acl_free( newACL );
+        allIsWell = false;
+    } else {
+        acl_free( m_acl );
+        m_acl = newACL;
+    }
+    return allIsWell;
+}
+
+bool KACL::setNamedUserPermissions( const QString& name, unsigned short permissions \
) +{
+    return setNamedUserOrGroupPermissions( name, permissions, ACL_USER );
+}
+
+ACLUserPermissionsList KACL::allUserPermissions() const
+{
+    ACLUserPermissionsList list;
+    acl_entry_t entry;
+    uid_t id;
+    int ret = acl_get_entry( m_acl, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag ==  ACL_USER ) {
+            id = *( (uid_t*) acl_get_qualifier( entry ) );
+            QString name = getUserName( id );
+            unsigned short permissions = entryToPermissions( entry );
+            ACLUserPermissions pair = qMakePair( name, permissions );
+            list.append( pair );
+        }
+        ret = acl_get_entry( m_acl, ACL_NEXT_ENTRY, &entry );
+    }
+    return list;
+}
+
+bool KACL::setAllUsersOrGroups( const QValueList< QPair<QString, unsigned short> > \
&list, acl_tag_t type ) +{
+    bool allIsWell = true; 
+    bool atLeastOneUserOrGroup = false;
+
+    // make working copy, in case something goes wrong
+    acl_t newACL = acl_dup( m_acl ); 
+    acl_entry_t entry;
+
+//printACL( newACL, "Before cleaning: " );
+    // clear user entries
+    int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag ==  type ) {
+            acl_delete_entry( newACL, entry );
+            // we have to start from the beginning, the iterator is 
+            // invalidated, on deletion
+            ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
+        } else {
+            ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry );
+        }
+    }
+//printACL( newACL, "After cleaning out entries: " );
+
+    // now add the entries from the list
+    QValueList< QPair<QString, unsigned short> >::const_iterator it = \
list.constBegin(); +    while ( it != list.constEnd() ) {
+        acl_create_entry( &newACL, &entry );
+        acl_set_tag_type( entry, type );
+        int id = type == ACL_USER? getUidForName( (*it).first):getGidForName( \
(*it).first ); +        if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) {
+            // user or group doesn't exist => error
+            acl_delete_entry( newACL, entry );
+            allIsWell = false;
+            break;
+        } else {
+            permissionsToEntry( entry, (*it).second );
+            atLeastOneUserOrGroup = true;
+        }
+        ++it;
+    }
+//printACL( newACL, "After adding entries: " );
+    if ( allIsWell && atLeastOneUserOrGroup ) {
+        // 23.1.1 of 1003.1e states that as soon as there is a named user or
+        // named group entry, there needs to be a mask entry as well, so add 
+        // one.
+        setMaskPermissions( acl_calc_mask( &newACL ) );
+    }
+    if ( allIsWell && ( acl_valid( newACL ) == 0 ) ) {
+        acl_free( m_acl );
+        m_acl = newACL;
+    } else {
+        acl_free( newACL );
+    }
+    return allIsWell;
+}
+
+bool KACL::setAllUserPermissions( const ACLUserPermissionsList &users )
+{
+    return setAllUsersOrGroups( users, ACL_USER );
+}
+
+
+/**************************
+ * Deal with named groups  *
+ **************************/
+
+unsigned short KACL::namedGroupPermissions( const QString& name, bool *exists ) \
const +{
+    *exists = false;
+    acl_entry_t entry;
+    gid_t id;
+    int ret = acl_get_entry( m_acl, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag ==  ACL_GROUP ) {
+            id = *( (gid_t*) acl_get_qualifier( entry ) );
+            if ( getGroupName( id ) == name ) {
+                *exists = true;
+                return entryToPermissions( entry );
+            }
+        }
+        ret = acl_get_entry( m_acl, ACL_NEXT_ENTRY, &entry );
+    }
+
+    return 0;
+}
+
+bool KACL::setNamedGroupPermissions( const QString& name, unsigned short permissions \
) +{
+    return setNamedUserOrGroupPermissions( name, permissions, ACL_GROUP );
+}
+
+
+ACLGroupPermissionsList KACL::allGroupPermissions() const
+{
+    ACLGroupPermissionsList list;
+    acl_entry_t entry;
+    gid_t id;
+    int ret = acl_get_entry( m_acl, ACL_FIRST_ENTRY, &entry );
+    while ( ret == 1 ) {
+        acl_tag_t currentTag;
+        acl_get_tag_type( entry, &currentTag );
+        if ( currentTag ==  ACL_GROUP ) {
+            id = *( (gid_t*) acl_get_qualifier( entry ) );
+            QString name = getGroupName( id ); 
+            unsigned short permissions = entryToPermissions( entry );
+            ACLGroupPermissions pair = qMakePair( name, permissions );
+            list.append( pair );
+        }
+        ret = acl_get_entry( m_acl, ACL_NEXT_ENTRY, &entry );
+    }
+    return list;
+}
+
+bool KACL::setAllGroupPermissions( const ACLGroupPermissionsList &groups )
+{
+    return setAllUsersOrGroups( groups, ACL_GROUP );
+}
+
+/**************************
+ * from and to string     *
+ **************************/
+
+bool KACL::setACL( const QString &aclStr )
+{
+    bool ret = false;
+    acl_t temp = acl_from_text( aclStr.latin1() );
+    if ( acl_valid( temp ) != 0 ) {
+        // TODO errno is set, what to do with it here?
+        acl_free( temp );
+    } else {
+        if ( m_acl )
+            acl_free( m_acl );
+        m_acl = temp;
+        ret = true;
+    }
+    return ret;
+}
+
+QString KACL::asString() const
+{
+    ssize_t size = acl_size( m_acl );
+    return QString::fromLatin1( acl_to_text( m_acl, &size ) );
+}
+
+
+// helpers
+
+QString KACL::getUserName( uid_t uid ) const
+{
+    QString *temp;
+    temp = m_usercache.find( uid );
+    if ( !temp ) {
+        struct passwd *user = getpwuid( uid );
+        if ( user ) {
+            m_usercache.insert( uid, new QString(QString::fromLatin1(user->pw_name)) \
); +            return QString::fromLatin1( user->pw_name );
+        }
+        else
+            return QString::number( uid );
+    }
+    else
+        return *temp;
+}
+
+
+QString KACL::getGroupName( gid_t gid ) const
+{
+    QString *temp;
+    temp = m_groupcache.find( gid );
+    if ( !temp ) {
+        struct group *grp = getgrgid( gid );
+        if ( grp ) {
+            m_groupcache.insert( gid, new QString(QString::fromLatin1(grp->gr_name)) \
); +            return QString::fromLatin1( grp->gr_name );
+        }
+        else
+            return QString::number( gid );
+    }
+    else
+        return *temp;
+}
+
+void KACL::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+QDataStream & operator<< ( QDataStream & s, const KACL & a )
+{
+    s << a.asString();
+    return s;
+}
+
+QDataStream & operator>> ( QDataStream & s, KACL & a )
+{
+    QString str;
+    s >> str;
+    a.setACL( str );
+    return s;
+}
+
+// vim:set ts=8 sw=4:
Index: kio/kacl.h
===================================================================
--- kio/kacl.h	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ kio/kacl.h	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -0,0 +1,221 @@
+/* This file is part of the KDE project
+   Copyright (C) 2005 Till Adam <adam@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __kacl_h__
+#define __kacl_h__
+
+#include <sys/acl.h>
+#include <acl/libacl.h>
+
+#include <qintdict.h>
+
+#include <kio/global.h>
+
+
+typedef QPair<QString, unsigned short> ACLUserPermissions;
+typedef QValueList<ACLUserPermissions> ACLUserPermissionsList;
+typedef QValueListIterator<ACLUserPermissions> ACLUserPermissionsIterator;
+typedef QValueListConstIterator<ACLUserPermissions> ACLUserPermissionsConstIterator;
+
+typedef QPair<QString, unsigned short> ACLGroupPermissions;
+typedef QValueList<ACLGroupPermissions> ACLGroupPermissionsList;
+typedef QValueListIterator<ACLGroupPermissions> ACLGroupPermissionsIterator;
+typedef QValueListConstIterator<ACLGroupPermissions> \
ACLGroupPermissionsConstIterator; +
+/**
+ * The KCAL class encapsulates a POSIX Access Control List. It follows the 
+ * little standard that couldn't, 1003.1e/1003.2c, which died in draft status.
+ * @short a POSIX ACL encapsulation
+ * @author Till Adam <adam@kde.org>
+ */
+class KIO_EXPORT KACL
+{
+public:
+  /**
+   * Creates a new KACL from @p aclString. If the string is a valid acl
+   * string, isValid() will afterwards return true.
+   */
+  KACL( const QString & aclString );
+
+  /** Copy ctor */
+  KACL( const KACL& rhs );
+
+  /** 
+   * Creates a new KACL from the basic permissions passed in @p basicPermissions.
+   * isValid() will return true, afterwards.
+   */
+  KACL( mode_t basicPermissions );
+
+  /**
+   * Creates an empty KACL. Until a valid acl string is set via setACL,
+   * isValid() will return false.
+   */
+  KACL();
+
+  virtual ~KACL();
+
+  KACL& operator=( const KACL& rhs ) { 
+    if ( this != &rhs )
+      setACL( rhs.asString() );
+    return *this;
+  }
+
+  bool operator==( const KACL& rhs ) const {
+    return ( acl_cmp( m_acl, rhs.m_acl ) == 0 );
+  }
+
+  bool operator!=( const KACL& rhs ) const {
+    return !operator==( rhs );
+  }
+
+  /**
+   * Returns whether the KACL object represents a valid acl.
+   * @return whether the KACL object represents a valid acl.
+   */
+  bool isValid() const;
+
+  /** The standard (non-extended) part of an ACL. These map directly to 
+   * standard unix file permissions. Setting them will never make a valid
+   * ACL invalid. */
+
+  /** @return the owner's premissions entry */
+  unsigned short ownerPermissions() const;
+
+  /** Set the owner's permissions entry.
+   * @return success or failure */
+  bool setOwnerPermissions( unsigned short );
+
+  /** @return the owning group's premissions entry */
+  unsigned short owningGroupPermissions() const;
+
+  /** Set the owning group's permissions entry.
+   * @return success or failure */
+  bool setOwningGroupPermissions( unsigned short );
+
+  /** @return the premissions entry for others */
+  unsigned short othersPermissions() const;
+
+  /** Set the permissions entry for others.
+   * @return success or failure */
+  bool setOthersPermissions( unsigned short );
+
+  /** @return the basic (owner/group/others) part of the ACL as a mode_t */
+  mode_t basePermissions() const;
+
+  /** The interface to the extended ACL. This is a mask, permissions for 
+   * n named users and permissions for m named groups. */
+
+  /**
+   * Return whether the ACL contains extended entries or can be expressed
+   * using only basic file permissions.
+   * @return whether the ACL contains extended entries */
+  bool isExtended() const;
+
+  /**
+   * Return the entry for the permissions mask if there is one and sets
+   * @param exists to true. If there is no such entry, @param is set to false.
+   * @return the permissions mask entry */
+  unsigned short maskPermissions( bool &exists ) const;
+
+  /** Set the permissions mask for the ACL. Permissions set for individual 
+   * entries will be masked with this, such that their effective permissions
+   * are the result of the logical and of their entry and the mask. 
+   * @return success or failure */
+  bool setMaskPermissions( unsigned short );
+
+  /** 
+   * Access to the permissions entry for a named user, if such an entry 
+   * exists. @param exists is set to true if a matching entry exists and
+   * to false otherwise.
+   * @return the permissions for a user entry with the name in @p name */
+  unsigned short namedUserPermissions( const QString& name, bool *exits ) const;
+  /** Set the permissions for a user with the name @p name. Will fail
+   * if the user doesn't exist, in which case the ACL will be unchanged.
+   * @return success or failure. */
+  bool setNamedUserPermissions( const QString& name, unsigned short );
+
+  /** Returns the list of all group permission entries. Each entry consists
+   * of a name/permissions pair. This is a QPair, therefor access is provided 
+   * via the .first and .next members.
+   * @return the list of all group permission entries. */
+  ACLUserPermissionsList KACL::allUserPermissions() const;
+
+  /** Replace the list of all user permissions with @p list. If one
+   * of the entries in the list does not exists, or setting of the ACL
+   * entry fails for any reason, the ACL will be left unchanged.
+   * @return success or failure */
+  bool setAllUserPermissions( const ACLUserPermissionsList &list );
+
+  /**
+   * Access to the permissions entry for a named group, if such an entry 
+   * exists. @param exists is set to true if a matching entry exists and
+   * to false otherwise.
+   * @return the permissions for a group with the name in @p name */
+  unsigned short namedGroupPermissions( const QString& name, bool *exists ) const;
+
+  /** Set the permissions for a group with the name @p name. Will fail
+   * if the group doesn't exist, in which case the ACL be unchanged.
+   * @return success or failure. */
+  bool setNamedGroupPermissions( const QString& name, unsigned short );
+
+  /** Returns the list of all group permission entries. Each entry consists
+   * of a name/permissions pair. This is a QPair, therefor access is provided 
+   * via the .first and .next members.
+   * @return the list of all group permission entries. */
+
+  ACLGroupPermissionsList KACL::allGroupPermissions() const;
+  /** Replace the list of all user permissions with @p list. If one
+   * of the entries in the list does not exists, or setting of the ACL
+   * entry fails for any reason, the ACL will be left unchanged.
+   * @return success or failure */
+  bool setAllGroupPermissions( const ACLGroupPermissionsList & );
+
+  /** Sets the whole list from a string. If the string in @p aclStr represents 
+   * a valid ACL, it will be set, otherwise the ACL remains unchanged.
+   * @return whether setting the ACL was successful. */
+  bool setACL( const QString &aclStr );
+
+  /** Return a string representation of the ACL.
+   * @return a string version of the ACL in the format compatible with libacl and
+   * POSIX 1003.1e. Implementations conforming to that standard should be able
+   * to take such strings as input. */
+  QString asString() const;
+
+protected:
+  // helpers
+  QString getUserName( uid_t uid ) const;
+  QString getGroupName( gid_t gid ) const;
+  bool setAllUsersOrGroups( const QValueList< QPair<QString, unsigned short> > \
&list, acl_tag_t type ); +  bool setNamedUserOrGroupPermissions( const QString& name, \
unsigned short permissions, acl_tag_t type ); +  virtual void virtual_hook( int id, \
void* data ); +private:
+  void init();
+  acl_t m_acl;
+  mutable QIntDict<QString> m_usercache;
+  mutable QIntDict<QString> m_groupcache;
+  class KACLPrivate;
+  KACLPrivate * d;
+  KIO_EXPORT friend QDataStream & operator<< ( QDataStream & s, const KACL & a );
+  KIO_EXPORT friend QDataStream & operator>> ( QDataStream & s, KACL & a );
+};
+
+KIO_EXPORT QDataStream & operator<< ( QDataStream & s, const KACL & a );
+KIO_EXPORT QDataStream & operator>> ( QDataStream & s, KACL & a );
+
+#endif
Index: kio/Makefile.am
===================================================================
--- kio/Makefile.am	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kio/Makefile.am	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -42,6 +42,9 @@ libksycoca_la_SOURCES = \
 	kdatatool.cpp karchive.cpp kfilefilter.cpp \
 	kfilemetainfo.cpp kdcopservicestarter.cpp \
 	dataslave.cpp dataprotocol.cpp
+#if USE_POSIX_ACL
+  libksycoca_la_SOURCES += kacl.cpp
+#endif
 
 include_HEADERS = \
 	kservicetype.h kmimetype.h kmimemagic.h kservice.h \
@@ -55,6 +58,10 @@ include_HEADERS = \
 	karchive.h kfilefilter.h kfilemetainfo.h renamedlgplugin.h \
 	kmimetyperesolver.h kdcopservicestarter.h kremoteencoding.h \
 	kmimetypechooser.h
+#if USE_POSIX_ACL
+include_HEADERS += kacl.h
+#endif
+
 
 #libkiocore_la_LDFLAGS = $(all_libraries)
 #libkiocore_la_LIBADD = ../../kdeui/libkdeui.la ../../kdesu/libkdesu.la $(LIBZ) \
$(LIBFAM) $(LIBVOLMGT) @@ -98,6 +105,8 @@ kioinclude_HEADERS = connection.h \
 	ioslave_defaults.h http_slave_defaults.h previewjob.h thumbcreator.h \
 	metainfojob.h davjob.h renamedlg.h skipdlg.h
 
+
+  
 # Internal
 noinst_HEADERS = kservicetypefactory.h kservicefactory.h \
 	kmessageboxwrapper.h \
Index: kio/kfileitem.h
===================================================================
--- kio/kfileitem.h	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kio/kfileitem.h	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -26,6 +26,7 @@
 #include <qptrlist.h>
 #include <kio/global.h>
 #include <kurl.h>
+#include <kacl.h>
 #include <kmimetype.h>
 #include <kfilemetainfo.h>
 
@@ -139,6 +140,27 @@ public:
   QString permissionsString() const;
 
   /**
+   * Tells if the file has extended access level information ( Posix ACL )
+   * @return true if the file has extend ACL information or false if it hasn't
+   * @since 3.5
+  */
+  bool hasExtendedACL() const;
+
+  /**
+   * Returns the access control list for the file.
+   * @return the access control list as a KACL
+   * @since 3.5
+   */
+  KACL ACL() const;
+
+  /**
+   * Returns the default access control list for the directory.
+   * @return the default access control list as a KACL
+   * @since 3.5
+   */
+  KACL defaultACL() const;
+
+  /**
    * Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...).
    * @return the file type
    */
Index: kio/global.h
===================================================================
--- kio/global.h	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kio/global.h	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -325,8 +325,19 @@ namespace KIO
     /// A local file path if the ioslave display files sitting
     /// on the local filesystem (but in another hierarchy, e.g. media:/)
     UDS_LOCAL_PATH = 72 | UDS_STRING,
+    /// Indicates that the entry has extended ACL entries
+    /// @since 3.5
+    UDS_EXTENDED_ACL = 80 | UDS_LONG,
+    /// The access control list serialized into a single string.
+    /// @since 3.5
+    UDS_ACL_STRING = 88 | UDS_STRING,
+
+    /// The default access control list serialized into a single string.
+    /// Only available for directories.
+    /// @since 3.5
+    UDS_DEFAULT_ACL_STRING = 96 | UDS_STRING,
 
-    // available: 80, 88, 92, 100, 108 etc.
+    // available: 104, 112, 120 
 
     /// Access permissions (part of the mode returned by stat)
     UDS_ACCESS = 128 | UDS_LONG,
@@ -352,7 +363,7 @@ namespace KIO
     UDS_GUESSED_MIME_TYPE = 16392 | UDS_STRING,
     /// XML properties, e.g. for WebDAV
     /// @since 3.1
-    UDS_XML_PROPERTIES = 32768 | UDS_STRING
+    UDS_XML_PROPERTIES = 0x8000 | UDS_STRING
   };
 
   /**
Index: kio/chmodjob.cpp
===================================================================
--- kio/chmodjob.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kio/chmodjob.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -183,6 +183,13 @@ void ChmodJob::chmodNextFile()
         kdDebug(7007) << "ChmodJob::chmodNextFile chmod'ing " << \
                info.url.prettyURL()
                       << " to " << QString::number(info.permissions,8) << endl;
         KIO::SimpleJob * job = KIO::chmod( info.url, info.permissions );
+        // copy the metadata for acl and default acl
+        const QString aclString = queryMetaData( "ACL_STRING" );
+        const QString defaultAclString = queryMetaData( "DEFAULT_ACL_STRING" );
+        if ( !aclString.isEmpty() )
+            job->addMetaData( "ACL_STRING", aclString );
+        if ( !defaultAclString.isEmpty() )
+            job->addMetaData( "DEFAULT_ACL_STRING", defaultAclString );
         addSubjob(job);
     }
     else
Index: kfile/kacleditwidget.h
===================================================================
--- kfile/kacleditwidget.h	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ kfile/kacleditwidget.h	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -0,0 +1,54 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Sean Harmer <sh@rama.homelinux.org>             *
+ *                         Till Adam <adam@kde.org>                        *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef KACLEDITWIDGET_H
+#define KACLEDITWIDGET_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <klistview.h>
+#include <kacl.h>
+#include <kfileitem.h>
+
+class KACLListViewItem;
+class KACLListView;
+class QPushButton;
+
+class KACLEditWidget : QWidget
+{
+  Q_OBJECT
+public:
+  KACLEditWidget( QWidget *parent = 0, const char *name = 0 );
+  KACL getACL() const;
+  KACL getDefaultACL() const;
+  void setACL( const KACL & );
+  void setDefaultACL( const KACL & );
+private slots:
+  void slotUpdateButtons();
+private:
+  KACLListView* m_listView;
+  QPushButton *m_AddBtn;
+  QPushButton *m_EditBtn;
+  QPushButton *m_DelBtn;
+};
+
+
+#endif
Index: kfile/kpropertiesdialog.h
===================================================================
--- kfile/kpropertiesdialog.h	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kfile/kpropertiesdialog.h	(.../work/posix-acl-support/kdelibs/kio)	(revision \
443749) @@ -35,6 +35,7 @@
 #include <kurl.h>
 #include <kfileitem.h>
 #include <kdialogbase.h>
+#include <kacl.h>
 
 class QLineEdit;
 class QCheckBox;
@@ -45,6 +46,7 @@ class QButton;
 class KIconButton;
 class KPropsDlgPlugin;
 class QComboBox;
+class QGroupBox;
 
 #define KPropsPage KPropsDlgPlugin
 
Index: kfile/images.h
===================================================================
--- kfile/images.h	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ kfile/images.h	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -0,0 +1,275 @@
+#ifndef _QEMBED_1804289383
+#define _QEMBED_1804289383
+#include <qimage.h>
+#include <qdict.h>
+static const QRgb group_grey_data[] = {
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x42484848,0xc39b9b9b,0xeab1b1b1,0xce9d9d9d,0x5a4d4d4d,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x563b3b3b,0xfdaeaeae,0xffcfcfcf,0xffcccccc,0xffcecece,
 +    0xffbababa,0x62393939,0x0,0x0,0x0,0x0,0x0,0x0,0x4525252,0x9383838,0x0,0xd0515151,0xff969696,0xff959595,
 +    0xff969696,0xff959595,0xff969696,0xdd505050,0x6000000,0x0,0x0,0x0,0xa191919,0x908f8f8f,0xebc1c1c1,0xf6c6c6c6,0xc0a1a1a1,0xf74f4f4f,
 +    0xff626262,0xff6a6a6a,0xff6c6c6c,0xff6a6a6a,0xff636363,0xfb4a4a4a,0x1a000000,0x0,0x0,0x0,0xa3828282,0xffdfdfdf,0xffdedede,0xffdddddd,
 +    0xffe0e0e0,0xffa4a4a4,0xff636363,0xff666666,0xff6a6a6a,0xff676767,0xff5f5f5f,0xe6494949,0xd000000,0x0,0x0,0x21232323,0xfca2a2a2,0xffc3c3c3,
 +    0xffc6c6c6,0xffc6c6c6,0xffc4c4c4,0xffbababa,0xff717171,0xff7e7e7e,0xff7e7e7e,0xff7d7d7d,0xfe6f6f6f,0x812b2b2b,0x0,0x0,0x0,0x3e303030,
 +    0xffa6a6a6,0xffb7b7b7,0xffbdbdbd,0xffbebebe,0xffb9b9b9,0xffacacac,0xff808080,0xff8f8f8f,0xff939393,0xff909090,0xf86b6b6b,0x34202020,0x0,0x0,
 +    0x0,0x1c191919,0xf8a5a5a5,0xffc2c2c2,0xffcccccc,0xffcecece,0xffc5c5c5,0xffbababa,0xff888888,0xffa5a5a5,0xffa4a4a4,0xffa5a5a5,0xffa1a1a1,0xd3515151,
 +    0x8030303,0x0,0x0,0x0,0x8f6f6f6f,0xffd3d3d3,0xffe2e2e2,0xffe3e3e3,0xffdbdbdb,0xff9b9b9b,0xff6f6f6f,0xff727272,0xff6e6e6e,0xff717171,
 +    0xff707070,0xff606060,0x62363636,0x0,0x0,0x0,0x6e5b5b5b,0xffb4b4b4,0xffd4d4d4,0xffdadada,0xffcecece,0xff737373,0xff656565,0xff676767,
 +    0xff696969,0xff676767,0xff636363,0xff5d5d5d,0xc44b4b4b,0x0,0x0,0x27343434,0xf5a5a5a5,0xffd1d1d1,0xffd2d2d2,0xffd1d1d1,0xffd1d1d1,0xffc3c3c3,
 +    0xff7b7b7b,0xff6f6f6f,0xff727272,0xff6f6f6f,0xff696969,0xff626262,0xf7606060,0x16050505,0x0,0xa07d7d7d,0xffb8b8b8,0xffc0c0c0,0xffc2c2c2,0xffc1c1c1,
 +    0xffc1c1c1,0xffbbbbbb,0xffa6a6a6,0xff868686,0xff858585,0xff838383,0xff838383,0xff878787,0xe95e5e5e,0x19050505,0xd141414,0xf0a2a2a2,0xffbdbdbd,0xffc6c6c6,
 +    0xffcbcbcb,0xffcbcbcb,0xffc8c8c8,0xffc0c0c0,0xffb8b8b8,0xef8b8b8b,0xd6707070,0xd26a6a6a,0xbb595959,0x82363636,0x24070707,0x0,0x44505050,0xffc0c0c0,
 +    0xffc7c7c7,0xffd2d2d2,0xffd9d9d9,0xffdadada,0xffd4d4d4,0xffcacaca,0xffc1c1c1,0xc8979797,0x3000000,0x4000000,0x0,0x0,0x0,0x0,
 +    0x2b393939,0xeeaaaaaa,0xffdadada,0xffe4e4e4,0xffeaeaea,0xffeaeaea,0xffe4e4e4,0xffdddddd,0xffd1d1d1,0xae797979,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x1e0f0f0f,0x76575757,0xae898989,0xc49c9c9c,0xc6a0a0a0,0xbc9a9a9a,0x98808080,0x57414141,0xb000000,0x0,0x0,
 +    0x0,0x0,0x0,0x0
+};
+
+/* Generated by qembed */
+static const QRgb group_data[] = {
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4223731d,0xc37fbb7c,0xea9bca98,0xce86b982,0x5a316e2c,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x56146610,0xfd8fce8e,0xffbae4bb,0xffb7e2b7,0xffbae3ba,
 +    0xff9ed89d,0x62166112,0x0,0x0,0x0,0x0,0x0,0x0,0x4003ca5,0x9003171,0x0,0xd0198b17,0xff6ac468,0xff6ec665,
 +    0xff70c865,0xff6ec665,0xff6ac468,0xdd1a8918,0x6000000,0x0,0x0,0x0,0xa001333,0x905b8cc4,0xeb9fc0e4,0xf6a8c5e4,0xc07a9dc9,0xf7108e1e,
 +    0xff2eb113,0xff42be17,0xff49c216,0xff44be17,0xff31b214,0xfb109301,0x1a000000,0x0,0x0,0x0,0xa3497ebb,0xffc6def8,0xffc6ddf6,0xffc5ddf6,
 +    0xffc9e0f8,0xff77abd2,0xff31af18,0xff50cd00,0xff5cd400,0xff52ce00,0xff3bbe00,0xe6218f03,0xd000000,0x0,0x0,0x21042043,0xfc61a0e3,0xff97c3f0,
 +    0xff9cc7f1,0xff9cc8f1,0xff98c4f0,0xff85b8ef,0xff469d88,0xff7de517,0xff87ed10,0xff7ce417,0xfe5bca14,0x811b5304,0x0,0x0,0x0,0x3e082e58,
 +    0xff60a7ed,0xff7fbaef,0xff8ac3f1,0xff8bc4f1,0xff83beef,0xff6caeec,0xff4e98b3,0xff88d54a,0xff98e343,0xff89d64a,0xf846a630,0x340c4100,0x0,0x0,
 +    0x0,0x1c021631,0xf867a7e3,0xff90c9f4,0xffa1d6f8,0xffa4d8f8,0xff96cef5,0xff82bef2,0xff65aca9,0xff8ad576,0xff86d276,0xff88d377,0xff80d072,0xd3279310,
 +    0x8000700,0x0,0x0,0x0,0x8f3f6f9f,0xffaedcf9,0xffc7effe,0xffc9f0fe,0xffbbe5fc,0xff73b7c4,0xff45ba25,0xff51c61e,0xff50c617,0xff51c61c,
 +    0xff4bc120,0xff30b50c,0x62206705,0x0,0x0,0x0,0x6e1d5899,0xff81b2e7,0xffb4d7f4,0xffbddcf7,0xffa8cef4,0xff4698a0,0xff45c407,0xff53ce00,
 +    0xff59d200,0xff54cf00,0xff46c600,0xff34bb00,0xc42d9105,0x0,0x0,0x270a305f,0xf567a5e3,0xffaed3f4,0xffafd4f5,0xffaed4f4,0xffaed3f4,0xff94c2f3,
 +    0xff4fa782,0xff6cdf00,0xff74e400,0xff6cdf00,0xff5ad300,0xff43c401,0xf742b110,0x16060b00,0x0,0xa0407dba,0xff80bcf1,0xff8ec5f2,0xff92c8f2,0xff91c8f2,
 +    0xff90c6f2,0xff86bff0,0xff67a8e6,0xff80e02c,0xff95f615,0xff8aee18,0xff7de323,0xff76db33,0xe951a31a,0x19040a00,0xd000f28,0xf066a4de,0xff88c4f2,0xff97cef5,
 +    0xffa0d4f7,0xffa0d5f7,0xff9ad0f6,0xff8dc7f3,0xff7ebcf2,0xef6ea88b,0xd679b12f,0xd271a82d,0xbb5c9221,0x8235600c,0x24060d02,0x0,0x44184c89,0xff8cc6f4,
 +    0xff99d0f6,0xffabddfa,0xffb7e6fc,0xffb8e6fc,0xffaee0fb,0xff9ed4f7,0xff90c9f3,0xc86697c9,0x3000000,0x4000000,0x0,0x0,0x0,0x0,
 +    0x2b0e3664,0xee7caed8,0xffb9e4fc,0xffcaf1fe,0xffd5f6ff,0xffd6f7ff,0xffcbf2fe,0xffbee8fc,0xffa9d6f9,0xae5378a0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x1e020c1d,0x7634547a,0xae6a88a8,0xc4849bb4,0xc689a0b8,0xbc7d9ab7,0x98627f9f,0x572c3e56,0xb000000,0x0,0x0,
 +    0x0,0x0,0x0,0x0
+};
+
+static const QRgb mask_data[] = {
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x11c84a00,0x1000000,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68d14e00,0xffda6400,0x72bf4700,0x3000000,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14d04d00,0xefda6400,0xfffec300,0xf2d86300,0x24742b00,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98d14e00,0xfff3b537,0xfffffed6,
+    0xfff3b537,0xa5c04800,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30cf4d00,0xfbe17803,
 +    0xfff1e7ad,0xffcacaba,0xfff1e7ac,0xfce07803,0x42973800,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0xc4d35300,0xfffad45c,0xffc0c0b2,0xff979797,0xffb2b2a9,0xfffad45a,0xccca5000,0xa000000,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x58d14e00,0xffe89410,0xfffffcbd,0xffc6c6af,0xff929292,0xffbdbda8,0xfffffcb9,0xffe8930e,0x69b04100,0x2000000,0x0,0x0,0x0,
 +    0x0,0x0,0x8cb4c00,0xe3d76000,0xfffeec6c,0xffffffbe,0xffe2e2b2,0xff878787,0xffd5d5a8,0xffffffb7,0xfffeeb62,0xe8d35e00,0x17491b00,0x0,
 +    0x0,0x0,0x0,0x0,0x84d14e00,0xfff0b41a,0xfffffea3,0xffffffa8,0xfffbfba7,0xff7b7b76,0xfff6f6a0,0xffffff9c,0xfffffe8e,0xfff0b414,
 +    0x92bd4600,0x4000000,0x0,0x0,0x0,0x20d04d00,0xf7dd7400,0xfffff85e,0xffffff89,0xffffff8b,0xffffff8a,0xffc3c376,0xffffff82,0xffffff7b,
 +    0xffffff71,0xfffff746,0xf9db7300,0x32873200,0x0,0x0,0x0,0xa8d25200,0xfff6d518,0xffffff61,0xffffff65,0xffffff65,0xffecec60,0xff9f9f52,
 +    0xffe6e657,0xffffff54,0xffffff4c,0xffffff44,0xfff6d50e,0xb4c54c00,0x8000000,0x0,0x40d04e00,0xffe49001,0xfffffd2b,0xffffff3a,0xffffff3d,0xffffff3d,
 +    0xff9b9b33,0xff272727,0xff84842a,0xffffff2e,0xffffff28,0xffffff22,0xfffffd18,0xffe49000,0x52a33c00,0x2000000,0xd0d55b00,0xfffcef07,0xffffff17,0xffffff1a,
 +    0xffffff1b,0xffffff1b,0xffeaea19,0xff8c8c16,0xffdbdb13,0xffffff10,0xffffff0d,0xffffff0a,0xffffff07,0xfffcef02,0xd7ce5800,0xc000000,0xe4d55d00,0xffe49204,
 +    0xffe4940a,0xffe4950c,0xffe4960d,0xffe4950c,0xffe4950c,0xffe4940a,0xffe49308,0xffe49307,0xffe49205,0xffe39204,0xffe39102,0xffe39100,0xead05b00,0x1c000000,
 +    0x13582100,0x3c702900,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,0x40692700,
 +    0x29290f00,0xc000000,0x0,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,0x2000000,
 +    0x2000000,0x2000000,0x2000000,0x0
+};
+
+static const QRgb others_grey_data[] = {
+    0x0,0x0,0x0,0xa4c4c4c,0x5d676767,0x777c7c7c,0x3d555555,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x17535353,0xd2afafaf,0xffebebeb,0xffe5e5e5,0xfec2c2c2,0x906d6d6d,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0xa09c9c9c,0xfff1f1f1,0xfff5f5f5,0xffe6e6e6,0xffd4d4d4,0xffbebebe,0x4c424242,0x117b7b7b,
 +    0x357d7d7d,0x177c7c7c,0x0,0x0,0x0,0x0,0x0,0x606060,0xe4b4b4b4,0xffe1e1e1,0xffe4e4e4,0xffdcdcdc,0xffcecece,0xffc0c0c0,
 +    0xd9858585,0xf3d5d5d5,0xffe6e6e6,0xf8cdcdcd,0x8c828282,0x2030303,0x0,0x0,0x0,0x0,0xdcaaaaaa,0xffd0d0d0,0xffd2d2d2,0xffcecece,
 +    0xffc5c5c5,0xffa3a3a3,0xffe8e8e8,0xfffbfbfb,0xfff4f4f4,0xffeaeaea,0xffe0e0e0,0x6f767676,0x0,0x0,0x0,0x0,0x7e848484,0xffc3c3c3,
 +    0xffc3c3c3,0xffbfbfbf,0xffaeaeae,0xffaaaaaa,0xfff1f1f1,0xfff2f2f2,0xffefefef,0xffe6e6e6,0xffe0e0e0,0xcba6a6a6,0x0,0x0,0x0,0xc3f3f3f,
 +    0xb8858585,0xff8f8f8f,0xff969696,0xff919191,0xff787878,0xffa5a5a5,0xffe7e7e7,0xffe8e8e8,0xffe5e5e5,0xffe1e1e1,0xffdcdcdc,0xd4a6a6a6,0x0,0x0,
 +    0x0,0xa1959595,0xffdbdbdb,0xffe3e3e3,0xff999999,0xff7a7a7a,0xffb9b9b9,0xffb5b5b5,0xffdedede,0xffe0e0e0,0xffdfdfdf,0xffdbdbdb,0xffd1d1d1,0x8e898989,
 +    0x0,0x0,0x28363636,0xfcb2b2b2,0xffdadada,0xffededed,0xfff5f5f5,0xffd5d5d5,0xfff5f5f5,0xffcbcbcb,0xffb7b7b7,0xffd2d2d2,0xffd3d3d3,0xffc8c8c8,
 +    0xffb2b2b2,0x78979797,0x0,0x0,0x694b4b4b,0xffafafaf,0xffc8c8c8,0xffd1d1d1,0xffd8d8d8,0xffdbdbdb,0xffb9b9b9,0xffd7d7d7,0xffe4e4e4,0xffc3c3c3,
 +    0xffa0a0a0,0xffb9b9b9,0xffd2d2d2,0xffdbdbdb,0x5e979797,0x0,0x70434343,0xff9c9c9c,0xffadadad,0xffb6b6b6,0xffbbbbbb,0xffbbbbbb,0xffb0b0b0,0xffdfdfdf,
 +    0xfff5f5f5,0xfff8f8f8,0xfff2f2f2,0xfff8f8f8,0xfff6f6f6,0xffe3e3e3,0xe6bababa,0x90b0b0b,0x30232323,0xfb767676,0xff939393,0xff9a9a9a,0xff9f9f9f,0xff969696,
 +    0xffbbbbbb,0xffdadada,0xffe3e3e3,0xffe8e8e8,0xffeaeaea,0xffe9e9e9,0xffe5e5e5,0xffdedede,0xffc8c8c8,0x41363636,0x0,0x5a2e2e2e,0xde5b5b5b,0xfe707070,
 +    0xff7a7a7a,0xff727272,0xffb3b3b3,0xffcbcbcb,0xffd1d1d1,0xffd6d6d6,0xffd8d8d8,0xffd7d7d7,0xffd3d3d3,0xffcecece,0xffbfbfbf,0x57424242,0x0,0x0,
 +    0x4000000,0x20050505,0x33070707,0x3b181818,0xf2959595,0xffbababa,0xffbfbfbf,0xffc3c3c3,0xffc5c5c5,0xffc4c4c4,0xffc1c1c1,0xffbcbcbc,0xfe9f9f9f,0x321d1d1d,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x5c5f5f5f,0xf08a8a8a,0xffa7a7a7,0xffb0b0b0,0xffb2b2b2,0xffb0b0b0,0xffa9a9a9,0xf98e8e8e,
 +    0x874c4c4c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf101010,0x4a2b2b2b,0x65424242,0x6c4a4a4a,0x67444444,
 +    0x542e2e2e,0x200f0f0f,0x0,0x0
+};
+
+static const QRgb others_data[] = {
+    0x0,0x0,0x0,0xa804618,0x5d95643a,0x77a77c52,0x3d855126,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x17964f11,0xd2cfb190,0xfff8efdf,0xffffeccb,0xfeedce98,0x909b703f,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0xa0d29e66,0xfffff7e3,0xfffff8ec,0xffffedce,0xffffe0a9,0xfff5cd88,0x4c6f4316,0x11f72300,
 +    0x35fb2b00,0x17f82200,0x0,0x0,0x0,0x0,0x0,0xc15b00,0xe4ebbc7d,0xffffeac4,0xffffecca,0xffffe5b9,0xfffedb9e,0xfff4d38d,
 +    0xd9c76844,0xf3f6b8b4,0xfffcdad0,0xf8f5b3a6,0x8cba524a,0x2070000,0x0,0x0,0x0,0x0,0xdce9b56b,0xfffedda3,0xffffdea6,0xfffedb9e,
 +    0xfff8d592,0xffdf9f67,0xfffadad6,0xfffffaf8,0xfffef0ea,0xfffee2d6,0xfffed4c3,0x6fac4f41,0x0,0x0,0x0,0x0,0x7ecd933c,0xfff7d390,
 +    0xfff6d390,0xfff3d08b,0xffe6c377,0xffe29c73,0xfffeece4,0xfffeeee7,0xfffee9e0,0xfffddecf,0xfff9d8c8,0xcbd0927d,0x0,0x0,0x0,0xc007f00,
 +    0xb85db05a,0xffbbae64,0xffd4ab58,0xffd0a952,0xffba9636,0xffd9a571,0xfffddfd2,0xfffee0d3,0xfffdddce,0xfffbd8c8,0xfff6d2c2,0xd4d3927a,0x0,0x0,
 +    0x0,0xa171b973,0xffd4e9ce,0xffd4f2d5,0xffb2a680,0xffdb301a,0xff9cde94,0xffd3c097,0xfffbd4c2,0xfffad7c6,0xfff9d6c6,0xfff6d2c0,0xfff8c2ab,0x8ec26d51,
 +    0x0,0x0,0x28026b11,0xfc9ed391,0xffd4efc6,0xffeaf8e2,0xfff2fcee,0xffe5cdc6,0xfff4fcee,0xffb5cbe1,0xffd19da5,0xfff9c2ab,0xfff8c5ae,0xfff9b398,
 +    0xffd6918f,0x785982d5,0x0,0x0,0x691d792e,0xff9edc82,0xffbdeaa7,0xffc8edb6,0xffd0f0c0,0xffd4f2c4,0xffa8cac2,0xffbdccf2,0xffcfdefa,0xffbcb5d2,
 +    0xff9a91b0,0xffaba8ca,0xffaaccfb,0xffc4d1f2,0x5e6184ce,0x0,0x70196d2d,0xff88d663,0xff9dde7c,0xffa8e28a,0xffaee493,0xffb0e493,0xff89add8,0xffc6dbf8,
 +    0xffebf5ff,0xfff1f7ff,0xffe6f3ff,0xfff2f8ff,0xffeef6ff,0xffcfe0f8,0xe694b1e1,0x9000317,0x30004626,0xfb57b03d,0xff7ed651,0xff86d75e,0xff8cd966,0xff7cc16b,
 +    0xff84aff2,0xffb6dafe,0xffc8e4ff,0xffd2eaff,0xffd6ebff,0xffd4eaff,0xffcce6ff,0xffbee0ff,0xff96c2fb,0x410e1f5e,0x0,0x5a104c2a,0xde3b882f,0xfe51aa37,
 +    0xff5cb83c,0xff4e9c49,0xff70a8f6,0xff98cdff,0xffa4d3ff,0xffaed7ff,0xffb1d8ff,0xffb0d8ff,0xffa8d4ff,0xff9dcfff,0xff81bcfe,0x57132571,0x0,0x0,
 +    0x4000001,0x2000020a,0x3300080f,0x3b000d31,0xf24a83e0,0xff76bdff,0xff80c2ff,0xff88c5ff,0xff8cc6ff,0xff8ac6ff,0xff84c2ff,0xff7abeff,0xfe5694e9,0x32030738,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x5c1429ab,0xf03d77d8,0xff57a3f7,0xff62b0fe,0xff65b2ff,0xff63b1fe,0xff5aa6f9,0xf9427edb,
 +    0x87152a83,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf000021,0x4a020554,0x650b167a,0x6c0e1d86,0x670c187d,
 +    0x5403065a,0x2000001e,0x0,0x0
+};
+
+static const QRgb user_green_data[] = {
+    0x0,0x0,0x0,0x0,0x5029,0x6c1c6e21,0xe332aa3b,0xf83ac841,0xf838c83f,0xda369a3b,0x5a145819,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x7e1a6c1e,0xff32da39,0xff3de341,0xff3ee045,0xff3ee042,0xff3de345,0xff27d930,0x68125817,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f013105,0xf721a328,0xff22de27,0xff23dd27,0xff26dc26,0xff26dc2a,0xff22de27,
 +    0xff22de27,0xee268c2b,0x12001402,0x0,0x0,0x0,0x0,0x0,0x0,0x5c0b590f,0xff19b51d,0xff1ecc1e,0xff1dd31d,0xff22d41e,
 +    0xff1ed41e,0xff1cd21c,0xff1dcb21,0xff18b01b,0x4d093d0d,0x0,0x0,0x0,0x0,0x0,0x0,0x640f5f13,0xff18bc18,0xff1ec61a,
 +    0xff1ed119,0xff22d519,0xff22d519,0xff22ce1a,0xff1fc31b,0xff16be1a,0x5a0e4211,0x0,0x0,0x0,0x0,0x0,0x0,0x38033507,
 +    0xff1db91d,0xff21d818,0xff24e616,0xff2aec16,0xff2aec16,0xff24e416,0xff21d418,0xfd20b020,0x35031b05,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x2000000,0xbb2a702c,0xff2df018,0xff3af41c,0xff48f620,0xff46fa1c,0xff39f21a,0xff23ef13,0xb929632c,0x4000000,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x2e012703,0xfc279129,0xff3fe729,0xff5ff537,0xff5bf632,0xff2ef11f,0xf6298f2e,0x29010d02,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x6000e07,0xc41f7721,0xff1ee01e,0xff29dd2d,0xff22de27,0xff22de27,0xff2bdf34,0xff1ddf21,0xab227226,
 +    0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x6d136117,0xff1bc31b,0xff1ee01e,0xff24e020,0xff25e220,0xff25e220,0xff20e020,
 +    0xff20dc20,0xff1bbf1e,0x561b571f,0x0,0x0,0x0,0x0,0x0,0x8001205,0xe2268e29,0xff1eca1a,0xff23d41a,0xff23d81a,0xff23d81a,
 +    0xff23d81a,0xff23d81a,0xff24d11b,0xff1fc71b,0xd12a882c,0x4000000,0x0,0x0,0x0,0x0,0x4a0e5012,0xff1cc818,0xff21d119,0xff25db17,
 +    0xff25e117,0xff24e616,0xff24e616,0xff27df19,0xff21da18,0xff22cf19,0xff18c418,0x3a173d19,0x0,0x0,0x0,0x0,0x982b732c,0xff1ed915,
 +    0xff25dd17,0xff28ec14,0xff32f018,0xff34f21a,0xff32f218,0xff2af016,0xff28ea14,0xff20db17,0xff1bd317,0x85316f34,0x0,0x0,0x0,0x0,
 +    0xbf318131,0xff22e818,0xff2aea16,0xff39ef1b,0xff44fa1a,0xff4dfa1e,0xff4dfa1e,0xff3ff71b,0xff30f016,0xff2ae616,0xff20e616,0xb3337936,0x0,0x0,
 +    0x0,0x0,0x51134b16,0xed369834,0xff2ef11f,0xff54f828,0xff67fc2c,0xff6bfb33,0xff6ffc30,0xff5ffc28,0xff48f925,0xff30f41c,0xec33a135,0x561b3d1e,
 +    0x0,0x0,0x0,0x0,0x0,0x18000802,0x60113b14,0x9d315f33,0xbb417143,0xc6467848,0xc6467a48,0xbc417942,0x9e3c663e,0x64224225,
 +    0x1c020604,0x0,0x0,0x0
+};
+
+static const QRgb user_grey_data[] = {
+    0x0,0x0,0x0,0x0,0x404040,0x6c6e6e6e,0xe3b0b0b0,0xf8cecece,0xf8cccccc,0xdaa6a6a6,0x5a575757,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x7e6b6b6b,0xffd6d6d6,0xffe6e6e6,0xffe4e4e4,0xffe4e4e4,0xffe6e6e6,0xffcccccc,0x68555555,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f282828,0xf79d9d9d,0xffcccccc,0xffcdcdcd,0xffcecece,0xffcecece,0xffcccccc,
 +    0xffcccccc,0xee8f8f8f,0x12101010,0x0,0x0,0x0,0x0,0x0,0x0,0x5c515151,0xffa5a5a5,0xffbbbbbb,0xffc0c0c0,0xffc1c1c1,
 +    0xffc1c1c1,0xffbfbfbf,0xffbababa,0xffa0a0a0,0x4d383838,0x0,0x0,0x0,0x0,0x0,0x0,0x64585858,0xffaaaaaa,0xffb4b4b4,
 +    0xffbcbcbc,0xffbfbfbf,0xffbfbfbf,0xffbababa,0xffb2b2b2,0xffa9a9a9,0x5a404040,0x0,0x0,0x0,0x0,0x0,0x0,0x382e2e2e,
 +    0xffababab,0xffc0c0c0,0xffc9c9c9,0xffcfcfcf,0xffcecece,0xffc8c8c8,0xffbdbdbd,0xfda6a6a6,0x35181818,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x2000000,0xbb7c7c7c,0xffd3d3d3,0xffd9d9d9,0xffdfdfdf,0xffdedede,0xffd7d7d7,0xffcecece,0xb9717171,0x4000000,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x2e202020,0xfc939393,0xffdadada,0xfff0f0f0,0xffededed,0xffdadada,0xf6949494,0x290c0c0c,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x60c0c0c,0xc4787878,0xffcbcbcb,0xffd2d2d2,0xffcccccc,0xffcccccc,0xffd4d4d4,0xffc9c9c9,0xab777777,
 +    0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x6d5d5d5d,0xffb2b2b2,0xffcbcbcb,0xffcccccc,0xffcecece,0xffcecece,0xffcccccc,
 +    0xffcacaca,0xffafafaf,0x565c5c5c,0x0,0x0,0x0,0x0,0x0,0x80f0f0f,0xe2909090,0xffb7b7b7,0xffbebebe,0xffc1c1c1,0xffc1c1c1,
 +    0xffc1c1c1,0xffc1c1c1,0xffbdbdbd,0xffb5b5b5,0xd18f8f8f,0x4000000,0x0,0x0,0x0,0x0,0x4a4c4c4c,0xffb3b3b3,0xffbbbbbb,0xffc2c2c2,
 +    0xffc7c7c7,0xffc9c9c9,0xffc9c9c9,0xffc6c6c6,0xffc1c1c1,0xffb9b9b9,0xffb0b0b0,0x3a434343,0x0,0x0,0x0,0x0,0x987e7e7e,0xffbfbfbf,
 +    0xffc4c4c4,0xffcdcdcd,0xffd3d3d3,0xffd6d6d6,0xffd5d5d5,0xffd1d1d1,0xffcbcbcb,0xffc2c2c2,0xffbbbbbb,0x85808080,0x0,0x0,0x0,0x0,
 +    0xbf8e8e8e,0xffcccccc,0xffcccccc,0xffd5d5d5,0xffdddddd,0xffe0e0e0,0xffe0e0e0,0xffdbdbdb,0xffd2d2d2,0xffcacaca,0xffcacaca,0xb38a8a8a,0x0,0x0,
 +    0x0,0x0,0x514b4b4b,0xeda4a4a4,0xffdadada,0xffe7e7e7,0xffededed,0xfff1f1f1,0xfff0f0f0,0xffeaeaea,0xffe4e4e4,0xffdadada,0xeca9a9a9,0x56474747,
 +    0x0,0x0,0x0,0x0,0x0,0x18070707,0x603e3e3e,0x9d747474,0xbb8e8e8e,0xc6989898,0xc69a9a9a,0xbc959595,0x9e828282,0x64505050,
 +    0x1c070707,0x0,0x0,0x0
+};
+
+static const QRgb user_data[] = {
+    0x0,0x0,0x0,0x0,0x7f,0x6c2c68af,0xe384abdb,0xf8b2ccea,0xf8aecae9,0xda7ba3d1,0x5a20508d,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x7e2a66ac,0xffb8d4f3,0xffd2e5f9,0xffd0e3f8,0xffcfe3f8,0xffd3e5f9,0xffa7c9f0,0x681d4e8c,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f02244d,0xf75c9ade,0xffa6cbf2,0xffa7ccf2,0xffa9cef2,0xffa9cdf2,0xffa6ccf2,
 +    0xffa6ccf2,0xee4d8bd0,0x12000d1f,0x0,0x0,0x0,0x0,0x0,0x0,0x5c124c8f,0xff60a4ea,0xff88bcee,0xff8fc1f0,0xff92c4f0,
 +    0xff92c3f0,0xff8ec0f0,0xff85baee,0xff579fe9,0x4d0f3460,0x0,0x0,0x0,0x0,0x0,0x0,0x64185598,0xff67acec,0xff7ab8ee,
 +    0xff87c1f1,0xff8cc5f2,0xff8cc5f2,0xff84c0f0,0xff76b6ed,0xff63a9ee,0x5a173d69,0x0,0x0,0x0,0x0,0x0,0x0,0x38052a56,
 +    0xff6caee9,0xff8cc6f3,0xff9cd2f6,0xffa5d8f8,0xffa4d7f8,0xff99d0f6,0xff88c3f2,0xfd68a7e4,0x3505152b,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x2000000,0xbb447bb3,0xffabdbfa,0xffb6e4fc,0xffc0ecfe,0xffbeebfe,0xffb3e2fb,0xffa2d6f9,0xb9426e9f,0x4000000,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x2e011c3e,0xfc5491d2,0xffbce2f8,0xffe1f6fe,0xffdcf5fe,0xffb9e0fb,0xf65790d0,0x29010b17,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x6000017,0xc43276be,0xffa2cbf3,0xffb0d1f3,0xffa6cbf2,0xffa6cbf2,0xffb4d2f4,0xff9ec9f3,0xab3774b7,
 +    0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x6d1f599b,0xff76b3ed,0xffa3ccf3,0xffa5cff3,0xffa7d0f4,0xffa7d0f4,0xffa5cef3,
 +    0xffa2ccf2,0xff71afed,0x562c578c,0x0,0x0,0x0,0x0,0x0,0x800071e,0xe24f8fd0,0xff7ebaef,0xff8bc4f1,0xff90c7f2,0xff8fc7f2,
 +    0xff8fc6f2,0xff8fc6f2,0xff89c2f0,0xff7bb8ee,0xd1538dca,0x4000000,0x0,0x0,0x0,0x0,0x4a164781,0xff77b8ef,0xff85c2f1,0xff90caf4,
 +    0xff98cff5,0xff9cd2f6,0xff9bd1f6,0xff97cef5,0xff8ec8f3,0xff82bff0,0xff72b2ee,0x3a244062,0x0,0x0,0x0,0x0,0x98447db7,0xff8ac5f4,
 +    0xff94ccf4,0xffa1d6f8,0xffabddfa,0xffb0e0fb,0xffafe0fb,0xffa8dbfa,0xff9ed4f7,0xff90c9f4,0xff83c0f3,0x854d7cb2,0x0,0x0,0x0,0x0,
 +    0xbf5a8fc1,0xffa1d2f6,0xffa0d6f7,0xffafe0fa,0xffbceafe,0xffc2eefe,0xffc2eefe,0xffb9e8fd,0xffaaddfa,0xff9ed3f6,0xff9dd0f6,0xb35a88ba,0x0,0x0,
 +    0x0,0x0,0x511f4776,0xed77a7d1,0xffb8e0fb,0xffcff1fe,0xffdaf8ff,0xffe2f9ff,0xffe0f9ff,0xffd5f6ff,0xffcaeefe,0xffb8e1fc,0xec7ca9d6,0x562c4462,
 +    0x0,0x0,0x0,0x0,0x0,0x1800030d,0x601b3a61,0x9d4f7198,0xbb6e8cad,0xc67d96b3,0xc67d98b6,0xbc7494b5,0x9e6180a3,0x64364c69,
 +    0x1c040409,0x0,0x0,0x0
+};
+
+static const QRgb yes_data[] = {
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0,0x11049c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x1005a200,0x800ba600,0xd512a700,0xe1009d00,0x34007700,0x1000000,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x8009500,0x78009b00,0xf0039f03,0xe2009800,0x46003700,0xbb009300,0xeb11a111,0x35006f00,0x1000000,0x0,0x0,
 +    0x0,0x0,0x0,0x8008d00,0x70009300,0xe819ab19,0xff3ec63e,0xd4099109,0x45002c00,0xf000000,0x6f008900,0xff33d633,0xeb0a9e0a,0x35006800,
 +    0x1000000,0x0,0x0,0x4008900,0x6c008a00,0xe8099e09,0xff2cd52c,0xff3bd93b,0xcb048404,0x3c001c00,0xc000000,0x0,0x28007a00,0xff0abc0a,
 +    0xff10f410,0xeb019701,0x35006100,0x5006d00,0x60008100,0xdc009800,0xff06d206,0xff13fd13,0xff11ce11,0xc2017801,0x32000b00,0xa000000,0x0,0x0,
 +    0x1000000,0xde009100,0xff00f700,0xff00eb00,0xeb008e00,0xde008700,0xff00c400,0xff00f500,0xff00f700,0xff00c100,0xae006700,0x30000b00,0x8000000,0x0,
 +    0x0,0x0,0x0,0x97007100,0xff00dc00,0xff00eb00,0xff00e500,0xff00e700,0xff00eb00,0xff00eb00,0xff00a800,0xa2005c00,0x29000000,0x6000000,
 +    0x0,0x0,0x0,0x0,0x0,0x4c006600,0xff00af00,0xff00de00,0xff00de00,0xff00de00,0xff00dc00,0xff009600,0x96005100,0x24000000,
 +    0x5000000,0x0,0x0,0x0,0x0,0x0,0x0,0xa004e00,0xf8008200,0xff00d200,0xff00d200,0xff00ce00,0xfc008700,0x80004100,
 +    0x21000000,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb005f00,0xff00c300,0xff00c100,0xf6007500,
 +    0x76003800,0x1d000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f005800,0xff009700,
 +    0xf4006d00,0x6c003200,0x19000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x28005300,0xed005900,0x5a002800,0x16000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x1000000,0x15000000,0x12000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0
+};
+
+static const QRgb yespartial_data[] = {
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0,0x114e4e4e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x10515151,0x80535353,0xd5535353,0xe14e4e4e,0x343b3b3b,0x1000000,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x84a4a4a,0x784d4d4d,0x9e515151,0xe24c4c4c,0x461b1b1b,0xbb494949,0xeb595959,0x35373737,0x1000000,0x0,0x0,
 +    0x0,0x0,0x0,0x8464646,0x70494949,0xe8626262,0x93828282,0xc34d4d4d,0x45161616,0xf000000,0x6f444444,0x8c848484,0xcf545454,0x35343434,
 +    0x1000000,0x0,0x0,0x4444444,0x6c454545,0xe5535353,0x57808080,0x5e8a8a8a,0xcb444444,0x3c0e0e0e,0xc000000,0x0,0x283d3d3d,0xa7636363,
 +    0x32828282,0xeb4c4c4c,0x35303030,0x5363636,0x60404040,0xdc4c4c4c,0x756c6c6c,0x2b888888,0x7b6f6f6f,0xc23c3c3c,0x32050505,0xa000000,0x0,0x0,
 +    0x1000000,0xde484848,0x3c7b7b7b,0x1f757575,0xeb474747,0xde434343,0x80626262,0x3c7a7a7a,0x337b7b7b,0x56606060,0xae333333,0x30050505,0x8000000,0x0,
 +    0x0,0x0,0x0,0x97383838,0x4c6e6e6e,0xa757575,0x3727272,0x24737373,0x2b757575,0x27757575,0x75545454,0x952e2e2e,0x29000000,0x6000000,
 +    0x0,0x0,0x0,0x0,0x0,0x4c333333,0x8e575757,0x1c6f6f6f,0x46f6f6f,0xb6f6f6f,0x2b6e6e6e,0x994b4b4b,0x85282828,0x24000000,
 +    0x5000000,0x0,0x0,0x0,0x0,0x0,0x0,0xa272727,0xdc414141,0x3d696969,0x11696969,0x1e676767,0x91434343,0x77202020,
 +    0x21000000,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbb2f2f2f,0x5d616161,0x36606060,0x893a3a3a,
 +    0x761c1c1c,0x1d000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6f2c2c2c,0x9a4b4b4b,
 +    0x9b363636,0x6c191919,0x19000000,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x28292929,0xed2c2c2c,0x5a141414,0x16000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x1000000,0x15000000,0x12000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
 +    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+    0x0,0x0,0x0,0x0
+};
+
+static struct EmbedImage {
+    int width, height, depth;
+    const unsigned char *data;
+    int numColors;
+    const QRgb *colorTable;
+    bool alpha;
+    const char *name;
+} embed_image_vec[] = {
+    { 16, 16, 32, (const unsigned char*)group_grey_data, 0, 0, TRUE, "group-grey" },
+    { 16, 16, 32, (const unsigned char*)group_data, 0, 0, TRUE, "group" },
+    { 16, 16, 32, (const unsigned char*)mask_data, 0, 0, TRUE, "mask" },
+    { 16, 16, 32, (const unsigned char*)others_grey_data, 0, 0, TRUE, "others-grey" \
}, +    { 16, 16, 32, (const unsigned char*)others_data, 0, 0, TRUE, "others" },
+    { 16, 16, 32, (const unsigned char*)user_green_data, 0, 0, TRUE, "user-green" },
+    { 16, 16, 32, (const unsigned char*)user_grey_data, 0, 0, TRUE, "user-grey" },
+    { 16, 16, 32, (const unsigned char*)user_data, 0, 0, TRUE, "user" },
+    { 16, 16, 32, (const unsigned char*)yes_data, 0, 0, TRUE, "yes" },
+    { 16, 16, 32, (const unsigned char*)yespartial_data, 0, 0, TRUE, "yespartial" },
+    { 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static const QImage& qembed_findImage( const QString& name )
+{
+    static QDict<QImage> dict;
+    QImage* img = dict.find( name );
+    if ( !img ) {
+	for ( int i = 0; embed_image_vec[i].data; i++ ) {
+	    if ( strcmp(embed_image_vec[i].name, name.latin1()) == 0 ) {
+		img = new QImage((uchar*)embed_image_vec[i].data,
+			    embed_image_vec[i].width,
+			    embed_image_vec[i].height,
+			    embed_image_vec[i].depth,
+			    (QRgb*)embed_image_vec[i].colorTable,
+			    embed_image_vec[i].numColors,
+			    QImage::BigEndian );
+		if ( embed_image_vec[i].alpha )
+		    img->setAlphaBuffer( TRUE );
+		dict.insert( name, img );
+		break;
+	    }
+	}
+	if ( !img ) {
+	    static QImage dummy;
+	    return dummy;
+	}
+    }
+    return *img;
+}
+
+#endif
Index: kfile/kpropertiesdialog.cpp
===================================================================
--- kfile/kpropertiesdialog.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kfile/kpropertiesdialog.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision \
443749) @@ -46,10 +46,14 @@ extern "C" {
 #include <pwd.h>
 #include <grp.h>
 #include <time.h>
+#include <sys/types.h>
+#include <attr/xattr.h>
 }
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
+#include <algorithm>
+#include <functional>
 
 #include <qfile.h>
 #include <qdir.h>
@@ -67,6 +71,8 @@ extern "C" {
 #include <qtooltip.h>
 #include <qstyle.h>
 #include <qprogressbar.h>
+#include <qvbox.h>
+#include <qvaluevector.h>
 
 #include <kapplication.h>
 #include <kdialog.h>
@@ -106,11 +112,13 @@ extern "C" {
 #include <kprocess.h>
 #include <krun.h>
 #include <klistview.h>
+#include <kacl.h>
 #include "kfilesharedlg.h"
 
 #include "kpropertiesdesktopbase.h"
 #include "kpropertiesdesktopadvbase.h"
 #include "kpropertiesmimetypebase.h"
+#include "kacleditwidget.h"
 
 #include "kpropertiesdialog.h"
 
@@ -1457,6 +1465,9 @@ public:
   KFilePermissionsPropsPlugin::PermissionsMode pmode;
   bool canChangePermissions;
   bool isIrregular;
+  bool hasExtendedACL;
+  KACL extendedACL;
+  KACL defaultACL;
 };
 
 #define UniOwner    (S_IRUSR|S_IWUSR|S_IXUSR)
@@ -1511,6 +1522,9 @@ KFilePermissionsPropsPlugin::KFilePermis
   d->isIrregular = isIrregular(permissions, isDir, isLink);
   strOwner = item->user();
   strGroup = item->group();
+  d->hasExtendedACL = item->hasExtendedACL();
+  d->extendedACL = item->ACL();
+  d->defaultACL = item->defaultACL();
 
   if ( properties->items().count() > 1 )
   {
@@ -1523,6 +1537,7 @@ KFilePermissionsPropsPlugin::KFilePermis
 	d->isIrregular |= isIrregular((*it)->permissions(),
 				      (*it)->isDir() == isDir,
 				      (*it)->isLink() == isLink);
+      d->hasExtendedACL = d->hasExtendedACL || (*it)->hasExtendedACL();
       if ( (*it)->isLink() != isLink )
         isLink = false;
       if ( (*it)->isDir() != isDir )
@@ -1819,23 +1834,28 @@ void KFilePermissionsPropsPlugin::slotSh
   QGroupBox *gb;
   QGridLayout *gl;
 
+  QVBox *mainVBox = dlg.makeVBoxMainWidget();
+
   // Group: Access Permissions
-  gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), &dlg );
+  gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), mainVBox );
   gb->layout()->setSpacing(KDialog::spacingHint());
   gb->layout()->setMargin(KDialog::marginHint());
-  dlg.setMainWidget(gb);
 
   gl = new QGridLayout (gb->layout(), 6, 6);
   gl->addRowSpacing(0, 10);
 
-  l = new QLabel(i18n("Class"), gb);
+  QValueVector<QWidget*> theNotSpecials;
+
+  l = new QLabel(i18n("Class"), gb );
   gl->addWidget(l, 1, 0);
+  theNotSpecials.append( l );
 
   if (isDir)
     l = new QLabel( i18n("Show\nEntries"), gb );
   else
     l = new QLabel( i18n("Read"), gb );
   gl->addWidget (l, 1, 1);
+  theNotSpecials.append( l );
   QString readWhatsThis;
   if (isDir)
     readWhatsThis = i18n("This flag allows viewing the content of the folder.");
@@ -1848,6 +1868,7 @@ void KFilePermissionsPropsPlugin::slotSh
   else
     l = new QLabel( i18n("Write"), gb );
   gl->addWidget (l, 1, 2);
+  theNotSpecials.append( l );
   QString writeWhatsThis;
   if (isDir)
     writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. \
" @@ -1866,6 +1887,7 @@ void KFilePermissionsPropsPlugin::slotSh
     execWhatsThis = i18n("Enable this flag to allow executing the file as a \
program.");  }
   QWhatsThis::add(l, execWhatsThis);
+  theNotSpecials.append( l );
   // GJ: Add space between normal and special modes
   QSize size = l->sizeHint();
   size.setWidth(size.width() + 15);
@@ -1885,14 +1907,17 @@ void KFilePermissionsPropsPlugin::slotSh
 
   cl[0] = new QLabel( i18n("User"), gb );
   gl->addWidget (cl[0], 2, 0);
+  theNotSpecials.append( cl[0] );
 
   cl[1] = new QLabel( i18n("Group"), gb );
   gl->addWidget (cl[1], 3, 0);
+  theNotSpecials.append( cl[1] );
 
   cl[2] = new QLabel( i18n("Others"), gb );
   gl->addWidget (cl[2], 4, 0);
+  theNotSpecials.append( cl[2] );
 
-  l = new QLabel(i18n("Set UID"), gb);
+  l = new QLabel(i18n("Set UID"), gb );
   gl->addWidget(l, 2, 5);
   QString setUidWhatsThis;
   if (isDir)
@@ -1959,7 +1984,8 @@ void KFilePermissionsPropsPlugin::slotSh
   QCheckBox *cba[3][4];
   for (int row = 0; row < 3 ; ++row) {
     for (int col = 0; col < 4; ++col) {
-      QCheckBox *cb = new QCheckBox(gb);
+      QCheckBox *cb = new QCheckBox( gb );
+      if ( col != 3 ) theNotSpecials.append( cb );
       cba[row][col] = cb;
       cb->setChecked(aPermissions & fperm[row][col]);
       if ( aPartialPermissions & fperm[row][col] )
@@ -2000,6 +2026,26 @@ void KFilePermissionsPropsPlugin::slotSh
   }
   gl->setColStretch(6, 10);
 
+  KACLEditWidget *extendedACLs = 0;
+  bool fileSystemSupportsACLs = false;
+
+  // FIXME make it work with partial entries
+  if ( properties->items().count() == 1 ) {
+    QCString pathCString = QFile::encodeName( properties->item()->url().path() );
+    fileSystemSupportsACLs =
+      getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || \
errno == ENODATA; +  }
+  if ( fileSystemSupportsACLs  ) {
+    std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( \
&QWidget::hide ) ); +    extendedACLs = new KACLEditWidget( mainVBox );
+    if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
+      extendedACLs->setACL( d->extendedACL );
+    else
+      extendedACLs->setACL( KACL( aPermissions ) );
+
+    if ( d->defaultACL.isValid() )
+      extendedACLs->setDefaultACL( d->defaultACL );
+  }
   if (dlg.exec() != KDialogBase::Accepted)
     return;
 
@@ -2033,6 +2079,15 @@ void KFilePermissionsPropsPlugin::slotSh
   permissions = orPermissions;
   d->partialPermissions = andPermissions;
 
+  // override with the acls, if present
+  if ( extendedACLs ) {
+    d->extendedACL = extendedACLs->getACL();
+    d->defaultACL = extendedACLs->getDefaultACL();
+    d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
+    permissions = d->extendedACL.basePermissions();
+    permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
+  }
+
   emit changed();
   updateAccessControls();
 }
@@ -2168,9 +2223,9 @@ void KFilePermissionsPropsPlugin::update
     enableAccessControls(false);
     break;
   case PermissionsOnlyFiles:
-    enableAccessControls(d->canChangePermissions && !d->isIrregular);
+    enableAccessControls(d->canChangePermissions && !d->isIrregular && \
!d->hasExtendedACL);  if (d->canChangePermissions)
-      d->explanationLabel->setText(d->isIrregular ?
+      d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
 				   i18n("This file uses advanced permissions",
 				      "These files use advanced permissions.",
 				      properties->items().count()) : "");
@@ -2184,9 +2239,14 @@ void KFilePermissionsPropsPlugin::update
     }
     break;
   case PermissionsOnlyDirs:
-    enableAccessControls(d->canChangePermissions && !d->isIrregular);
+    enableAccessControls(d->canChangePermissions && !d->isIrregular && \
!d->hasExtendedACL); +    // if this is a dir, and we can change permissions, don't \
dis-allow  +    // recursive, we can do that for ACL setting.
+    if ( d->cbRecursive )
+       d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
+
     if (d->canChangePermissions)
-      d->explanationLabel->setText(d->isIrregular ?
+      d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
 				   i18n("This folder uses advanced permissions.",
 				      "These folders use advanced permissions.",
 				      properties->items().count()) : "");
@@ -2200,9 +2260,9 @@ void KFilePermissionsPropsPlugin::update
     }
     break;
   case PermissionsMixed:
-    enableAccessControls(d->canChangePermissions && !d->isIrregular);
+    enableAccessControls(d->canChangePermissions && !d->isIrregular && \
!d->hasExtendedACL);  if (d->canChangePermissions)
-      d->explanationLabel->setText(d->isIrregular ?
+      d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
 				   i18n("These files use advanced permissions.") : "");
     break;
     if (d->partialPermissions & S_ISVTX) {
@@ -2348,12 +2408,22 @@ void KFilePermissionsPropsPlugin::applyC
     }
   }
 
-  if ( !owner.isEmpty() || !group.isEmpty() || recursive || permissionChange)
-  {
+  const bool ACLChange = ( d->extendedACL !=  properties->item()->ACL() );
+  const bool defaultACLChange = ( d->defaultACL != properties->item()->defaultACL() \
); +
+  if ( owner.isEmpty() && group.isEmpty() && !recursive 
+      && !permissionChange && !ACLChange && !defaultACLChange )
+    return;
+
     KIO::Job * job;
     if (files.count() > 0) {
       job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
 			owner, group, false );
+    if ( ACLChange )
+      job->addMetaData( "ACL_STRING", \
d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); +    if ( \
defaultACLChange ) +      job->addMetaData( "DEFAULT_ACL_STRING", \
d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); +
       connect( job, SIGNAL( result( KIO::Job * ) ),
 	       SLOT( slotChmodResult( KIO::Job * ) ) );
       // Wait for job
@@ -2365,6 +2435,11 @@ void KFilePermissionsPropsPlugin::applyC
     if (dirs.count() > 0) {
       job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
 			owner, group, recursive );
+    if ( ACLChange )
+      job->addMetaData( "ACL_STRING", \
d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); +    if ( \
defaultACLChange ) +      job->addMetaData( "DEFAULT_ACL_STRING", \
d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); +
       connect( job, SIGNAL( result( KIO::Job * ) ),
 	       SLOT( slotChmodResult( KIO::Job * ) ) );
       // Wait for job
@@ -2373,7 +2448,6 @@ void KFilePermissionsPropsPlugin::applyC
       qApp->enter_loop();
       qt_leave_modal(&dummy);
     }
-  }
 }
 
 void KFilePermissionsPropsPlugin::slotChmodResult( KIO::Job * job )
Index: kfile/kacleditwidget_p.h
===================================================================
--- kfile/kacleditwidget_p.h	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ kfile/kacleditwidget_p.h	(.../work/posix-acl-support/kdelibs/kio)	(revision \
443749) @@ -0,0 +1,185 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Sean Harmer <sh@rama.homelinux.org>             *
+ *                         Till Adam <adam@kde.org>                        *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef KACLEDITWIDGET_P_H
+#define KACLEDITWIDGET_P_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <klistview.h>
+#include <kacl.h>
+#include <kfileitem.h>
+#include <kdialogbase.h>
+#include <qpixmap.h>
+
+class KACLListViewItem;
+class QPushButton;
+class QVButtonGroup;
+class KACLListView;
+class QWidgetStack;
+class QCheckBox;
+
+/**
+@author Sean Harmer
+*/
+class KACLListView : public KListView
+{
+Q_OBJECT
+    friend class KACLListViewItem;
+public:
+    enum Types
+    {
+        OWNER_IDX = 0,
+        GROUP_IDX,
+        OTHERS_IDX,
+        MASK_IDX,
+        NAMED_USER_IDX,
+        NAMED_GROUP_IDX,
+        LAST_IDX
+    };
+    enum EntryType { User = 1,
+                     Group = 2,
+                     Others = 4,
+                     Mask = 8,
+                     NamedUser = 16,
+                     NamedGroup = 32,
+                     AllTypes = 63 };
+
+    KACLListView( QWidget* parent = 0, const char* name = 0 );
+    ~KACLListView();
+
+    bool hasMaskEntry() const { return m_hasMask; }
+    bool hasDefaultEntries() const;
+    bool allowDefaults() const { return m_allowDefaults; }
+    void setAllowDefaults( bool v ) { m_allowDefaults = v; }
+    unsigned short maskPermissions() const;
+    void setMaskPermissions( unsigned short maskPerms );
+    acl_perm_t maskPartialPermissions() const;
+    void setMaskPartialPermissions( acl_perm_t maskPerms );
+
+    const KACLListViewItem* findDefaultItemByType( EntryType type ) const;
+    const KACLListViewItem* findItemByType( EntryType type,
+                                            bool defaults = false ) const;
+    unsigned short calculateMaskValue( bool defaults ) const;
+    void calculateEffectiveRights();
+
+    QStringList allowedUsers( bool defaults, KACLListViewItem *allowedItem = 0 );
+    QStringList allowedGroups( bool defaults, KACLListViewItem *allowedItem = 0 );
+
+    const KACL getACL() const { return getACL(); }
+    KACL getACL();
+
+    const KACL getDefaultACL() const { return getDefaultACL(); }
+    KACL getDefaultACL();
+
+public slots:
+    void slotAddEntry();
+    void slotEditEntry();
+    void slotRemoveEntry();
+    void setACL( const KACL &anACL );
+    void setDefaultACL( const KACL &anACL );
+
+protected slots:
+    void entryClicked( QListViewItem* pItem, const QPoint& pt, int col );
+protected:
+    void contentsMousePressEvent( QMouseEvent * e );
+
+private:
+    void fillItemsFromACL( const KACL &pACL, bool defaults = false );
+    KACL itemsToACL( bool defaults ) const;
+
+    KACL m_ACL;
+    KACL m_defaultACL;
+    unsigned short m_mask;
+    bool m_hasMask;
+    bool m_allowDefaults;
+    QStringList m_allUsers;
+    QStringList m_allGroups;
+};
+
+class EditACLEntryDialog : public KDialogBase
+{
+    Q_OBJECT
+public:
+    EditACLEntryDialog( KACLListView *listView, KACLListViewItem *item,
+                        const QStringList &users,
+                        const QStringList &groups,
+                        const QStringList &defaultUsers,
+                        const QStringList &defaultGroups,
+                        int allowedTypes = KACLListView::AllTypes,
+                        int allowedDefaultTypes = KACLListView::AllTypes,
+                        bool allowDefault = false );
+    KACLListViewItem* item() const { return m_item; }
+public slots:
+     void slotOk();
+     void slotSelectionChanged( int id );
+private slots:
+     void slotUpdateAllowedUsersAndGroups();
+     void slotUpdateAllowedTypes();
+private:
+     KACLListView *m_listView;
+     KACLListViewItem *m_item;
+     QStringList m_users;
+     QStringList m_groups;
+     QStringList m_defaultUsers;
+     QStringList m_defaultGroups;
+     int m_allowedTypes;
+     int m_allowedDefaultTypes;
+     QVButtonGroup *m_buttonGroup;
+     QComboBox *m_usersCombo;
+     QComboBox *m_groupsCombo;
+     QWidgetStack *m_widgetStack;
+     QCheckBox *m_defaultCB;
+};
+
+
+class KACLListViewItem : public KListViewItem
+{
+public:
+    KACLListViewItem( QListView* parent, KACLListView::EntryType type,
+                      unsigned short value,
+                      bool defaultEntry,
+                      const QString& qualifier = QString::null );
+    virtual ~KACLListViewItem();
+    virtual QString key( int column, bool ascending ) const;
+
+    void calcEffectiveRights();
+
+    void togglePerm( acl_perm_t perm );
+
+    virtual void paintCell( QPainter *p, const QColorGroup &cg,
+                            int column, int width, int alignment );
+
+    void updatePermPixmaps();
+    void repaint();
+
+    KACLListView::EntryType type;
+    unsigned short value;
+    bool isDefault;
+    QString qualifier;
+    bool isPartial;
+
+private:
+    KACLListView* m_pACLListView;
+};
+
+
+#endif
Index: kfile/kacleditwidget.cpp
===================================================================
--- kfile/kacleditwidget.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ kfile/kacleditwidget.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision \
443749) @@ -0,0 +1,999 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Sean Harmer <sh@rama.homelinux.org>             *
+ *                         Till Adam <adam@kde.org>                        *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "kacleditwidget.h"
+#include "kacleditwidget_p.h"
+
+#include <qpainter.h>
+#include <qptrlist.h>
+#include <qvbox.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qvbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qwidgetstack.h>
+#include <qheader.h>
+#include <qpixmapcache.h>
+
+#include <klocale.h>
+#include <kfileitem.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+
+#ifdef HAVE_ACL_LIBACL_H
+# include <acl/libacl.h>
+#endif
+extern "C" {
+#include <pwd.h>
+#include <grp.h>
+}
+#include <assert.h>
+
+#include "images.h"
+
+static const struct {
+    const char* label;
+    const char* pixmap;
+} s_itemAttributes[] = {
+    { I18N_NOOP( "Owner" ), "user-grey" },
+    { I18N_NOOP( "Owning Group" ), "group-grey" },
+    { I18N_NOOP( "Others" ), "others-grey" },
+    { I18N_NOOP( "Mask" ), "mask" },
+    { I18N_NOOP( "Named User" ), "user" },
+    { I18N_NOOP( "Named Group" ), "group" },
+};
+
+KACLEditWidget::KACLEditWidget( QWidget *parent, const char *name )
+   :QWidget( parent, name )
+{
+    QHBox *hbox = new QHBox( parent );
+    m_listView = new KACLListView( hbox, "acl_listview" );
+    connect( m_listView, SIGNAL( selectionChanged() ),
+            this, SLOT( slotUpdateButtons() ) );
+    QVBox *vbox = new QVBox( hbox );
+    vbox->setSpacing(  KDialog::spacingHint() );
+    m_AddBtn = new QPushButton( i18n( "Add Entry" ), vbox, "add_entry_button" );
+    connect( m_AddBtn, SIGNAL( clicked() ), m_listView, SLOT( slotAddEntry() ) );
+    m_EditBtn = new QPushButton( i18n( "Edit Entry" ), vbox, "edit_entry_button" );
+    connect( m_EditBtn, SIGNAL( clicked() ), m_listView, SLOT( slotEditEntry() ) );
+    m_DelBtn = new QPushButton( i18n( "Delete Entry" ), vbox, "delete_entry_button" \
); +    connect( m_DelBtn, SIGNAL( clicked() ), m_listView, SLOT( slotRemoveEntry() ) \
); +    QWidget *spacer = new QWidget( vbox );
+    spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding );
+    slotUpdateButtons();
+}
+
+void KACLEditWidget::slotUpdateButtons()
+{
+    int selectedItemsCount = 0;
+    QListViewItemIterator it( m_listView, QListViewItemIterator::Selected );
+    while ( it.current() ) {
+        ++it;
+        if ( ++selectedItemsCount > 1 )
+            break;
+    }
+    m_EditBtn->setEnabled( selectedItemsCount == 1 );
+    m_DelBtn->setEnabled( selectedItemsCount > 0 );
+}
+
+KACL KACLEditWidget::getACL() const
+{
+  return m_listView->getACL();
+}
+
+KACL KACLEditWidget::getDefaultACL() const
+{
+  return m_listView->getDefaultACL();
+}
+
+void KACLEditWidget::setACL( const KACL &acl )
+{
+  return m_listView->setACL( acl );
+}
+
+void KACLEditWidget::setDefaultACL( const KACL &acl )
+{
+  return m_listView->setDefaultACL( acl );
+}
+
+
+KACLListViewItem::KACLListViewItem( QListView* parent,
+                                    KACLListView::EntryType _type,
+                                    unsigned short _value, bool defaults,
+                                    const QString& _qualifier )
+ : KListViewItem( parent, parent->lastItem() ), // we want to append
+   type( _type ), value( _value ), isDefault( defaults ),
+   qualifier( _qualifier ), isPartial( false )
+{
+    m_pACLListView = dynamic_cast<KACLListView*>( parent );
+    repaint();
+}
+
+
+KACLListViewItem::~ KACLListViewItem()
+{
+
+}
+
+QString KACLListViewItem::key( int, bool ) const
+{
+    QString key;
+    if ( !isDefault )
+        key = "A";
+    else
+        key = "B";
+    switch ( type )
+    {
+        case KACLListView::User:
+            key += "A";
+            break;
+        case KACLListView::Group:
+            key += "B";
+            break;
+        case KACLListView::Others:
+            key += "C";
+            break;
+        case KACLListView::Mask:
+            key += "D";
+            break;
+        case KACLListView::NamedUser:
+            key += "E" + text( 1 );
+            break;
+        case KACLListView::NamedGroup:
+            key += "F" + text( 1 );
+            break;
+        default:
+            key += text( 0 );
+            break;
+    }
+    return key;
+}
+
+void KACLListViewItem::paintCell( QPainter* p, const QColorGroup &cg,
+                                  int column, int width, int alignment )
+{
+    QColorGroup mycg = cg;
+    if ( isDefault ) {
+        mycg.setColor( QColorGroup::Text, QColor( 0, 0, 255 ) );
+    }
+    if ( isPartial ) {
+        QFont font = p->font();
+        font.setItalic( true );
+        mycg.setColor( QColorGroup::Text, QColor( 100, 100, 100 ) );
+        p->setFont( font );
+    }
+    KListViewItem::paintCell( p, mycg, column, width, alignment );
+
+    KACLListViewItem *below =0;
+    if ( itemBelow() )
+        below = static_cast<KACLListViewItem*>( itemBelow() );
+    const bool lastUser = type == KACLListView::NamedUser && below && below->type == \
KACLListView::NamedGroup; +    const bool lastNonDefault = !isDefault && below && \
below->isDefault; +    if ( type == KACLListView::Mask || lastUser || lastNonDefault \
) +    {
+        p->setPen( QPen( Qt::gray, 0, QPen::DotLine ) );
+        if ( type == KACLListView::Mask )
+            p->drawLine( 0, 0, width - 1, 0 );
+        p->drawLine( 0, height() - 1, width - 1, height() - 1 );
+    }
+}
+
+
+void KACLListViewItem::updatePermPixmaps()
+{
+    unsigned int partialPerms = value;
+
+    if ( value & ACL_READ )
+        setPixmap( 2, *QPixmapCache::find("yes") );
+    else if ( partialPerms & ACL_READ )
+        setPixmap( 2, *QPixmapCache::find("yespartial") );
+    else
+        setPixmap( 2, QPixmap() );
+
+    if ( value & ACL_WRITE )
+        setPixmap( 3, *QPixmapCache::find("yes") );
+    else if ( partialPerms & ACL_WRITE )
+        setPixmap( 3, *QPixmapCache::find("yespartial") );
+    else
+        setPixmap( 3, QPixmap() );
+
+    if ( value & ACL_EXECUTE )
+        setPixmap( 4, *QPixmapCache::find("yes") );
+    else if ( partialPerms & ACL_EXECUTE )
+        setPixmap( 4, *QPixmapCache::find("yespartial") );
+    else
+        setPixmap( 4, QPixmap() );
+}
+
+void KACLListViewItem::repaint()
+{
+    int idx = 0;
+    switch ( type )
+    {
+      case KACLListView::User:
+          idx = KACLListView::OWNER_IDX;
+            break;
+        case KACLListView::Group:
+          idx = KACLListView::GROUP_IDX;
+            break;
+        case KACLListView::Others:
+          idx = KACLListView::OTHERS_IDX;
+            break;
+        case KACLListView::Mask:
+          idx = KACLListView::MASK_IDX;
+            break;
+        case KACLListView::NamedUser:
+          idx = KACLListView::NAMED_USER_IDX;
+            break;
+        case KACLListView::NamedGroup:
+          idx = KACLListView::NAMED_GROUP_IDX;
+            break;
+        default:
+          idx = KACLListView::OWNER_IDX;
+            break;
+    }
+    QPixmap *pic = QPixmapCache::find( s_itemAttributes[idx].pixmap );
+    assert( pic );
+    setPixmap( 0, *pic );
+    setText( 0, s_itemAttributes[idx].label );
+    if ( isDefault )
+        setText( 0, text( 0 ) + i18n( " (Default)" ) );
+    setText( 1, qualifier );
+    // Set the pixmaps for which of the perms are set
+    updatePermPixmaps();
+}
+
+void KACLListViewItem::calcEffectiveRights()
+{
+    QString strEffective = QString( "---" );
+
+    // Do we need to worry about the mask entry? It applies to named users,
+    // owning group, and named groups
+    if ( m_pACLListView->hasMaskEntry()
+            && ( type == KACLListView::NamedUser
+              || type == KACLListView::Group
+              || type == KACLListView::NamedGroup ) 
+            && !isDefault )
+    {
+
+        strEffective[0] = ( m_pACLListView->maskPermissions() & value & ACL_READ ) ? \
'r' : '-'; +        strEffective[1] = ( m_pACLListView->maskPermissions() & value & \
ACL_WRITE ) ? 'w' : '-'; +        strEffective[2] = ( \
m_pACLListView->maskPermissions() & value & ACL_EXECUTE ) ? 'x' : '-'; +/*
+        // What about any partial perms?
+        if ( maskPerms & partialPerms & ACL_READ || // Partial perms on entry
+             maskPartialPerms & perms & ACL_READ || // Partial perms on mask
+             maskPartialPerms & partialPerms & ACL_READ ) // Partial perms on mask \
and entry +            strEffective[0] = 'R';
+        if ( maskPerms & partialPerms & ACL_WRITE || // Partial perms on entry
+             maskPartialPerms & perms & ACL_WRITE || // Partial perms on mask
+             maskPartialPerms & partialPerms & ACL_WRITE ) // Partial perms on mask \
and entry +            strEffective[1] = 'W';
+        if ( maskPerms & partialPerms & ACL_EXECUTE || // Partial perms on entry
+             maskPartialPerms & perms & ACL_EXECUTE || // Partial perms on mask
+             maskPartialPerms & partialPerms & ACL_EXECUTE ) // Partial perms on \
mask and entry +            strEffective[2] = 'X';
+*/
+    }
+    else
+    {
+        // No, the effective value are just the value in this entry
+        strEffective[0] = ( value & ACL_READ ) ? 'r' : '-';
+        strEffective[1] = ( value & ACL_WRITE ) ? 'w' : '-';
+        strEffective[2] = ( value & ACL_EXECUTE ) ? 'x' : '-';
+
+        /*
+        // What about any partial perms?
+        if ( partialPerms & ACL_READ )
+            strEffective[0] = 'R';
+        if ( partialPerms & ACL_WRITE )
+            strEffective[1] = 'W';
+        if ( partialPerms & ACL_EXECUTE )
+            strEffective[2] = 'X';
+            */
+    }
+    setText( 5, strEffective );
+}
+
+
+void KACLListViewItem::togglePerm( acl_perm_t perm )
+{
+    value ^= perm; // Toggle the perm
+    if ( type == KACLListView::Mask && !isDefault ) {
+        m_pACLListView->setMaskPermissions( value );
+    }
+    calcEffectiveRights();
+    updatePermPixmaps();
+/*
+    // If the perm is in the partial perms then remove it. i.e. Once
+    // a user changes a partial perm it then applies to all selected files.
+    if ( m_pEntry->m_partialPerms & perm )
+        m_pEntry->m_partialPerms ^= perm;
+
+    m_pEntry->setPartialEntry( false );
+    // Make sure that all entries have their effective rights calculated if
+    // we are changing the ACL_MASK entry.
+    if ( type == Mask )
+    {
+        m_pACLListView->setMaskPartialPermissions( m_pEntry->m_partialPerms );
+        m_pACLListView->setMaskPermissions( value );
+        m_pACLListView->calculateEffectiveRights();
+    }
+*/
+}
+
+
+
+EditACLEntryDialog::EditACLEntryDialog( KACLListView *listView, KACLListViewItem \
*item, +                                        const QStringList &users,
+                                        const QStringList &groups,
+                                        const QStringList &defaultUsers,
+                                        const QStringList &defaultGroups,
+                                        int allowedTypes, int allowedDefaultTypes,
+                                        bool allowDefaults )
+      : KDialogBase( listView, "edit_entry_dialog", true,
+              i18n( "Edit ACL entry" ), KDialogBase::Ok|KDialogBase::Cancel,
+              KDialogBase::Ok, false ), 
+        m_listView( listView ), m_item( item ), m_users( users ), m_groups( groups \
), +        m_defaultUsers( defaultUsers ), m_defaultGroups( defaultGroups ),
+        m_allowedTypes( allowedTypes ), m_allowedDefaultTypes( allowedDefaultTypes \
), +        m_defaultCB( 0 )
+{
+    QWidget *page = new QWidget(  this );
+    setMainWidget( page );
+    QVBoxLayout *mainLayout = new QVBoxLayout( page, marginHint(), spacingHint(), \
"mainLayout" ); +    m_buttonGroup = new QVButtonGroup( i18n("Entry type"), page, \
"bg" ); +
+    if ( allowDefaults ) {
+        m_defaultCB = new QCheckBox( i18n("Default for new files in this \
directory"), page, "defaultCB" ); +        mainLayout->addWidget( m_defaultCB );
+        connect( m_defaultCB, SIGNAL( toggled( bool ) ),
+                 this, SLOT( slotUpdateAllowedUsersAndGroups() ) );
+        connect( m_defaultCB, SIGNAL( toggled( bool ) ),
+                 this, SLOT( slotUpdateAllowedTypes() ) );
+
+    }
+
+    mainLayout->addWidget( m_buttonGroup );
+
+    QRadioButton *ownerType = new QRadioButton( i18n("Owner"), m_buttonGroup, \
"ownerType" ); +    m_buttonGroup->insert( ownerType, KACLListView::User );
+    QRadioButton *owningGroupType = new QRadioButton( i18n("Owning Group"), \
m_buttonGroup, "owningGroupType" ); +    m_buttonGroup->insert( owningGroupType, \
KACLListView::Group ); +    QRadioButton *othersType = new QRadioButton( \
i18n("Others"), m_buttonGroup, "othersType" ); +    m_buttonGroup->insert( \
othersType, KACLListView::Others ); +    QRadioButton *maskType = new QRadioButton( \
i18n("Mask"), m_buttonGroup, "maskType" ); +    m_buttonGroup->insert( maskType, \
KACLListView::Mask ); +    QRadioButton *namedUserType = new QRadioButton( \
i18n("Named User"), m_buttonGroup, "namesUserType" ); +    m_buttonGroup->insert( \
namedUserType, KACLListView::NamedUser ); +    QRadioButton *namedGroupType = new \
QRadioButton( i18n("Named Group"), m_buttonGroup, "namedGroupType" ); +    \
m_buttonGroup->insert( namedGroupType, KACLListView::NamedGroup ); +
+    connect( m_buttonGroup, SIGNAL( clicked( int ) ),
+             this, SLOT( slotSelectionChanged( int ) ) );
+
+    m_widgetStack = new QWidgetStack( page );
+    mainLayout->addWidget( m_widgetStack );
+
+    QHBox *usersBox = new QHBox( m_widgetStack );
+    m_widgetStack->addWidget( usersBox, KACLListView::NamedUser );
+
+    QHBox *groupsBox = new QHBox( m_widgetStack );
+    m_widgetStack->addWidget( groupsBox, KACLListView::NamedGroup );
+
+    QLabel *usersLabel = new QLabel( i18n( "User " ), usersBox );
+    m_usersCombo = new QComboBox( false, usersBox, "users" );
+    usersLabel->setBuddy( m_usersCombo );
+
+    QLabel *groupsLabel = new QLabel( i18n( "Group " ), groupsBox );
+    m_groupsCombo = new QComboBox( false, groupsBox, "groups" );
+    groupsLabel->setBuddy( m_groupsCombo );
+
+    if ( m_item ) {
+        m_buttonGroup->setButton( m_item->type );
+        if ( m_defaultCB )
+            m_defaultCB->setChecked( m_item->isDefault );
+        slotUpdateAllowedTypes();
+        slotSelectionChanged( m_item->type );
+        slotUpdateAllowedUsersAndGroups();
+        if ( m_item->type == KACLListView::NamedUser ) {
+            m_usersCombo->setCurrentText( m_item->qualifier );
+        } else if ( m_item->type == KACLListView::NamedGroup ) {
+            m_groupsCombo->setCurrentText( m_item->qualifier );
+        }
+    } else {
+        // new entry, preselect "named user", arguably the most common one
+        m_buttonGroup->setButton( KACLListView::NamedUser );
+        slotUpdateAllowedTypes();
+        slotSelectionChanged( KACLListView::NamedUser );
+        slotUpdateAllowedUsersAndGroups();
+    }
+    incInitialSize(  QSize( 100, 0 ) );
+}
+
+void EditACLEntryDialog::slotUpdateAllowedTypes()
+{
+    int allowedTypes = m_allowedTypes;
+    if ( m_defaultCB && m_defaultCB->isChecked() ) {
+        allowedTypes = m_allowedDefaultTypes;
+    }
+    for ( int i=1; i < KACLListView::AllTypes; i=i*2 ) {
+        if ( allowedTypes & i )
+            m_buttonGroup->find( i )->show();
+        else
+            m_buttonGroup->find( i )->hide();
+    }
+}
+
+void EditACLEntryDialog::slotUpdateAllowedUsersAndGroups()
+{
+    const QString oldUser = m_usersCombo->currentText();
+    const QString oldGroup = m_groupsCombo->currentText();
+    m_usersCombo->clear();
+    m_groupsCombo->clear();
+    if ( m_defaultCB && m_defaultCB->isChecked() ) {
+        m_usersCombo->insertStringList( m_defaultUsers );
+        if ( m_defaultUsers.find( oldUser ) != m_defaultUsers.end() )
+            m_usersCombo->setCurrentText( oldUser );
+        m_groupsCombo->insertStringList( m_defaultGroups );
+        if ( m_defaultGroups.find( oldGroup ) != m_defaultGroups.end() )
+            m_groupsCombo->setCurrentText( oldGroup );
+    } else {
+        m_usersCombo->insertStringList( m_users );
+        if ( m_users.find( oldUser ) != m_users.end() )
+            m_usersCombo->setCurrentText( oldUser );
+        m_groupsCombo->insertStringList( m_groups );
+        if ( m_groups.find( oldGroup ) != m_groups.end() )
+            m_groupsCombo->setCurrentText( oldGroup );
+    }
+}
+void EditACLEntryDialog::slotOk()
+{
+    KACLListView::EntryType type = static_cast<KACLListView::EntryType>( \
m_buttonGroup->selectedId() ); +
+    QString qualifier;
+    if ( type == KACLListView::NamedUser )
+      qualifier = m_usersCombo->currentText();
+    if ( type == KACLListView::NamedGroup )
+      qualifier = m_groupsCombo->currentText();
+
+    if ( !m_item ) {
+        m_item = new KACLListViewItem( m_listView, type, 0, false, qualifier );
+    } else {
+        m_item->type = type;
+        m_item->qualifier = qualifier;
+    }
+    if ( m_defaultCB )
+        m_item->isDefault = m_defaultCB->isChecked();
+    m_item->repaint();
+
+    KDialogBase::slotOk();
+}
+
+void EditACLEntryDialog::slotSelectionChanged( int id )
+{
+    switch ( id ) {
+        case KACLListView::User:
+        case KACLListView::Group:
+        case KACLListView::Others:
+        case KACLListView::Mask:
+            m_widgetStack->setEnabled( false );
+            break;
+        case KACLListView::NamedUser:
+            m_widgetStack->setEnabled( true );
+            m_widgetStack->raiseWidget( KACLListView::NamedUser );
+            break;
+        case KACLListView::NamedGroup:
+            m_widgetStack->setEnabled( true );
+            m_widgetStack->raiseWidget( KACLListView::NamedGroup );
+            break;
+        default:
+            break;
+    }
+}
+
+
+KACLListView::KACLListView( QWidget* parent, const char* name )
+ : KListView( parent, name ),
+   m_hasMask( false ), m_allowDefaults( false )
+{
+    // Add the columns
+    addColumn( i18n( "Type" ) );
+    addColumn( i18n( "Name" ) );
+    addColumn( i18n( "r" ) );
+    addColumn( i18n( "w" ) );
+    addColumn( i18n( "x" ) );
+    addColumn( i18n( "Effective" ) );
+
+    header()->setClickEnabled( false );
+
+    // Load the avatars
+    if ( !QPixmapCache::find( s_itemAttributes[OWNER_IDX].pixmap ) ) {
+        for ( int i=0; i < LAST_IDX; i++ ) {
+            QPixmapCache::insert( s_itemAttributes[i].pixmap,
+                    qembed_findImage( s_itemAttributes[i].pixmap ) );
+        }
+        QPixmapCache::insert( "yes", qembed_findImage( "yes" ) );
+        QPixmapCache::insert( "yespartial", qembed_findImage( "yespartial" ) );
+    }
+
+    setSelectionMode( QListView::Extended );
+
+    // fill the lists of all legal users and groups
+    struct passwd *user = 0;
+    setpwent();
+    while ( ( user = getpwent() ) != 0 ) {
+       m_allUsers << QString::fromLatin1( user->pw_name );
+    }
+    endpwent();
+
+    struct group *gr = 0;
+    setgrent();
+    while ( ( gr = getgrent() ) != 0 ) {
+       m_allGroups << QString::fromLatin1( gr->gr_name );
+    }
+    endgrent();
+    m_allUsers.sort();
+    m_allGroups.sort();
+}
+
+
+KACLListView::~KACLListView()
+{
+
+}
+
+QStringList KACLListView::allowedUsers( bool defaults, KACLListViewItem *allowedItem \
) +{
+    QStringList allowedUsers = m_allUsers;
+    QListViewItemIterator it( this );
+    while ( it.current() ) {
+        const KACLListViewItem *item = static_cast<const KACLListViewItem*>( *it );
+        ++it;
+        if ( !item->type == NamedUser || item->isDefault != defaults ) continue;
+        if ( allowedItem && item == allowedItem && allowedItem->isDefault == \
defaults ) continue; +        allowedUsers.remove( item->qualifier );
+    }
+    return allowedUsers;
+}
+
+QStringList KACLListView::allowedGroups( bool defaults, KACLListViewItem \
*allowedItem ) +{
+    QStringList allowedGroups = m_allGroups;
+    QListViewItemIterator it( this );
+    while ( it.current() ) {
+        const KACLListViewItem *item = static_cast<const KACLListViewItem*>( *it );
+        ++it;
+        if ( !item->type == NamedGroup || item->isDefault != defaults ) continue;
+        if ( allowedItem && item == allowedItem && allowedItem->isDefault == \
defaults ) continue; +        allowedGroups.remove( item->qualifier );
+    }
+    return allowedGroups;
+}
+
+void KACLListView::fillItemsFromACL( const KACL &pACL, bool defaults )
+{
+    // clear out old entries of that ilk
+    QListViewItemIterator it( this );
+    while ( KACLListViewItem *item = static_cast<KACLListViewItem*>( it.current() ) \
) { +        ++it;
+        if ( item->isDefault == defaults )
+            delete item;
+    }
+    KACLListViewItem *item =
+        new KACLListViewItem( this, User, pACL.ownerPermissions(), defaults );
+
+    item = new KACLListViewItem( this, Group, pACL.owningGroupPermissions(), \
defaults ); +
+    item = new KACLListViewItem( this, Others, pACL.othersPermissions(), defaults );
+
+    bool hasMask = false;
+    unsigned short mask = pACL.maskPermissions( hasMask );
+    if ( hasMask ) {
+        item = new KACLListViewItem( this, Mask, mask, defaults );
+    }
+
+    // read all named user entries
+    const ACLUserPermissionsList &userList =  pACL.allUserPermissions();
+    ACLUserPermissionsConstIterator itu = userList.begin();
+    while ( itu != userList.end() ) {
+        new KACLListViewItem( this, NamedUser, (*itu).second, defaults, (*itu).first \
); +        ++itu;
+    }
+
+    // and now all named groups
+    const ACLUserPermissionsList &groupList =  pACL.allGroupPermissions();
+    ACLUserPermissionsConstIterator itg = groupList.begin();
+    while ( itg != groupList.end() ) {
+        new KACLListViewItem( this, NamedGroup, (*itg).second, defaults, \
(*itg).first ); +        ++itg;
+    }
+}
+
+void KACLListView::setACL( const KACL &acl )
+{
+    if ( !acl.isValid() ) return;
+    // Remove any entries left over from displaying a previous ACL
+    m_ACL = acl;
+    fillItemsFromACL( m_ACL );
+
+    m_mask = acl.maskPermissions( m_hasMask );
+    calculateEffectiveRights();
+}
+
+void KACLListView::setDefaultACL( const KACL &acl )
+{
+    if ( !acl.isValid() ) return;
+    m_defaultACL = acl;
+    fillItemsFromACL( m_defaultACL, true );
+    calculateEffectiveRights();
+}
+
+KACL KACLListView::itemsToACL( bool defaults ) const
+{
+    KACL newACL( 0 );
+    bool atLeastOneEntry = false;
+    ACLUserPermissionsList users;
+    ACLGroupPermissionsList groups;
+    QListViewItemIterator it( const_cast<KACLListView*>( this ) );
+    while ( QListViewItem* qlvi = it.current() ) {
+        ++it;
+        const KACLListViewItem* item = static_cast<KACLListViewItem*>( qlvi );
+        if ( item->isDefault != defaults ) continue;
+        atLeastOneEntry = true;
+        switch ( item->type ) {
+            case User:
+                newACL.setOwnerPermissions( item->value );
+                break;
+            case Group:
+                newACL.setOwningGroupPermissions( item->value );
+                break;
+            case Others:
+                newACL.setOthersPermissions( item->value );
+                break;
+            case Mask:
+                newACL.setMaskPermissions( item->value );
+                break;
+            case NamedUser:
+                users.append( qMakePair( item->text( 1 ), item->value ) );
+                break;
+            case NamedGroup:
+                groups.append( qMakePair( item->text( 1 ), item->value ) );
+                break;
+            default:
+                break;
+        }
+    }
+    if ( atLeastOneEntry ) {
+        newACL.setAllUserPermissions( users );
+        newACL.setAllGroupPermissions( groups );
+        if ( newACL.isValid() )
+            return newACL;
+    }
+    return KACL();
+}
+
+KACL KACLListView::getACL()
+{
+    return itemsToACL( false );
+}
+
+
+KACL KACLListView::getDefaultACL()
+{
+    return itemsToACL( true );
+}
+
+void KACLListView::contentsMousePressEvent( QMouseEvent * e )
+{
+    QListViewItem *clickedItem = itemAt( contentsToViewport(  e->pos() ) );
+    if ( !clickedItem ) return;
+    // if the click is on an as yet unselected item, select it first
+    if ( !clickedItem->isSelected() )
+        KListView::contentsMousePressEvent( e );
+
+    if ( !currentItem() ) return;
+    int column = header()->sectionAt( e->x() );
+    acl_perm_t perm;
+    switch ( column )
+    {
+        case 2:
+            perm = ACL_READ;
+            break;
+        case 3:
+            perm = ACL_WRITE;
+            break;
+        case 4:
+            perm = ACL_EXECUTE;
+            break;
+        default:
+            return KListView::contentsMousePressEvent( e );
+    }
+    KACLListViewItem* referenceItem = static_cast<KACLListViewItem*>( clickedItem );
+    unsigned short referenceHadItSet = referenceItem->value & perm;
+    QListViewItemIterator it( this );
+    while ( KACLListViewItem* item = static_cast<KACLListViewItem*>( it.current() ) \
) { +        ++it;
+        if ( !item->isSelected() ) continue;
+        // toggle those with the same value as the clicked item, leave the others
+        if ( referenceHadItSet == ( item->value & perm ) )
+            item->togglePerm( perm );
+    }
+}
+
+void KACLListView::entryClicked( QListViewItem* pItem, const QPoint& /*pt*/, int col \
) +{
+    if ( !pItem ) return;
+
+    QListViewItemIterator it( this );
+    while ( KACLListViewItem* item = static_cast<KACLListViewItem*>( it.current() ) \
) { +        ++it;
+        if ( !item->isSelected() ) continue;
+        switch ( col )
+        {
+            case 2:
+                item->togglePerm( ACL_READ );
+                break;
+            case 3:
+                item->togglePerm( ACL_WRITE );
+                break;
+            case 4:
+                item->togglePerm( ACL_EXECUTE );
+                break;
+
+            default:
+                ; // Do nothing
+        }
+    }
+    /*
+    // Has the user changed one of the required entries in a default ACL?
+    if ( m_pACL->aclType() == ACL_TYPE_DEFAULT &&
+    ( col == 2 || col == 3 || col == 4 ) &&
+    ( pACLItem->entryType() == ACL_USER_OBJ ||
+    pACLItem->entryType() == ACL_GROUP_OBJ ||
+    pACLItem->entryType() == ACL_OTHER ) )
+    {
+    // Mark the required entries as no longer being partial entries.
+    // That is, they will get applied to all selected directories.
+    KACLListViewItem* pUserObj = findACLEntryByType( this, ACL_USER_OBJ );
+    pUserObj->entry()->setPartialEntry( false );
+
+    KACLListViewItem* pGroupObj = findACLEntryByType( this, ACL_GROUP_OBJ );
+    pGroupObj->entry()->setPartialEntry( false );
+
+    KACLListViewItem* pOther = findACLEntryByType( this, ACL_OTHER );
+    pOther->entry()->setPartialEntry( false );
+
+    update();
+    }
+     */
+}
+
+
+void KACLListView::calculateEffectiveRights()
+{
+    QListViewItemIterator it( this );
+    KACLListViewItem* pItem;
+    while ( ( pItem = dynamic_cast<KACLListViewItem*>( it.current() ) ) != 0 )
+    {
+        ++it;
+        pItem->calcEffectiveRights();
+    }
+}
+
+
+unsigned short KACLListView::maskPermissions() const
+{
+  return m_mask;
+}
+
+
+void KACLListView::setMaskPermissions( unsigned short maskPerms )
+{
+    m_mask = maskPerms;
+    calculateEffectiveRights();
+}
+
+
+acl_perm_t KACLListView::maskPartialPermissions() const
+{
+  //  return m_pMaskEntry->m_partialPerms;
+  return 0;
+}
+
+
+void KACLListView::setMaskPartialPermissions( acl_perm_t /*maskPartialPerms*/ )
+{
+    //m_pMaskEntry->m_partialPerms = maskPartialPerms;
+    calculateEffectiveRights();
+}
+
+bool KACLListView::hasDefaultEntries() const
+{
+    QListViewItemIterator it( const_cast<KACLListView*>( this ) );
+    while ( it.current() ) {
+        const KACLListViewItem *item = static_cast<const KACLListViewItem*>( \
it.current() ); +        ++it;
+        if ( item->isDefault ) return true;
+    }
+    return false;
+}
+
+const KACLListViewItem* KACLListView::findDefaultItemByType( EntryType type ) const
+{
+    return findItemByType( type, true );
+}
+
+const KACLListViewItem* KACLListView::findItemByType( EntryType type, bool defaults \
) const +{
+    QListViewItemIterator it( const_cast<KACLListView*>( this ) );
+    while ( it.current() ) {
+        const KACLListViewItem *item = static_cast<const KACLListViewItem*>( \
it.current() ); +        ++it;
+        if ( item->isDefault == defaults && item->type == type ) {
+            return item;
+        }
+    }
+    return 0;
+}
+
+
+unsigned short KACLListView::calculateMaskValue( bool defaults ) const
+{
+    // KACL auto-adds the relevant maks entries, so we can simply query
+    bool dummy;
+    return itemsToACL( defaults ).maskPermissions( dummy );
+}
+
+void KACLListView::slotAddEntry()
+{
+    int allowedTypes = NamedUser | NamedGroup;
+    if ( !m_hasMask )
+        allowedTypes |= Mask;
+    int allowedDefaultTypes = NamedUser | NamedGroup;
+    if ( !findDefaultItemByType( Mask ) )
+        allowedDefaultTypes |=  Mask;
+    if ( !hasDefaultEntries() )
+        allowedDefaultTypes |= User | Group;
+    EditACLEntryDialog dlg( this, 0,
+                            allowedUsers( false ), allowedGroups( false ),
+                            allowedUsers( true ), allowedGroups( true ),
+                            allowedTypes, allowedDefaultTypes, m_allowDefaults );
+    dlg.exec();
+    KACLListViewItem *item = dlg.item();
+    if ( !item ) return; // canceled
+    if ( item->type == Mask && !item->isDefault ) {
+        m_hasMask = true;
+        m_mask = item->value;
+    }
+    if ( item->isDefault && !hasDefaultEntries() ) {
+        // first default entry, fill in what is needed
+        if ( item->type != User ) {
+            unsigned short v = findDefaultItemByType( User )->value;
+            new KACLListViewItem( this, User, v, true );
+        }
+        if ( item->type != Group ) {
+            unsigned short v = findDefaultItemByType( Group )->value;
+            new KACLListViewItem( this, Group, v, true );
+        }
+        if ( item->type != Others ) {
+            unsigned short v = findDefaultItemByType( Others )->value;
+            new KACLListViewItem( this, Others, v, true );
+        }
+    }
+    const KACLListViewItem *defaultMaskItem = findDefaultItemByType( Mask );
+    if ( item->isDefault && !defaultMaskItem ) {
+        unsigned short v = calculateMaskValue( true );
+        new KACLListViewItem( this, Mask, v, true );
+    }
+    if ( !item->isDefault && !m_hasMask &&
+            ( item->type == Group
+              || item->type == NamedUser
+              || item->type == NamedGroup ) ) {
+        // auto-add a mask entry
+        unsigned short v = calculateMaskValue( false );
+        new KACLListViewItem( this, Mask, v, false );
+    }
+    calculateEffectiveRights();
+    sort();
+    setCurrentItem( item );
+    // QListView doesn't seem to emit, in this case, and we need to update 
+    // the buttons...
+    if ( childCount() == 1 ) 
+        emit currentChanged( item );
+}
+
+void KACLListView::slotEditEntry()
+{
+    QListViewItem * current = currentItem();
+    if ( !current ) return;
+    KACLListViewItem *item = static_cast<KACLListViewItem*>( current );
+    int allowedTypes = item->type | NamedUser | NamedGroup;
+    bool itemWasMask = item->type == Mask;
+    if ( !m_hasMask || itemWasMask )
+        allowedTypes |= Mask;
+    int allowedDefaultTypes = item->type | NamedUser | NamedGroup;
+    if ( !findDefaultItemByType( Mask ) )
+        allowedDefaultTypes |=  Mask;
+    if ( !hasDefaultEntries() )
+        allowedDefaultTypes |= User | Group;
+
+    EditACLEntryDialog dlg( this, item,
+                            allowedUsers( false, item ), allowedGroups( false, item \
), +                            allowedUsers( true, item ), allowedGroups( true, item \
), +                            allowedTypes, allowedDefaultTypes, m_allowDefaults );
+    dlg.exec();
+    if ( itemWasMask && item->type != Mask ) {
+        m_hasMask = false;
+        m_mask = 0;
+    }
+    if ( !itemWasMask && item->type == Mask ) {
+        m_mask = item->value;
+        m_hasMask = true;
+    }
+    calculateEffectiveRights();
+    sort();
+}
+
+void KACLListView::slotRemoveEntry()
+{
+    bool needsMask = findItemByType( NamedUser ) || findItemByType( NamedGroup );
+    bool needsDefaultMask = findDefaultItemByType( NamedUser ) || \
findDefaultItemByType( NamedGroup ); +    QListViewItemIterator it( this, \
QListViewItemIterator::Selected ); +    while ( it.current() ) {
+        KACLListViewItem *item = static_cast<KACLListViewItem*>( it.current() );
+        ++it;
+        /* First check if it's a mask entry and if so, make sure that there is
+         * either no name user or group entry, which means the mask can be 
+         * removed, or don't remove it, but reset it. That is allowed. */
+        if ( item->type == Mask ) {
+            if ( !item->isDefault && !needsMask ) {
+                m_hasMask= false;
+                m_mask = 0;
+                delete item;
+            } else if ( item->isDefault && !needsDefaultMask ) {
+                delete item;
+            } else {
+                item->value = 0;
+                item->repaint();
+            }
+            if ( !item->isDefault )
+                calculateEffectiveRights();
+        } else {
+            // for the base permissions, disable them, which is what libacl does
+            if ( !item->isDefault &&
+                    ( item->type == User
+                      || item->type == Group
+                      || item->type == Others ) ) {
+                item->value = 0;
+                item->repaint();
+            } else {
+                delete item;
+            }
+        }
+    }
+}
+
+#include "kacleditwidget.moc"
+// vim:set ts=8 sw=4:
Index: kfile/Makefile.am
===================================================================
--- kfile/Makefile.am	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ kfile/Makefile.am	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -39,7 +39,7 @@ include_HEADERS = kfiledialog.h kencodin
 
 noinst_HEADERS = config-kfile.h krecentdirs.h kmetaprops.h \
 	kfilebookmarkhandler.h kfilemetainfowidget.h kopenwith_p.h \
-	kfilespeedbar.h kpreviewprops.h
+	kfilespeedbar.h kpreviewprops.h kacleditwidget.h kacleditwidget_p.h images.h
 
 libkfile_la_SOURCES = \
 	kfilefiltercombo.cpp \
@@ -56,7 +56,7 @@ libkfile_la_SOURCES = \
 	knotifydialog.cpp kfilespeedbar.cpp kpreviewwidgetbase.cpp \
 	kfilemetapreview.cpp kpropertiesdesktopbase.ui \
 	kpropertiesdesktopadvbase.ui kpropertiesmimetypebase.ui \
-	kencodingfiledialog.cpp
+	kencodingfiledialog.cpp kacleditwidget.cpp
 
 libkfile_la_COMPILE_FIRST = $(srcdir)/../kio/kdirnotify_stub.h
 
Index: tests/kacltest.h
===================================================================
--- tests/kacltest.h	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ tests/kacltest.h	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -0,0 +1,52 @@
+/* This file is part of the KDE project
+   Copyright (C) 2005 Till Adam <adam@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KACLTEST_H
+#define KACLTEST_H
+
+#include <qobject.h>
+#include <kacl.h>
+
+class KACLTest
+{
+public:
+    KACLTest();
+    void setup();
+    void runAll();
+    void cleanup();
+
+    void testAsString();
+    void testSetACL();
+    void testGetOwnerPermissions();
+    void testGetOwningGroupPermissions();
+    void testGetOthersPermissions();
+    void testGetMaskPermissions();
+    void testGetAllUserPermissions();
+    void testGetAllGroupsPermissions();
+    void testIsExtended();
+    void testOperators();
+    void testSettingBasic();
+    void testSettingExtended();
+    void testSettingErrorHandling();
+
+private:
+    KACL m_acl;
+};
+
+#endif
Index: tests/kacltest.cpp
===================================================================
--- tests/kacltest.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 0)
+++ tests/kacltest.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -0,0 +1,280 @@
+/* This file is part of the KDE project
+   Copyright (C) 2005 Till Adam <adam@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+
+#include "kacltest.h"
+
+#include <config.h>
+
+#include <kacl.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+
+#include <qfileinfo.h>
+#include <qeventloop.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <qdir.h>
+
+// The code comes partly from kdebase/kioslave/trash/testtrash.cpp
+
+static bool check(const QString& txt, QString a, QString b)
+{
+    if (a.isEmpty())
+        a = QString::null;
+    if (b.isEmpty())
+        b = QString::null;
+    if (a == b) {
+        kdDebug() << txt << " : checking '" << a << "' against expected value '" << \
b << "'... " << "ok" << endl; +    }
+    else {
+        kdDebug() << txt << " : checking '" << a << "' against expected value '" << \
b << "'... " << "KO !" << endl; +        exit(1);
+    }
+    return true;
+}
+
+template<typename T> 
+static bool check(const QString& txt, T a, T b)
+{
+    if (a == b) {
+        kdDebug() << txt << " : checking '" << a << "' against expected value '" << \
b << "'... " << "ok" << endl; +    }
+    else {
+        kdDebug() << txt << " : checking '" << a << "' against expected value '" << \
b << "'... " << "KO !" << endl; +        exit(1);
+    }
+    return true;
+}
+
+int main(int argc, char *argv[])
+{
+    KApplication::disableAutoDcopRegistration();
+    KCmdLineArgs::init(argc,argv,"kacltest", 0, 0, 0, 0);
+    KApplication app;
+
+    KACLTest test;
+    test.setup();
+    test.runAll();
+    test.cleanup();
+    kdDebug() << "All tests OK." << endl;
+    return 0; // success. The exit(1) in check() is what happens in case of failure.
+}
+
+static const QString s_testACL( \
"user::rw-\nuser:bin:rwx\ngroup::rw-\nmask::rwx\nother::r--\n" ); +static const \
QString s_testACL2( "user::rwx\nuser:bin:rwx\ngroup::rw-\ngroup:users:r--\ngroup:audio:--x\nmask::r-x\nother::r--\n" \
); +static const QString s_testACLEffective("user::rwx\nuser:bin:rwx    \
#effective:r-x\ngroup::rw-      \
#effective:r--\ngroup:audio:--x\ngroup:users:r--\nmask::r-x\nother::r--\n"); +
+KACLTest::KACLTest()
+:m_acl( s_testACL )
+{
+}
+
+void KACLTest::setup()
+{
+}
+
+void KACLTest::runAll()
+{
+  testAsString();
+  testGetOwnerPermissions();
+  testGetOwningGroupPermissions();
+  testGetOthersPermissions();
+  
+  testGetMaskPermissions();
+  testGetAllUserPermissions();
+
+  testIsExtended();
+
+  // from here on we operate with the second test string
+  testSetACL();
+  testGetAllGroupsPermissions();
+
+  testOperators();
+  testSettingBasic();
+  testSettingExtended();
+  testSettingErrorHandling();
+}
+
+void KACLTest::cleanup()
+{
+}
+
+void KACLTest::testAsString()
+{
+  check( "asString: ", s_testACL, m_acl.asString() );
+}
+
+void KACLTest::testSetACL()
+{
+  m_acl.setACL( s_testACL2 );
+  check( "setACL: ", s_testACLEffective.simplifyWhiteSpace(), \
m_acl.asString().simplifyWhiteSpace() ); +}
+
+void KACLTest::testGetOwnerPermissions()
+{
+  check( "Owner permissions: ", QString::number( m_acl.ownerPermissions() ), "6" );
+}
+
+void KACLTest::testGetOwningGroupPermissions()
+{
+  check( "Owning group permissions: ", QString::number( \
m_acl.owningGroupPermissions() ), "6" ); +}
+
+void KACLTest::testGetOthersPermissions()
+{
+  check( "Others permissions: ", QString::number( m_acl.othersPermissions() ), "4" \
); +}
+
+void KACLTest::testGetMaskPermissions()
+{
+  bool exists = false;
+  unsigned short mask = m_acl.maskPermissions( exists );
+  check( "Mask permissions: ", QString::number( mask ), "7" );
+  check( "Mask permissions: ", exists, true );
+}
+
+void KACLTest::testGetAllUserPermissions()
+{
+  ACLUserPermissionsList list = m_acl.allUserPermissions();
+  ACLUserPermissionsConstIterator it = list.begin();
+  QString name;
+  unsigned short permissions;
+  int count = 0;
+  while ( it != list.end() ) {
+    name = ( *it ).first;
+    permissions = ( *it ).second;
+    ++it;
+    ++count;
+  }
+  check( "All users count: ", QString::number( count ), "1" );
+  check( "All users name: ", name, "bin" );
+  check( "All users permissions: ", QString::number( permissions ), "7" );
+}
+
+void KACLTest::testGetAllGroupsPermissions()
+{
+  ACLGroupPermissionsList list = m_acl.allGroupPermissions();
+  ACLGroupPermissionsConstIterator it = list.begin();
+  QString name;
+  unsigned short permissions;
+  int count = 0;
+  while ( it != list.end() ) {
+    name = ( *it ).first;
+    permissions = ( *it ).second;
+    // setACL sorts them alphabetically ...
+    if ( count == 0 ) {
+      check( "All groups name: ", name, "audio" );
+      check( "All groups permissions: ", QString::number( permissions ), "1" );
+    } else if ( count == 1 ) {
+      check( "All groups name: ", name, "users" );
+      check( "All groups permissions: ", QString::number( permissions ), "4" );
+    }
+    ++it;
+    ++count;
+  }
+  check( "All users count: ", QString::number( count ), "2" );
+}
+
+void KACLTest::testIsExtended()
+{
+  KACL dukeOfMonmoth( s_testACL );
+  check( "isExtended on an extended one: ", dukeOfMonmoth.isExtended(), true );
+  KACL earlOfUpnor( "user::r--\ngroup::r--\nother::r--\n" );
+  check( "isExtended on a not extended one: ", earlOfUpnor.isExtended(), false );
+}
+
+void KACLTest::testOperators()
+{
+  KACL dukeOfMonmoth( s_testACL );
+  KACL JamesScott( s_testACL );
+  KACL earlOfUpnor( s_testACL2 );
+  check( "operator== on different ones: ", dukeOfMonmoth == earlOfUpnor, false );
+  check( "operator== on identical ones: ", dukeOfMonmoth == JamesScott, true );
+  check( "operator!= on diffenrent ones: ", dukeOfMonmoth != earlOfUpnor, true );
+  check( "operator!=: on identical ones: ", dukeOfMonmoth != JamesScott, false );
+}
+
+void KACLTest::testSettingBasic()
+{
+  KACL CharlesII( s_testACL );
+  CharlesII.setOwnerPermissions( 7 ); // clearly
+  CharlesII.setOwningGroupPermissions( 0 );
+  CharlesII.setOthersPermissions( 0 );
+  check( "setOwnerPermissions: ", QString::number( CharlesII.ownerPermissions() \
),"7" ); +  check( "setOwningGroupPermissions: ", QString::number( \
CharlesII.owningGroupPermissions() ),"0" ); +  check( "setOthersPermissions: ", \
QString::number( CharlesII.othersPermissions() ),"0" ); +}
+
+void KACLTest::testSettingExtended()
+{
+  KACL CharlesII( s_testACL );
+  CharlesII.setMaskPermissions( 7 ); // clearly
+  bool dummy = false;
+  check( "setMaskPermissions: ", QString::number( CharlesII.maskPermissions( dummy ) \
),"7" ); +
+  const QString expected( \
"user::rw-\nuser:root:rwx\nuser:bin:r--\ngroup::rw-\nmask::rwx\nother::r--\n" ); +  
+  ACLUserPermissionsList users;
+  ACLUserPermissions user = qMakePair( QString( "root" ), ( unsigned short )7 );
+  users.append( user );
+  user = qMakePair( QString( "bin" ), ( unsigned short )4 );
+  users.append( user );
+  CharlesII.setAllUserPermissions( users );
+  check( "setAllUserPermissions: ", CharlesII.asString(), expected );
+
+  CharlesII.setACL( s_testACL ); // reset
+  // it already has an entry for bin, let's change it
+  CharlesII.setNamedUserPermissions( QString("bin"), 4 );
+  CharlesII.setNamedUserPermissions( QString( "root" ), 7 );
+  check( "setNamedUserPermissions: ", CharlesII.asString(), expected );
+
+  // groups, all and named
+  
+  const QString expected2( \
"user::rw-\nuser:bin:rwx\ngroup::rw-\ngroup:audio:-wx\ngroup:users:r--\nmask::rwx\nother::r--\n" \
); +  CharlesII.setACL( s_testACL ); // reset
+  ACLGroupPermissionsList groups;
+  ACLGroupPermissions group = qMakePair( QString( "audio" ), ( unsigned short )3 );
+  groups.append( group );
+  group = qMakePair( QString( "users" ), ( unsigned short )4 );
+  groups.append( group );
+  CharlesII.setAllGroupPermissions( groups );
+  check( "setAllGroupPermissions: ", CharlesII.asString(), expected2 );
+
+  CharlesII.setACL( s_testACL ); // reset
+  CharlesII.setNamedGroupPermissions( QString( "audio" ), 3 );
+  CharlesII.setNamedGroupPermissions( QString( "users" ), 4 );
+  check( "setNamedGroupPermissions: ", CharlesII.asString(), expected2 );
+}
+
+void KACLTest::testSettingErrorHandling()
+{
+  KACL foo( s_testACL );
+  bool v = foo.setNamedGroupPermissions( "audio", 7 ); // existing group
+  check( "Existing group: ", v, true );
+  v = foo.setNamedGroupPermissions( "jongel", 7 ); // non-existing group
+  check( "Non-existing group: ", v, false );
+
+  v = foo.setNamedUserPermissions( "bin", 7 ); // existing user
+  check( "Existing user: ", v, true );
+  v = foo.setNamedUserPermissions( "jongel", 7 ); // non-existing user
+  check( "Non-existing user: ", v, false );
+}
Index: tests/kioslavetest.cpp
===================================================================
--- tests/kioslavetest.cpp	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ tests/kioslavetest.cpp	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -21,6 +21,7 @@
 #include <kstatusbar.h>
 #include <kio/job.h>
 #include <kio/scheduler.h>
+#include <kacl.h>
 #include <kprotocolinfo.h>
 
 #include "kioslavetest.h"
@@ -346,6 +347,16 @@ void KioslaveTest::slotSlaveError()
    slave = 0;
 }
 
+static void printACL( const QString& acl )
+{
+  KACL kacl( acl );
+  kdDebug() << "According to KACL: " << endl << kacl.asString() << endl;
+  kdDebug() << "Owner: " << kacl.ownerPermissions() << endl;
+  kdDebug() << "Owning group: " << kacl.owningGroupPermissions() << endl;
+  kdDebug() << "Others: " << kacl.othersPermissions() << endl;
+}
+
+
 void KioslaveTest::printUDSEntry( const KIO::UDSEntry & entry )
 {
     KIO::UDSEntry::ConstIterator it = entry.begin();
@@ -359,7 +370,19 @@ void KioslaveTest::printUDSEntry( const 
                 }
                 break;
             case KIO::UDS_ACCESS:
-                kdDebug() << "Access permissions : " << (mode_t)((*it).m_long) << \
endl; +                kdDebug() << "Access permissions : " << (*it).m_long << endl;
+                break;
+            case KIO::UDS_EXTENDED_ACL:
+                if( (*it).m_long == 1 )
+                  kdDebug() << "Has extended ACL information." << endl;
+                break;
+            case KIO::UDS_ACL_STRING:
+                kdDebug() << "ACL: " << ( (*it).m_str.ascii() ) << endl;
+                printACL( (*it).m_str );
+                break;
+            case KIO::UDS_DEFAULT_ACL_STRING:
+                kdDebug() << "Default ACL: " << ( (*it).m_str.ascii() ) << endl;
+                printACL( (*it).m_str );
                 break;
             case KIO::UDS_USER:
                 kdDebug() << "User : " << ((*it).m_str.ascii() ) << endl;
Index: tests/Makefile.am
===================================================================
--- tests/Makefile.am	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ tests/Makefile.am	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -27,7 +27,7 @@ check_PROGRAMS = ksycocatest getalltest 
                  previewtest kionetrctest kdcopcheck metatest \
 		 kmimefromext kpropsdlgtest kmfitest dataprotocoltest \
 		 kprotocolinfotest ksycocaupdatetest netaccesstest jobtest \
-		 kurlcompletiontest kmimetypetest
+		 kurlcompletiontest kmimetypetest kacltest
 
 # Unfortunately some tests depend on the network settings, it seems
 #check: kurifiltertest
@@ -72,6 +72,7 @@ netaccesstest_SOURCES = netaccesstest.cp
 jobtest_SOURCES = jobtest.cpp
 kurlcompletiontest_SOURCES = kurlcompletiontest.cpp
 kmimetypetest_SOURCES = kmimetypetest.cpp
+kacltest_SOURCES = kacltest.cpp
 
 # kfile meta stuff. Comment this in, if you want a small
 # metadata plugin test and "make install".
Index: Makefile.am
===================================================================
--- Makefile.am	(.../KDE/3.5/kdelibs/kio)	(revision 443749)
+++ Makefile.am	(.../work/posix-acl-support/kdelibs/kio)	(revision 443749)
@@ -32,7 +32,7 @@ libkio_la_LIBADD = kssl/libkssl.la kio/l
 	kio/libksycoca.la bookmarks/libkbookmarks.la kfile/libkfile.la \
 	../kdeui/libkdeui.la ../kdesu/libkdesu.la \
 	../kwallet/client/libkwalletclient.la \
-	$(LIBZ) $(LIBFAM) $(LIBVOLMGT)
+	$(LIBZ) $(LIBFAM) $(LIBVOLMGT) $(ACL_LIBS)
 
 kde_mime_DATA = magic
 kde_servicetypes_DATA = application.desktop kurifilterplugin.desktop \


[Attachment #7 (application/pgp-signature)]

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic