• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIOSlave

file_unix.cpp

Go to the documentation of this file.
00001 /*
00002    Copyright (C) 2000-2002 Stephan Kulow <coolo@kde.org>
00003    Copyright (C) 2000-2002 David Faure <faure@kde.org>
00004    Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org>
00005    Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
00006    Copyright (C) 2007 Thiago Macieira <thiago@kde.org>
00007    Copyright (C) 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License (LGPL) as published by the Free Software Foundation;
00012    either version 2 of the License, or (at your option) any later
00013    version.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018    Library General Public License for more details.
00019 
00020    You should have received a copy of the GNU Library General Public License
00021    along with this library; see the file COPYING.LIB.  If not, write to
00022    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023    Boston, MA 02110-1301, USA.
00024 */
00025 
00026 #define QT_NO_CAST_FROM_ASCII
00027 
00028 #include "file.h"
00029 
00030 #include <config.h>
00031 
00032 #include <QtCore/QFile>
00033 
00034 #include <kde_file.h>
00035 #include <kdebug.h>
00036 #include <kconfiggroup.h>
00037 #include <kmountpoint.h>
00038 
00039 #include <dirent.h>
00040 #include <errno.h>
00041 #include <fcntl.h>
00042 #include <grp.h>
00043 #include <utime.h>
00044 #include <pwd.h>
00045 
00046 #if defined(HAVE_LIMITS_H)
00047 #include <limits.h>  // PATH_MAX
00048 #endif
00049 
00050 using namespace KIO;
00051 
00052 #define MAX_IPC_SIZE (1024*32)
00053 
00054 static bool
00055 same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
00056 {
00057    if (src.st_ino == dest.st_ino &&
00058        src.st_dev == dest.st_dev)
00059      return true;
00060 
00061    return false;
00062 }
00063 
00064 extern int write_all(int fd, const char *buf, size_t len);
00065 
00066 void FileProtocol::copy( const KUrl &srcUrl, const KUrl &destUrl,
00067                          int _mode, JobFlags _flags )
00068 {
00069     kDebug(7101) << "copy(): " << srcUrl << " -> " << destUrl << ", mode=" << _mode;
00070 
00071     const QString src = srcUrl.toLocalFile();
00072     const QString dest = destUrl.toLocalFile();
00073     QByteArray _src( QFile::encodeName(src));
00074     QByteArray _dest( QFile::encodeName(dest));
00075     KDE_struct_stat buff_src;
00076 #ifdef HAVE_POSIX_ACL
00077     acl_t acl;
00078 #endif
00079 
00080     if ( KDE_stat( _src.data(), &buff_src ) == -1 ) {
00081         if ( errno == EACCES )
00082            error(KIO::ERR_ACCESS_DENIED, src);
00083         else
00084            error(KIO::ERR_DOES_NOT_EXIST, src);
00085     return;
00086     }
00087 
00088     if ( S_ISDIR( buff_src.st_mode ) ) {
00089     error(KIO::ERR_IS_DIRECTORY, src);
00090     return;
00091     }
00092     if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) ) {
00093     error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
00094     return;
00095     }
00096 
00097     KDE_struct_stat buff_dest;
00098     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00099     if ( dest_exists )
00100     {
00101         if (S_ISDIR(buff_dest.st_mode))
00102         {
00103            error(KIO::ERR_DIR_ALREADY_EXIST, dest);
00104            return;
00105         }
00106 
00107     if ( same_inode( buff_dest, buff_src) )
00108     {
00109         error(KIO::ERR_IDENTICAL_FILES, dest);
00110         return;
00111     }
00112 
00113         if (!(_flags & KIO::Overwrite))
00114         {
00115            error(KIO::ERR_FILE_ALREADY_EXIST, dest);
00116            return;
00117         }
00118 
00119         // If the destination is a symlink and overwrite is TRUE,
00120         // remove the symlink first to prevent the scenario where
00121         // the symlink actually points to current source!
00122         if ((_flags & KIO::Overwrite) && S_ISLNK(buff_dest.st_mode))
00123         {
00124             //kDebug(7101) << "copy(): LINK DESTINATION";
00125             remove( _dest.data() );
00126         }
00127     }
00128 
00129     int src_fd = KDE_open( _src.data(), O_RDONLY);
00130     if ( src_fd < 0 ) {
00131     error(KIO::ERR_CANNOT_OPEN_FOR_READING, src);
00132     return;
00133     }
00134 
00135 #ifdef HAVE_FADVISE
00136     posix_fadvise(src_fd,0,0,POSIX_FADV_SEQUENTIAL);
00137 #endif
00138     // WABA: Make sure that we keep writing permissions ourselves,
00139     // otherwise we can be in for a surprise on NFS.
00140     mode_t initialMode;
00141     if (_mode != -1)
00142        initialMode = _mode | S_IWUSR;
00143     else
00144        initialMode = 0666;
00145 
00146     int dest_fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
00147     if ( dest_fd < 0 ) {
00148     kDebug(7101) << "###### COULD NOT WRITE " << dest;
00149         if ( errno == EACCES ) {
00150             error(KIO::ERR_WRITE_ACCESS_DENIED, dest);
00151         } else {
00152             error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
00153         }
00154         ::close(src_fd);
00155         return;
00156     }
00157 
00158 #ifdef HAVE_FADVISE
00159     posix_fadvise(dest_fd,0,0,POSIX_FADV_SEQUENTIAL);
00160 #endif
00161 
00162 #ifdef HAVE_POSIX_ACL
00163     acl = acl_get_fd(src_fd);
00164     if ( acl && !isExtendedACL( acl ) ) {
00165         kDebug(7101) << _dest.data() << " doesn't have extended ACL";
00166         acl_free( acl );
00167         acl = NULL;
00168     }
00169 #endif
00170     totalSize( buff_src.st_size );
00171 
00172     KIO::filesize_t processed_size = 0;
00173     char buffer[ MAX_IPC_SIZE ];
00174     int n;
00175 #ifdef USE_SENDFILE
00176     bool use_sendfile=buff_src.st_size < 0x7FFFFFFF;
00177 #endif
00178     while( 1 )
00179     {
00180 #ifdef USE_SENDFILE
00181        if (use_sendfile) {
00182             off_t sf = processed_size;
00183             n = KDE_sendfile( dest_fd, src_fd, &sf, MAX_IPC_SIZE );
00184             processed_size = sf;
00185             if ( n == -1 && ( errno == EINVAL || errno == ENOSYS ) ) { //not all filesystems support sendfile()
00186                 kDebug(7101) << "sendfile() not supported, falling back ";
00187                 use_sendfile = false;
00188             }
00189        }
00190        if (!use_sendfile)
00191 #endif
00192         n = ::read( src_fd, buffer, MAX_IPC_SIZE );
00193 
00194        if (n == -1)
00195        {
00196           if (errno == EINTR)
00197               continue;
00198 #ifdef USE_SENDFILE
00199           if ( use_sendfile ) {
00200             kDebug(7101) << "sendfile() error:" << strerror(errno);
00201             if ( errno == ENOSPC ) // disk full
00202             {
00203                 error(KIO::ERR_DISK_FULL, dest);
00204                 remove( _dest.data() );
00205             }
00206             else {
00207                 error(KIO::ERR_SLAVE_DEFINED,
00208                       i18n("Cannot copy file from %1 to %2. (Errno: %3)",
00209                       src, dest, errno));
00210             }
00211           } else
00212 #endif
00213           error(KIO::ERR_COULD_NOT_READ, src);
00214           ::close(src_fd);
00215           ::close(dest_fd);
00216 #ifdef HAVE_POSIX_ACL
00217           if (acl) acl_free(acl);
00218 #endif
00219           return;
00220        }
00221        if (n == 0)
00222           break; // Finished
00223 #ifdef USE_SENDFILE
00224        if ( !use_sendfile ) {
00225 #endif
00226          if (write_all( dest_fd, buffer, n))
00227          {
00228            ::close(src_fd);
00229            ::close(dest_fd);
00230 
00231            if ( errno == ENOSPC ) // disk full
00232            {
00233               error(KIO::ERR_DISK_FULL, dest);
00234               remove( _dest.data() );
00235            }
00236            else
00237            {
00238               kWarning(7101) << "Couldn't write[2]. Error:" << strerror(errno);
00239               error(KIO::ERR_COULD_NOT_WRITE, dest);
00240            }
00241 #ifdef HAVE_POSIX_ACL
00242            if (acl) acl_free(acl);
00243 #endif
00244            return;
00245          }
00246          processed_size += n;
00247 #ifdef USE_SENDFILE
00248        }
00249 #endif
00250        processedSize( processed_size );
00251     }
00252 
00253     ::close( src_fd );
00254 
00255     if (::close( dest_fd))
00256     {
00257         kWarning(7101) << "Error when closing file descriptor[2]:" << strerror(errno);
00258         error(KIO::ERR_COULD_NOT_WRITE, dest);
00259 #ifdef HAVE_POSIX_ACL
00260         if (acl) acl_free(acl);
00261 #endif
00262         return;
00263     }
00264 
00265     // set final permissions
00266     if ( _mode != -1 )
00267     {
00268         if ( (::chmod(_dest.data(), _mode) != 0)
00269 #ifdef HAVE_POSIX_ACL
00270           || (acl && acl_set_file(_dest.data(), ACL_TYPE_ACCESS, acl) != 0)
00271 #endif
00272         )
00273        {
00274            KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(dest);
00275            // Eat the error if the filesystem apparently doesn't support chmod.
00276            if ( mp && mp->testFileSystemFlag( KMountPoint::SupportsChmod ) )
00277                warning(i18n("Could not change permissions for\n%1", dest));
00278        }
00279     }
00280 #ifdef HAVE_POSIX_ACL
00281     if (acl) acl_free(acl);
00282 #endif
00283 
00284     // copy access and modification time
00285     struct utimbuf ut;
00286     ut.actime = buff_src.st_atime;
00287     ut.modtime = buff_src.st_mtime;
00288     if ( ::utime( _dest.data(), &ut ) != 0 )
00289     {
00290         kWarning() << QString::fromLatin1("Couldn't preserve access and modification time for\n%1").arg(dest);
00291     }
00292 
00293     processedSize( buff_src.st_size );
00294     finished();
00295 }
00296 
00297 void FileProtocol::listDir( const KUrl& url)
00298 {
00299     if (!url.isLocalFile()) {
00300         KUrl redir(url);
00301     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00302     redirection(redir);
00303     kDebug(7101) << "redirecting to " << redir.url();
00304     finished();
00305     return;
00306     }
00307     const QString path(url.toLocalFile());
00308     const QByteArray _path(QFile::encodeName(path));
00309     DIR* dp = opendir(_path.data());
00310     if ( dp == 0 ) {
00311         switch (errno) {
00312         case ENOENT:
00313             error(KIO::ERR_DOES_NOT_EXIST, path);
00314             return;
00315         case ENOTDIR:
00316             error(KIO::ERR_IS_FILE, path);
00317             break;
00318 #ifdef ENOMEDIUM
00319     case ENOMEDIUM:
00320             error(ERR_SLAVE_DEFINED,
00321                   i18n("No media in device for %1", path));
00322             break;
00323 #endif
00324         default:
00325             error(KIO::ERR_CANNOT_ENTER_DIRECTORY, path);
00326             break;
00327         }
00328     return;
00329     }
00330 
00331     const QString sDetails = metaData(QLatin1String("details"));
00332     const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
00333     //kDebug(7101) << "========= LIST " << url << "details=" << details << " =========";
00334     UDSEntry entry;
00335 
00336     // Don't make this a QStringList. The locale file name we get here
00337     // should be passed intact to createUDSEntry to avoid problems with
00338     // files where QFile::encodeName(QFile::decodeName(a)) != a.
00339     QList<QByteArray> entryNames;
00340     KDE_struct_dirent *ep;
00341     if (details == 0) {
00342         // Fast path (for recursive deletion, mostly)
00343         // Simply emit the name and file type, nothing else.
00344         while ( ( ep = KDE_readdir( dp ) ) != 0 ) {
00345             entry.clear();
00346             entry.insert(KIO::UDSEntry::UDS_NAME, QFile::decodeName(ep->d_name));
00347             entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,
00348                          (ep->d_type & DT_DIR) ? S_IFDIR : S_IFREG );
00349             if (ep->d_type & DT_LNK) {
00350                 // for symlinks obey the UDSEntry contract and provide UDS_LINK_DEST
00351                 // even if we don't know the link dest (and DeleteJob doesn't care...)
00352                 entry.insert(KIO::UDSEntry::UDS_LINK_DEST, QLatin1String("Dummy Link Target"));
00353             }
00354             listEntry(entry, false);
00355         }
00356         closedir( dp );
00357         listEntry( entry, true ); // ready
00358     } else {
00359         while ( ( ep = KDE_readdir( dp ) ) != 0 ) {
00360             entryNames.append( ep->d_name );
00361         }
00362 
00363         closedir( dp );
00364         totalSize( entryNames.count() );
00365 
00366         /* set the current dir to the path to speed up
00367            in not having to pass an absolute path.
00368            We restore the path later to get out of the
00369            path - the kernel wouldn't unmount or delete
00370            directories we keep as active directory. And
00371            as the slave runs in the background, it's hard
00372            to see for the user what the problem would be */
00373         char path_buffer[PATH_MAX];
00374         path_buffer[0] = '\0';
00375         (void) getcwd(path_buffer, PATH_MAX - 1);
00376         if ( chdir( _path.data() ) )  {
00377             if (errno == EACCES)
00378                 error(ERR_ACCESS_DENIED, path);
00379             else
00380                 error(ERR_CANNOT_ENTER_DIRECTORY, path);
00381             finished();
00382         }
00383 
00384         QList<QByteArray>::ConstIterator it = entryNames.constBegin();
00385         QList<QByteArray>::ConstIterator end = entryNames.constEnd();
00386         for (; it != end; ++it) {
00387             entry.clear();
00388             if ( createUDSEntry( QFile::decodeName(*it),
00389                                  *it /* we can use the filename as relative path*/,
00390                                  entry, details, true ) )
00391                 listEntry( entry, false);
00392         }
00393 
00394         listEntry( entry, true ); // ready
00395 
00396         //kDebug(7101) << "============= COMPLETED LIST ============";
00397 
00398         if (*path_buffer)
00399             chdir(path_buffer);
00400     }
00401     finished();
00402 }
00403 
00404 void FileProtocol::rename( const KUrl &srcUrl, const KUrl &destUrl,
00405                            KIO::JobFlags _flags )
00406 {
00407     char off_t_should_be_64_bits[sizeof(off_t) >= 8 ? 1 : -1]; (void) off_t_should_be_64_bits;
00408     const QString src = srcUrl.toLocalFile();
00409     const QString dest = destUrl.toLocalFile();
00410     const QByteArray _src(QFile::encodeName(src));
00411     const QByteArray _dest(QFile::encodeName(dest));
00412     KDE_struct_stat buff_src;
00413     if ( KDE_lstat( _src.data(), &buff_src ) == -1 ) {
00414         if ( errno == EACCES )
00415            error(KIO::ERR_ACCESS_DENIED, src);
00416         else
00417            error(KIO::ERR_DOES_NOT_EXIST, src);
00418         return;
00419     }
00420 
00421     KDE_struct_stat buff_dest;
00422     // stat symlinks here (lstat, not stat), to avoid ERR_IDENTICAL_FILES when replacing symlink
00423     // with its target (#169547)
00424     bool dest_exists = ( KDE_lstat( _dest.data(), &buff_dest ) != -1 );
00425     if ( dest_exists )
00426     {
00427         if (S_ISDIR(buff_dest.st_mode))
00428         {
00429            error(KIO::ERR_DIR_ALREADY_EXIST, dest);
00430            return;
00431         }
00432 
00433     if ( same_inode( buff_dest, buff_src) )
00434     {
00435         error(KIO::ERR_IDENTICAL_FILES, dest);
00436         return;
00437     }
00438 
00439         if (!(_flags & KIO::Overwrite))
00440         {
00441            error(KIO::ERR_FILE_ALREADY_EXIST, dest);
00442            return;
00443         }
00444     }
00445 
00446     if ( KDE_rename( _src.data(), _dest.data()))
00447     {
00448         if (( errno == EACCES ) || (errno == EPERM)) {
00449             error(KIO::ERR_ACCESS_DENIED, dest);
00450         }
00451         else if (errno == EXDEV) {
00452            error(KIO::ERR_UNSUPPORTED_ACTION, QLatin1String("rename"));
00453         }
00454         else if (errno == EROFS) { // The file is on a read-only filesystem
00455            error(KIO::ERR_CANNOT_DELETE, src);
00456         }
00457         else {
00458            error(KIO::ERR_CANNOT_RENAME, src);
00459         }
00460         return;
00461     }
00462 
00463     finished();
00464 }
00465 
00466 void FileProtocol::symlink( const QString &target, const KUrl &destUrl, KIO::JobFlags flags )
00467 {
00468     const QString dest = destUrl.toLocalFile();
00469     // Assume dest is local too (wouldn't be here otherwise)
00470     if ( ::symlink( QFile::encodeName(target), QFile::encodeName(dest) ) == -1 )
00471     {
00472         // Does the destination already exist ?
00473         if ( errno == EEXIST )
00474         {
00475             if ( (flags & KIO::Overwrite) )
00476             {
00477                 // Try to delete the destination
00478                 if ( unlink( QFile::encodeName(dest) ) != 0 )
00479                 {
00480                     error(KIO::ERR_CANNOT_DELETE, dest);
00481                     return;
00482                 }
00483                 // Try again - this won't loop forever since unlink succeeded
00484                 symlink( target, destUrl, flags );
00485             }
00486             else
00487             {
00488                 KDE_struct_stat buff_dest;
00489                 KDE_lstat( QFile::encodeName(dest), &buff_dest );
00490                 if (S_ISDIR(buff_dest.st_mode))
00491                     error(KIO::ERR_DIR_ALREADY_EXIST, dest);
00492                 else
00493                     error(KIO::ERR_FILE_ALREADY_EXIST, dest);
00494                 return;
00495             }
00496         }
00497         else
00498         {
00499             // Some error occurred while we tried to symlink
00500             error(KIO::ERR_CANNOT_SYMLINK, dest);
00501             return;
00502         }
00503     }
00504     finished();
00505 }
00506 
00507 void FileProtocol::del(const KUrl& url, bool isfile)
00508 {
00509     const QString path = url.toLocalFile();
00510     const QByteArray _path( QFile::encodeName(path));
00511     /*****
00512      * Delete files
00513      *****/
00514 
00515     if (isfile) {
00516     kDebug(7101) << "Deleting file "<< url;
00517 
00518     if ( unlink( _path.data() ) == -1 ) {
00519             if ((errno == EACCES) || (errno == EPERM))
00520                error(KIO::ERR_ACCESS_DENIED, path);
00521             else if (errno == EISDIR)
00522                error(KIO::ERR_IS_DIRECTORY, path);
00523             else
00524                error(KIO::ERR_CANNOT_DELETE, path);
00525         return;
00526     }
00527     } else {
00528 
00529       /*****
00530        * Delete empty directory
00531        *****/
00532 
00533       kDebug( 7101 ) << "Deleting directory " << url.url();
00534       if (metaData(QLatin1String("recurse")) == QLatin1String("true")) {
00535           if (!deleteRecursive(path))
00536               return;
00537       }
00538       if ( ::rmdir( _path.data() ) == -1 ) {
00539     if ((errno == EACCES) || (errno == EPERM))
00540       error(KIO::ERR_ACCESS_DENIED, path);
00541     else {
00542       kDebug( 7101 ) << "could not rmdir " << perror;
00543       error(KIO::ERR_COULD_NOT_RMDIR, path);
00544       return;
00545     }
00546       }
00547     }
00548 
00549     finished();
00550 }
00551 
00552 void FileProtocol::chown( const KUrl& url, const QString& owner, const QString& group )
00553 {
00554     const QString path = url.toLocalFile();
00555     const QByteArray _path( QFile::encodeName(path) );
00556     uid_t uid;
00557     gid_t gid;
00558 
00559     // get uid from given owner
00560     {
00561         struct passwd *p = ::getpwnam(owner.toAscii());
00562 
00563         if ( ! p ) {
00564             error( KIO::ERR_SLAVE_DEFINED,
00565                    i18n( "Could not get user id for given user name %1", owner ) );
00566             return;
00567         }
00568 
00569         uid = p->pw_uid;
00570     }
00571 
00572     // get gid from given group
00573     {
00574         struct group *p = ::getgrnam(group.toAscii());
00575 
00576         if ( ! p ) {
00577             error( KIO::ERR_SLAVE_DEFINED,
00578                    i18n( "Could not get group id for given group name %1", group ) );
00579             return;
00580         }
00581 
00582         gid = p->gr_gid;
00583     }
00584 
00585     if ( ::chown(_path, uid, gid) == -1 ) {
00586         switch ( errno ) {
00587             case EPERM:
00588             case EACCES:
00589                 error(KIO::ERR_ACCESS_DENIED, path);
00590                 break;
00591             case ENOSPC:
00592                 error(KIO::ERR_DISK_FULL, path);
00593                 break;
00594             default:
00595                 error(KIO::ERR_CANNOT_CHOWN, path);
00596         }
00597     } else
00598         finished();
00599 }
00600 
00601 void FileProtocol::stat( const KUrl & url )
00602 {
00603     if (!url.isLocalFile()) {
00604         KUrl redir(url);
00605     redir.setProtocol(config()->readEntry("DefaultRemoteProtocol", "smb"));
00606     redirection(redir);
00607     kDebug(7101) << "redirecting to " << redir.url();
00608     finished();
00609     return;
00610     }
00611 
00612     /* directories may not have a slash at the end if
00613      * we want to stat() them; it requires that we
00614      * change into it .. which may not be allowed
00615      * stat("/is/unaccessible")  -> rwx------
00616      * stat("/is/unaccessible/") -> EPERM            H.Z.
00617      * This is the reason for the -1
00618      */
00619     const QString path(url.path(KUrl::RemoveTrailingSlash));
00620     const QByteArray _path( QFile::encodeName(path));
00621     const QString sDetails = metaData(QLatin1String("details"));
00622     const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
00623 
00624     UDSEntry entry;
00625     if ( !createUDSEntry( url.fileName(), _path, entry, details, true /*with acls*/ ) )
00626     {
00627         error(KIO::ERR_DOES_NOT_EXIST, path);
00628         return;
00629     }
00630 #if 0
00632     MetaData::iterator it1 = mOutgoingMetaData.begin();
00633     for ( ; it1 != mOutgoingMetaData.end(); it1++ ) {
00634         kDebug(7101) << it1.key() << " = " << it1.data();
00635     }
00637 #endif
00638     statEntry( entry );
00639 
00640     finished();
00641 }

KIOSlave

Skip menu "KIOSlave"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal