fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kio / kio / kfileitem.cpp
blob75690c6cc82273d8b2949ed4f7288ab43c474009
1 /* This file is part of the KDE project
2 Copyright (C) 1999-2006 David Faure <faure@kde.org>
3 2001 Carsten Pfeiffer <pfeiffer@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "kfileitem.h"
23 #include <config.h>
25 #include <sys/time.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
31 #include <assert.h>
32 #include <unistd.h>
34 #include <QtCore/QDate>
35 #include <QtCore/QDir>
36 #include <QtCore/QFile>
37 #include <QtCore/QMap>
38 #include <QtGui/QApplication>
39 #include <QTextDocument>
41 #include <kdebug.h>
42 #include <kfilemetainfo.h>
43 #include <kglobal.h>
44 #include <kglobalsettings.h>
45 #include <kiconloader.h>
46 #include <klocale.h>
47 #include <kmimetype.h>
48 #include <krun.h>
49 #include <kde_file.h>
50 #include <kdesktopfile.h>
51 #include <kmountpoint.h>
52 #include <kconfiggroup.h>
53 #ifndef Q_OS_WIN
54 #include <knfsshare.h>
55 #include <ksambashare.h>
56 #endif
58 class KFileItemPrivate : public QSharedData
60 public:
61 KFileItemPrivate(const KIO::UDSEntry& entry,
62 mode_t mode, mode_t permissions,
63 const KUrl& itemOrDirUrl,
64 bool urlIsDirectory,
65 bool delayedMimeTypes)
66 : m_entry( entry ),
67 m_url(itemOrDirUrl),
68 m_strName(),
69 m_strText(),
70 m_iconName(),
71 m_strLowerCaseName(),
72 m_pMimeType( 0 ),
73 m_fileMode( mode ),
74 m_permissions( permissions ),
75 m_bMarked( false ),
76 m_bLink( false ),
77 m_bIsLocalUrl(itemOrDirUrl.isLocalFile()),
78 m_bMimeTypeKnown( false ),
79 m_delayedMimeTypes( delayedMimeTypes ),
80 m_useIconNameCache(false),
81 m_hidden( Auto )
83 if (entry.count() != 0) {
84 readUDSEntry( urlIsDirectory );
85 } else {
86 Q_ASSERT(!urlIsDirectory);
87 m_strName = itemOrDirUrl.fileName();
88 m_strText = KIO::decodeFileName( m_strName );
90 init();
93 ~KFileItemPrivate()
97 /**
98 * Computes the text and mode from the UDSEntry
99 * Called by constructor, but can be called again later
100 * Nothing does that anymore though (I guess some old KonqFileItem did)
101 * so it's not a protected method of the public class anymore.
103 void init();
105 KIO::filesize_t size() const;
106 KDateTime time( KFileItem::FileTimes which ) const;
107 void setTime(KFileItem::FileTimes which, long long time_t_val) const;
108 bool cmp( const KFileItemPrivate & item ) const;
109 QString user() const;
110 QString group() const;
113 * Extracts the data from the UDSEntry member and updates the KFileItem
114 * accordingly.
116 void readUDSEntry( bool _urlIsDirectory );
119 * Parses the given permission set and provides it for access()
121 QString parsePermissions( mode_t perm ) const;
124 * The UDSEntry that contains the data for this fileitem, if it came from a directory listing.
126 mutable KIO::UDSEntry m_entry;
128 * The url of the file
130 KUrl m_url;
133 * The text for this item, i.e. the file name without path,
135 QString m_strName;
138 * The text for this item, i.e. the file name without path, decoded
139 * ('%%' becomes '%', '%2F' becomes '/')
141 QString m_strText;
144 * The icon name for this item.
146 mutable QString m_iconName;
149 * The filename in lower case (to speed up sorting)
151 mutable QString m_strLowerCaseName;
154 * The mimetype of the file
156 mutable KMimeType::Ptr m_pMimeType;
159 * The file mode
161 mode_t m_fileMode;
163 * The permissions
165 mode_t m_permissions;
168 * Marked : see mark()
170 bool m_bMarked:1;
172 * Whether the file is a link
174 bool m_bLink:1;
176 * True if local file
178 bool m_bIsLocalUrl:1;
180 mutable bool m_bMimeTypeKnown:1;
181 bool m_delayedMimeTypes:1;
183 /** True if m_iconName should be used as cache. */
184 mutable bool m_useIconNameCache:1;
186 // Auto: check leading dot.
187 enum { Auto, Hidden, Shown } m_hidden:3;
189 // For special case like link to dirs over FTP
190 QString m_guessedMimeType;
191 mutable QString m_access;
192 QMap<const void*, void*> m_extra; // DEPRECATED
193 mutable KFileMetaInfo m_metaInfo;
195 enum { NumFlags = KFileItem::CreationTime + 1 };
196 mutable KDateTime m_time[3];
199 void KFileItemPrivate::init()
201 m_access.clear();
202 // metaInfo = KFileMetaInfo();
204 // determine mode and/or permissions if unknown
205 // TODO: delay this until requested
206 if ( m_fileMode == KFileItem::Unknown || m_permissions == KFileItem::Unknown )
208 mode_t mode = 0;
209 if ( m_url.isLocalFile() )
211 /* directories may not have a slash at the end if
212 * we want to stat() them; it requires that we
213 * change into it .. which may not be allowed
214 * stat("/is/unaccessible") -> rwx------
215 * stat("/is/unaccessible/") -> EPERM H.Z.
216 * This is the reason for the -1
218 KDE_struct_stat buf;
219 const QString path = m_url.toLocalFile( KUrl::RemoveTrailingSlash );
220 if ( KDE::lstat( path, &buf ) == 0 )
222 mode = buf.st_mode;
223 if ( S_ISLNK( mode ) )
225 m_bLink = true;
226 if ( KDE::stat( path, &buf ) == 0 )
227 mode = buf.st_mode;
228 else // link pointing to nowhere (see kio/file/file.cc)
229 mode = (S_IFMT-1) | S_IRWXU | S_IRWXG | S_IRWXO;
231 // While we're at it, store the times
232 setTime(KFileItem::ModificationTime, buf.st_mtime);
233 setTime(KFileItem::AccessTime, buf.st_atime);
234 if ( m_fileMode == KFileItem::Unknown )
235 m_fileMode = mode & S_IFMT; // extract file type
236 if ( m_permissions == KFileItem::Unknown )
237 m_permissions = mode & 07777; // extract permissions
238 } else {
239 kDebug() << path << "does not exist anymore";
245 void KFileItemPrivate::readUDSEntry( bool _urlIsDirectory )
247 // extract fields from the KIO::UDS Entry
249 m_fileMode = m_entry.numberValue( KIO::UDSEntry::UDS_FILE_TYPE );
250 m_permissions = m_entry.numberValue( KIO::UDSEntry::UDS_ACCESS );
251 m_strName = m_entry.stringValue( KIO::UDSEntry::UDS_NAME );
253 const QString displayName = m_entry.stringValue( KIO::UDSEntry::UDS_DISPLAY_NAME );
254 if (!displayName.isEmpty())
255 m_strText = displayName;
256 else
257 m_strText = KIO::decodeFileName( m_strName );
259 const QString urlStr = m_entry.stringValue( KIO::UDSEntry::UDS_URL );
260 const bool UDS_URL_seen = !urlStr.isEmpty();
261 if ( UDS_URL_seen ) {
262 m_url = KUrl( urlStr );
263 if ( m_url.isLocalFile() )
264 m_bIsLocalUrl = true;
266 const QString mimeTypeStr = m_entry.stringValue( KIO::UDSEntry::UDS_MIME_TYPE );
267 m_bMimeTypeKnown = !mimeTypeStr.isEmpty();
268 if ( m_bMimeTypeKnown )
269 m_pMimeType = KMimeType::mimeType( mimeTypeStr );
271 m_guessedMimeType = m_entry.stringValue( KIO::UDSEntry::UDS_GUESSED_MIME_TYPE );
272 m_bLink = !m_entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST ).isEmpty(); // we don't store the link dest
274 const int hiddenVal = m_entry.numberValue( KIO::UDSEntry::UDS_HIDDEN, -1 );
275 m_hidden = hiddenVal == 1 ? Hidden : ( hiddenVal == 0 ? Shown : Auto );
277 // avoid creating these QStrings again and again
278 static const QString& dot = KGlobal::staticQString(".");
279 if ( _urlIsDirectory && !UDS_URL_seen && !m_strName.isEmpty() && m_strName != dot )
280 m_url.addPath( m_strName );
282 m_iconName.clear();
285 inline //because it is used only in one place
286 KIO::filesize_t KFileItemPrivate::size() const
288 // Extract it from the KIO::UDSEntry
289 long long fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_SIZE, -1 );
290 if ( fieldVal != -1 ) {
291 return fieldVal;
294 // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL]
295 if ( m_bIsLocalUrl ) {
296 KDE_struct_stat buf;
297 if ( KDE::stat( m_url.toLocalFile(KUrl::RemoveTrailingSlash), &buf ) == 0 )
298 return buf.st_size;
300 return 0;
303 void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, long long time_t_val) const
305 m_time[mappedWhich].setTime_t(time_t_val);
306 m_time[mappedWhich] = m_time[mappedWhich].toLocalZone(); // #160979
309 KDateTime KFileItemPrivate::time( KFileItem::FileTimes mappedWhich ) const
311 if ( !m_time[mappedWhich].isNull() )
312 return m_time[mappedWhich];
314 // Extract it from the KIO::UDSEntry
315 long long fieldVal = -1;
316 switch ( mappedWhich ) {
317 case KFileItem::ModificationTime:
318 fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
319 break;
320 case KFileItem::AccessTime:
321 fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_ACCESS_TIME, -1 );
322 break;
323 case KFileItem::CreationTime:
324 fieldVal = m_entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
325 break;
327 if ( fieldVal != -1 ) {
328 setTime(mappedWhich, fieldVal);
329 return m_time[mappedWhich];
332 // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL]
333 if ( m_bIsLocalUrl )
335 KDE_struct_stat buf;
336 if ( KDE::stat( m_url.toLocalFile(KUrl::RemoveTrailingSlash), &buf ) == 0 )
338 setTime(KFileItem::ModificationTime, buf.st_mtime);
339 setTime(KFileItem::AccessTime, buf.st_atime);
340 m_time[KFileItem::CreationTime] = KDateTime();
341 return m_time[mappedWhich];
344 return KDateTime();
347 inline //because it is used only in one place
348 bool KFileItemPrivate::cmp( const KFileItemPrivate & item ) const
350 #if 0
351 kDebug() << "Comparing" << m_url << "and" << item.m_url;
352 kDebug() << " name" << (m_strName == item.m_strName);
353 kDebug() << " local" << (m_bIsLocalUrl == item.m_bIsLocalUrl);
354 kDebug() << " mode" << (m_fileMode == item.m_fileMode);
355 kDebug() << " perm" << (m_permissions == item.m_permissions);
356 kDebug() << " UDS_USER" << (user() == item.user());
357 kDebug() << " UDS_GROUP" << (group() == item.group());
358 kDebug() << " UDS_EXTENDED_ACL" << (m_entry.stringValue( KIO::UDSEntry::UDS_EXTENDED_ACL ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_EXTENDED_ACL ));
359 kDebug() << " UDS_ACL_STRING" << (m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING ));
360 kDebug() << " UDS_DEFAULT_ACL_STRING" << (m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING ));
361 kDebug() << " m_bLink" << (m_bLink == item.m_bLink);
362 kDebug() << " m_hidden" << (m_hidden == item.m_hidden);
363 kDebug() << " size" << (size() == item.size());
364 kDebug() << " ModificationTime" << (time(KFileItem::ModificationTime) == item.time(KFileItem::ModificationTime));
365 kDebug() << " UDS_ICON_NAME" << (m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ));
366 #endif
367 return ( m_strName == item.m_strName
368 && m_bIsLocalUrl == item.m_bIsLocalUrl
369 && m_fileMode == item.m_fileMode
370 && m_permissions == item.m_permissions
371 && user() == item.user()
372 && group() == item.group()
373 && m_entry.stringValue( KIO::UDSEntry::UDS_EXTENDED_ACL ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_EXTENDED_ACL )
374 && m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING )
375 && m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING )
376 && m_bLink == item.m_bLink
377 && m_hidden == item.m_hidden
378 && size() == item.size()
379 && time(KFileItem::ModificationTime) == item.time(KFileItem::ModificationTime)
380 && m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME )
383 // Don't compare the mimetypes here. They might not be known, and we don't want to
384 // do the slow operation of determining them here.
387 inline //because it is used only in one place
388 QString KFileItemPrivate::parsePermissions(mode_t perm) const
390 static char buffer[ 12 ];
392 char uxbit,gxbit,oxbit;
394 if ( (perm & (S_IXUSR|S_ISUID)) == (S_IXUSR|S_ISUID) )
395 uxbit = 's';
396 else if ( (perm & (S_IXUSR|S_ISUID)) == S_ISUID )
397 uxbit = 'S';
398 else if ( (perm & (S_IXUSR|S_ISUID)) == S_IXUSR )
399 uxbit = 'x';
400 else
401 uxbit = '-';
403 if ( (perm & (S_IXGRP|S_ISGID)) == (S_IXGRP|S_ISGID) )
404 gxbit = 's';
405 else if ( (perm & (S_IXGRP|S_ISGID)) == S_ISGID )
406 gxbit = 'S';
407 else if ( (perm & (S_IXGRP|S_ISGID)) == S_IXGRP )
408 gxbit = 'x';
409 else
410 gxbit = '-';
412 if ( (perm & (S_IXOTH|S_ISVTX)) == (S_IXOTH|S_ISVTX) )
413 oxbit = 't';
414 else if ( (perm & (S_IXOTH|S_ISVTX)) == S_ISVTX )
415 oxbit = 'T';
416 else if ( (perm & (S_IXOTH|S_ISVTX)) == S_IXOTH )
417 oxbit = 'x';
418 else
419 oxbit = '-';
421 // Include the type in the first char like kde3 did; people are more used to seeing it,
422 // even though it's not really part of the permissions per se.
423 if (m_bLink)
424 buffer[0] = 'l';
425 else if (m_fileMode != KFileItem::Unknown) {
426 if (S_ISDIR(m_fileMode))
427 buffer[0] = 'd';
428 else if (S_ISSOCK(m_fileMode))
429 buffer[0] = 's';
430 else if (S_ISCHR(m_fileMode))
431 buffer[0] = 'c';
432 else if (S_ISBLK(m_fileMode))
433 buffer[0] = 'b';
434 else if (S_ISFIFO(m_fileMode))
435 buffer[0] = 'p';
436 else
437 buffer[0] = '-';
438 } else {
439 buffer[0] = '-';
442 buffer[1] = ((( perm & S_IRUSR ) == S_IRUSR ) ? 'r' : '-' );
443 buffer[2] = ((( perm & S_IWUSR ) == S_IWUSR ) ? 'w' : '-' );
444 buffer[3] = uxbit;
445 buffer[4] = ((( perm & S_IRGRP ) == S_IRGRP ) ? 'r' : '-' );
446 buffer[5] = ((( perm & S_IWGRP ) == S_IWGRP ) ? 'w' : '-' );
447 buffer[6] = gxbit;
448 buffer[7] = ((( perm & S_IROTH ) == S_IROTH ) ? 'r' : '-' );
449 buffer[8] = ((( perm & S_IWOTH ) == S_IWOTH ) ? 'w' : '-' );
450 buffer[9] = oxbit;
451 // if (hasExtendedACL())
452 if (m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL)) {
453 buffer[10] = '+';
454 buffer[11] = 0;
455 } else {
456 buffer[10] = 0;
459 return QString::fromLatin1(buffer);
463 ///////
465 KFileItem::KFileItem()
466 : d(0)
470 KFileItem::KFileItem( const KIO::UDSEntry& entry, const KUrl& itemOrDirUrl,
471 bool delayedMimeTypes, bool urlIsDirectory )
472 : d(new KFileItemPrivate(entry, KFileItem::Unknown, KFileItem::Unknown,
473 itemOrDirUrl, urlIsDirectory, delayedMimeTypes))
477 KFileItem::KFileItem( mode_t mode, mode_t permissions, const KUrl& url, bool delayedMimeTypes )
478 : d(new KFileItemPrivate(KIO::UDSEntry(), mode, permissions,
479 url, false, delayedMimeTypes))
483 KFileItem::KFileItem( const KUrl &url, const QString &mimeType, mode_t mode )
484 : d(new KFileItemPrivate(KIO::UDSEntry(), mode, KFileItem::Unknown,
485 url, false, false))
487 d->m_bMimeTypeKnown = !mimeType.isEmpty();
488 if (d->m_bMimeTypeKnown)
489 d->m_pMimeType = KMimeType::mimeType( mimeType );
493 KFileItem::KFileItem(const KFileItem& other)
494 : d(other.d)
498 KFileItem::~KFileItem()
502 void KFileItem::refresh()
504 d->m_fileMode = KFileItem::Unknown;
505 d->m_permissions = KFileItem::Unknown;
506 d->m_metaInfo = KFileMetaInfo();
507 d->m_hidden = KFileItemPrivate::Auto;
508 refreshMimeType();
510 // Basically, we can't trust any information we got while listing.
511 // Everything could have changed...
512 // Clearing m_entry makes it possible to detect changes in the size of the file,
513 // the time information, etc.
514 d->m_entry.clear();
515 d->init();
518 void KFileItem::refreshMimeType()
520 d->m_pMimeType = 0;
521 d->m_bMimeTypeKnown = false;
522 d->m_iconName.clear();
525 void KFileItem::setUrl( const KUrl &url )
527 d->m_url = url;
528 setName( url.fileName() );
531 void KFileItem::setName( const QString& name )
533 d->m_strName = name;
534 d->m_strText = KIO::decodeFileName( d->m_strName );
537 QString KFileItem::linkDest() const
539 // Extract it from the KIO::UDSEntry
540 const QString linkStr = d->m_entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
541 if ( !linkStr.isEmpty() )
542 return linkStr;
544 // If not in the KIO::UDSEntry, or if UDSEntry empty, use readlink() [if local URL]
545 if ( d->m_bIsLocalUrl )
547 char buf[1000];
548 int n = readlink( QFile::encodeName(d->m_url.toLocalFile( KUrl::RemoveTrailingSlash )), buf, sizeof(buf)-1 );
549 if ( n != -1 )
551 buf[ n ] = 0;
552 return QFile::decodeName( buf );
555 return QString();
558 QString KFileItem::localPath() const
560 if ( d->m_bIsLocalUrl ) {
561 return d->m_url.toLocalFile();
564 // Extract the local path from the KIO::UDSEntry
565 return d->m_entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
568 KIO::filesize_t KFileItem::size() const
570 return d->size();
573 bool KFileItem::hasExtendedACL() const
575 // Check if the field exists; its value doesn't matter
576 return d->m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL);
579 KACL KFileItem::ACL() const
581 if ( hasExtendedACL() ) {
582 // Extract it from the KIO::UDSEntry
583 const QString fieldVal = d->m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING );
584 if ( !fieldVal.isEmpty() )
585 return KACL( fieldVal );
587 // create one from the basic permissions
588 return KACL( d->m_permissions );
591 KACL KFileItem::defaultACL() const
593 // Extract it from the KIO::UDSEntry
594 const QString fieldVal = d->m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING );
595 if ( !fieldVal.isEmpty() )
596 return KACL(fieldVal);
597 else
598 return KACL();
601 KDateTime KFileItem::time( FileTimes which ) const
603 return d->time(which);
606 time_t KFileItem::time( unsigned int which ) const
608 switch (which) {
609 case KIO::UDSEntry::UDS_ACCESS_TIME:
610 return d->time(AccessTime).toTime_t();
611 case KIO::UDSEntry::UDS_CREATION_TIME:
612 return d->time(CreationTime).toTime_t();
613 case KIO::UDSEntry::UDS_MODIFICATION_TIME:
614 default:
615 return d->time(ModificationTime).toTime_t();
619 QString KFileItem::user() const
621 return d->user();
624 QString KFileItemPrivate::user() const
626 QString userName = m_entry.stringValue(KIO::UDSEntry::UDS_USER);
627 if (userName.isEmpty() && m_bIsLocalUrl) {
628 #ifdef Q_WS_WIN
629 QFileInfo a(m_url.toLocalFile( KUrl::RemoveTrailingSlash ));
630 userName = a.owner();
631 m_entry.insert( KIO::UDSEntry::UDS_USER, userName );
632 #else
633 KDE_struct_stat buff;
634 if ( KDE::lstat( m_url.toLocalFile( KUrl::RemoveTrailingSlash ), &buff ) == 0) // get uid/gid of the link, if it's a link
636 struct passwd *pwuser = getpwuid( buff.st_uid );
637 if ( pwuser != 0 ) {
638 userName = QString::fromLocal8Bit(pwuser->pw_name);
639 m_entry.insert( KIO::UDSEntry::UDS_USER, userName );
642 #endif
644 return userName;
647 QString KFileItem::group() const
649 return d->group();
652 QString KFileItemPrivate::group() const
654 QString groupName = m_entry.stringValue( KIO::UDSEntry::UDS_GROUP );
655 if (groupName.isEmpty() && m_bIsLocalUrl )
657 #ifdef Q_WS_WIN
658 QFileInfo a(m_url.toLocalFile( KUrl::RemoveTrailingSlash ));
659 groupName = a.group();
660 m_entry.insert( KIO::UDSEntry::UDS_GROUP, groupName );
661 #else
662 KDE_struct_stat buff;
663 if ( KDE::lstat( m_url.toLocalFile( KUrl::RemoveTrailingSlash ), &buff ) == 0) // get uid/gid of the link, if it's a link
665 struct group *ge = getgrgid( buff.st_gid );
666 if ( ge != 0 ) {
667 groupName = QString::fromLocal8Bit(ge->gr_name);
668 if (groupName.isEmpty())
669 groupName.sprintf("%d",ge->gr_gid);
671 else
672 groupName.sprintf("%d",buff.st_gid);
673 m_entry.insert( KIO::UDSEntry::UDS_GROUP, groupName );
675 #endif
677 return groupName;
680 QString KFileItem::mimetype() const
682 KFileItem * that = const_cast<KFileItem *>(this);
683 return that->determineMimeType()->name();
686 KMimeType::Ptr KFileItem::determineMimeType() const
688 if ( !d->m_pMimeType || !d->m_bMimeTypeKnown )
690 bool isLocalUrl;
691 KUrl url = mostLocalUrl(isLocalUrl);
693 d->m_pMimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl );
694 Q_ASSERT(d->m_pMimeType);
695 //kDebug() << d << "finding final mimetype for" << url << ":" << d->m_pMimeType->name();
696 d->m_bMimeTypeKnown = true;
699 return d->m_pMimeType;
702 bool KFileItem::isMimeTypeKnown() const
704 // The mimetype isn't known if determineMimeType was never called (on-demand determination)
705 // or if this fileitem has a guessed mimetype (e.g. ftp symlink) - in which case
706 // it always remains "not fully determined"
707 return d->m_bMimeTypeKnown && d->m_guessedMimeType.isEmpty();
710 QString KFileItem::mimeComment() const
712 KMimeType::Ptr mType = determineMimeType();
714 bool isLocalUrl;
715 KUrl url = mostLocalUrl(isLocalUrl);
717 KMimeType::Ptr mime = mimeTypePtr();
718 if (isLocalUrl && mime->is("application/x-desktop")) {
719 KDesktopFile cfg( url.toLocalFile() );
720 QString comment = cfg.desktopGroup().readEntry( "Comment" );
721 if (!comment.isEmpty())
722 return comment;
725 QString comment = mType->comment( url );
726 //kDebug() << "finding comment for " << url.url() << " : " << d->m_pMimeType->name();
727 if (!comment.isEmpty())
728 return comment;
729 else
730 return mType->name();
733 static QString iconFromDesktopFile(const QString& path)
735 KDesktopFile cfg( path );
736 const KConfigGroup group = cfg.desktopGroup();
737 const QString icon = cfg.readIcon();
738 const QString type = cfg.readPath();
740 if ( cfg.hasDeviceType() )
742 const QString unmount_icon = group.readEntry( "UnmountIcon" );
743 const QString dev = cfg.readDevice();
744 if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() )
746 KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByDevice(dev);
747 if (!mountPoint) // not mounted?
748 return unmount_icon;
750 } else if ( cfg.hasLinkType() ) {
751 const QString emptyIcon = group.readEntry( "EmptyIcon" );
752 if ( !emptyIcon.isEmpty() ) {
753 const QString u = cfg.readUrl();
754 const KUrl url( u );
755 if ( url.protocol() == "trash" ) {
756 // We need to find if the trash is empty, preferrably without using a KIO job.
757 // So instead kio_trash leaves an entry in its config file for us.
758 KConfig trashConfig( "trashrc", KConfig::SimpleConfig );
759 if ( trashConfig.group("Status").readEntry( "Empty", true ) ) {
760 return emptyIcon;
764 } else if ( group.hasKey( "Exec" ) && !KDesktopFile::isAuthorizedDesktopFile( path ) ) {
765 // Disable custom icons for untrusted executables
766 return QString("application-x-desktop");
768 return icon;
771 QString KFileItem::iconName() const
773 if (d->m_useIconNameCache && !d->m_iconName.isEmpty()) {
774 return d->m_iconName;
777 d->m_iconName = d->m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME );
778 if (!d->m_iconName.isEmpty()) {
779 d->m_useIconNameCache = d->m_bMimeTypeKnown;
780 return d->m_iconName;
783 bool isLocalUrl;
784 KUrl url = mostLocalUrl(isLocalUrl);
786 KMimeType::Ptr mime = mimeTypePtr();
787 if (isLocalUrl && mime->is("application/x-desktop")) {
788 d->m_iconName = iconFromDesktopFile(url.toLocalFile());
789 if (!d->m_iconName.isEmpty()) {
790 d->m_useIconNameCache = d->m_bMimeTypeKnown;
791 return d->m_iconName;
795 // KDE5: handle .directory files here too, and get rid of
796 // KFolderMimeType and the url argument in KMimeType::iconName().
798 d->m_iconName = mime->iconName(url);
799 d->m_useIconNameCache = d->m_bMimeTypeKnown;
800 //kDebug() << "finding icon for" << url << ":" << d->m_iconName;
801 return d->m_iconName;
804 QStringList KFileItem::overlays() const
806 QStringList names;
807 if ( d->m_bLink ) {
808 names.append("emblem-symbolic-link");
811 if ( !S_ISDIR( d->m_fileMode ) // Locked dirs have a special icon, use the overlay for files only
812 && !isReadable()) {
813 names.append("object-locked");
816 if ( isHidden() ) {
817 names.append("hidden");
820 #ifndef Q_OS_WIN
821 if( S_ISDIR( d->m_fileMode ) && d->m_bIsLocalUrl)
823 if (KSambaShare::instance()->isDirectoryShared( d->m_url.toLocalFile() ) ||
824 KNFSShare::instance()->isDirectoryShared( d->m_url.toLocalFile() ))
826 //kDebug() << d->m_url.path();
827 names.append("network-workgroup");
830 #endif // Q_OS_WIN
832 if ( d->m_pMimeType && d->m_url.fileName().endsWith( QLatin1String( ".gz" ) ) &&
833 d->m_pMimeType->is("application/x-gzip") ) {
834 names.append("application-zip");
837 return names;
840 // ## where is this used?
841 QPixmap KFileItem::pixmap( int _size, int _state ) const
843 const QString iconName = d->m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME );
844 if ( !iconName.isEmpty() )
845 return DesktopIcon(iconName, _size, _state);
847 if (!d->m_pMimeType) {
848 // No mimetype determined yet, go for a fast default icon
849 if (S_ISDIR(d->m_fileMode)) {
850 static const QString * defaultFolderIcon = 0;
851 if ( !defaultFolderIcon ) {
852 const KMimeType::Ptr mimeType = KMimeType::mimeType( "inode/directory" );
853 if ( mimeType )
854 defaultFolderIcon = &KGlobal::staticQString( mimeType->iconName() );
855 else
856 kWarning(7000) << "No mimetype for inode/directory could be found. Check your installation.";
858 if ( defaultFolderIcon )
859 return DesktopIcon( *defaultFolderIcon, _size, _state );
862 return DesktopIcon( "unknown", _size, _state );
865 KMimeType::Ptr mime;
866 // Use guessed mimetype if the main one hasn't been determined for sure
867 if ( !d->m_bMimeTypeKnown && !d->m_guessedMimeType.isEmpty() )
868 mime = KMimeType::mimeType( d->m_guessedMimeType );
869 else
870 mime = d->m_pMimeType;
872 // Support for gzipped files: extract mimetype of contained file
873 // See also the relevant code in overlays, which adds the zip overlay.
874 if ( mime->name() == "application/x-gzip" && d->m_url.fileName().endsWith( QLatin1String( ".gz" ) ) )
876 KUrl sf;
877 sf.setPath( d->m_url.path().left( d->m_url.path().length() - 3 ) );
878 //kDebug() << "subFileName=" << subFileName;
879 mime = KMimeType::findByUrl( sf, 0, d->m_bIsLocalUrl );
882 bool isLocalUrl;
883 KUrl url = mostLocalUrl(isLocalUrl);
885 QPixmap p = KIconLoader::global()->loadMimeTypeIcon( mime->iconName( url ), KIconLoader::Desktop, _size, _state );
886 //kDebug() << "finding pixmap for " << url.url() << " : " << mime->name();
887 if (p.isNull())
888 kWarning() << "Pixmap not found for mimetype " << d->m_pMimeType->name();
890 return p;
893 bool KFileItem::isReadable() const
896 struct passwd * user = getpwuid( geteuid() );
897 bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user);
898 // This gets ugly for the group....
899 // Maybe we want a static QString for the user and a static QStringList
900 // for the groups... then we need to handle the deletion properly...
903 if (d->m_permissions != KFileItem::Unknown) {
904 // No read permission at all
905 if ( !(S_IRUSR & d->m_permissions) && !(S_IRGRP & d->m_permissions) && !(S_IROTH & d->m_permissions) )
906 return false;
908 // Read permissions for all: save a stat call
909 if ( (S_IRUSR|S_IRGRP|S_IROTH) & d->m_permissions )
910 return true;
913 // Or if we can't read it [using ::access()] - not network transparent
914 if ( d->m_bIsLocalUrl && KDE::access( d->m_url.toLocalFile(), R_OK ) == -1 )
915 return false;
917 return true;
920 bool KFileItem::isWritable() const
923 struct passwd * user = getpwuid( geteuid() );
924 bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user);
925 // This gets ugly for the group....
926 // Maybe we want a static QString for the user and a static QStringList
927 // for the groups... then we need to handle the deletion properly...
930 if (d->m_permissions != KFileItem::Unknown) {
931 // No write permission at all
932 if ( !(S_IWUSR & d->m_permissions) && !(S_IWGRP & d->m_permissions) && !(S_IWOTH & d->m_permissions) )
933 return false;
936 // Or if we can't read it [using ::access()] - not network transparent
937 if ( d->m_bIsLocalUrl && KDE::access( d->m_url.toLocalFile(), W_OK ) == -1 )
938 return false;
940 return true;
943 bool KFileItem::isHidden() const
945 // The kioslave can specify explicitly that a file is hidden or shown
946 if ( d->m_hidden != KFileItemPrivate::Auto )
947 return d->m_hidden == KFileItemPrivate::Hidden;
949 // Prefer the filename that is part of the URL, in case the display name is different.
950 QString fileName = d->m_url.fileName();
951 if (fileName.isEmpty()) // e.g. "trash:/"
952 fileName = d->m_strName;
953 return fileName.length() > 1 && fileName[0] == '.'; // Just "." is current directory, not hidden.
956 bool KFileItem::isDir() const
958 if (d->m_fileMode == KFileItem::Unknown) {
959 // Probably the file was deleted already, and KDirLister hasn't told the world yet.
960 //kDebug() << d << url() << "can't say -> false";
961 return false; // can't say for sure, so no
963 return (S_ISDIR(d->m_fileMode));
966 bool KFileItem::isFile() const
968 return !isDir();
971 bool KFileItem::acceptsDrops() const
973 // A directory ?
974 if ( S_ISDIR( mode() ) ) {
975 return isWritable();
978 // But only local .desktop files and executables
979 if ( !d->m_bIsLocalUrl )
980 return false;
982 if ( mimetype() == "application/x-desktop")
983 return true;
985 // Executable, shell script ... ?
986 if ( QFileInfo(d->m_url.toLocalFile()).isExecutable() )
987 return true;
989 return false;
992 QString KFileItem::getStatusBarInfo() const
994 QString text = d->m_strText;
995 const QString comment = mimeComment();
997 if ( d->m_bLink )
999 text += ' ';
1000 if ( comment.isEmpty() )
1001 text += i18n ( "(Symbolic Link to %1)", linkDest() );
1002 else
1003 text += i18n("(%1, Link to %2)", comment, linkDest());
1005 else if ( targetUrl() != url() )
1007 text += i18n ( " (Points to %1)", targetUrl().pathOrUrl());
1009 else if ( S_ISREG( d->m_fileMode ) )
1011 text += QString(" (%1, %2)").arg( comment, KIO::convertSize( size() ) );
1013 else
1015 text += QString(" (%1)").arg( comment );
1017 return text;
1020 QString KFileItem::getToolTipText(int maxcount) const
1022 // we can return QString() if no tool tip should be shown
1023 QString tip;
1024 KFileMetaInfo info = metaInfo();
1026 // the font tags are a workaround for the fact that the tool tip gets
1027 // screwed if the color scheme uses white as default text color
1028 const QString colorName = QApplication::palette().color(QPalette::ToolTipText).name();
1029 const QString start = "<tr><td align=\"right\"><nobr><font color=\"" + colorName + "\"><b>";
1030 const QString mid = "&nbsp;</b></font></nobr></td><td><nobr><font color=\"" + colorName + "\">";
1031 const char* end = "</font></nobr></td></tr>";
1033 tip = "<table cellspacing=0 cellpadding=0>";
1035 tip += start + i18n("Name:") + mid + text() + end;
1036 tip += start + i18n("Type:") + mid;
1038 QString type = Qt::escape(mimeComment());
1039 if ( d->m_bLink ) {
1040 tip += i18n("Link to %1 (%2)", linkDest(), type) + end;
1041 } else
1042 tip += type + end;
1044 if ( !S_ISDIR ( d->m_fileMode ) )
1045 tip += start + i18n("Size:") + mid +
1046 QString("%1").arg(KIO::convertSize(size())) +
1047 end;
1049 tip += start + i18n("Modified:") + mid +
1050 timeString( KFileItem::ModificationTime ) + end
1051 #ifndef Q_WS_WIN //TODO: show win32-specific permissions
1052 +start + i18n("Owner:") + mid + user() + " - " + group() + end +
1053 start + i18n("Permissions:") + mid +
1054 permissionsString() + end
1055 #endif
1058 if (info.isValid())
1060 const QStringList keys = info.preferredKeys();
1062 // now the rest
1063 QStringList::ConstIterator it = keys.begin();
1064 for (int count = 0; count<maxcount && it!=keys.end() ; ++it)
1066 if ( count == 0 )
1068 tip += "<tr><td colspan=2><center><s>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</s></center></td></tr>";
1071 KFileMetaInfoItem item = info.item( *it );
1072 if ( item.isValid() )
1074 QString s = item.value().toString();
1075 if ( ( item.properties().attributes() & PredicateProperties::SqueezeText )
1076 && s.length() > 50) {
1077 s.truncate(47);
1078 s.append("...");
1080 if ( !s.isEmpty() )
1082 count++;
1083 tip += start +
1084 Qt::escape( item.name() ) + ':' +
1085 mid +
1086 Qt::escape( s ) +
1087 end;
1093 tip += "</table>";
1095 //kDebug() << "making this the tool tip rich text:\n";
1096 //kDebug() << tip;
1098 return tip;
1101 void KFileItem::run( QWidget* parentWidget ) const
1103 (void) new KRun( targetUrl(), parentWidget, d->m_fileMode, d->m_bIsLocalUrl );
1106 bool KFileItem::cmp( const KFileItem & item ) const
1108 return d->cmp(*item.d);
1111 bool KFileItem::operator==(const KFileItem& other) const
1113 // is this enough?
1114 return d == other.d;
1117 bool KFileItem::operator!=(const KFileItem& other) const
1119 return d != other.d;
1122 void KFileItem::setUDSEntry( const KIO::UDSEntry& _entry, const KUrl& _url,
1123 bool _delayedMimeTypes, bool _urlIsDirectory )
1125 d->m_entry = _entry;
1126 d->m_url = _url;
1127 d->m_strName.clear();
1128 d->m_strText.clear();
1129 d->m_iconName.clear();
1130 d->m_strLowerCaseName.clear();
1131 d->m_pMimeType = 0;
1132 d->m_fileMode = KFileItem::Unknown;
1133 d->m_permissions = KFileItem::Unknown;
1134 d->m_bMarked = false;
1135 d->m_bLink = false;
1136 d->m_bIsLocalUrl = _url.isLocalFile();
1137 d->m_bMimeTypeKnown = false;
1138 d->m_hidden = KFileItemPrivate::Auto;
1139 d->m_guessedMimeType.clear();
1140 d->m_metaInfo = KFileMetaInfo();
1141 d->m_delayedMimeTypes = _delayedMimeTypes;
1142 d->m_useIconNameCache = false;
1144 d->readUDSEntry( _urlIsDirectory );
1145 d->init();
1148 KFileItem::operator QVariant() const
1150 return qVariantFromValue(*this);
1153 void KFileItem::setExtraData( const void *key, void *value )
1155 if ( !key )
1156 return;
1158 d->m_extra.insert( key, value ); // replaces the value of key if already there
1161 const void * KFileItem::extraData( const void *key ) const
1163 return d->m_extra.value( key, 0 );
1166 void KFileItem::removeExtraData( const void *key )
1168 d->m_extra.remove( key );
1171 QString KFileItem::permissionsString() const
1173 if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown)
1174 d->m_access = d->parsePermissions( d->m_permissions );
1176 return d->m_access;
1179 // check if we need to cache this
1180 QString KFileItem::timeString( FileTimes which ) const
1182 return KGlobal::locale()->formatDateTime( d->time(which) );
1185 QString KFileItem::timeString( unsigned int which ) const
1187 switch (which) {
1188 case KIO::UDSEntry::UDS_ACCESS_TIME:
1189 return timeString(AccessTime);
1190 case KIO::UDSEntry::UDS_CREATION_TIME:
1191 return timeString(CreationTime);
1192 case KIO::UDSEntry::UDS_MODIFICATION_TIME:
1193 default:
1194 return timeString(ModificationTime);
1198 void KFileItem::setMetaInfo( const KFileMetaInfo & info ) const
1200 d->m_metaInfo = info;
1203 KFileMetaInfo KFileItem::metaInfo(bool autoget, int) const
1205 if (autoget && !d->m_metaInfo.isValid())
1207 bool isLocalUrl;
1208 KUrl url(mostLocalUrl(isLocalUrl));
1209 if (KGlobalSettings::showFilePreview(url))
1210 d->m_metaInfo = KFileMetaInfo(url);//, mimetype() );
1212 return d->m_metaInfo;
1215 void KFileItem::assign( const KFileItem & item )
1217 *this = item;
1220 KUrl KFileItem::mostLocalUrl(bool &local) const
1222 QString local_path = localPath();
1224 if ( !local_path.isEmpty() )
1226 local = true;
1227 KUrl url;
1228 url.setPath(local_path);
1229 return url;
1231 else
1233 local = d->m_bIsLocalUrl;
1234 return d->m_url;
1238 QDataStream & operator<< ( QDataStream & s, const KFileItem & a )
1240 // We don't need to save/restore anything that refresh() invalidates,
1241 // since that means we can re-determine those by ourselves.
1242 s << a.d->m_url;
1243 s << a.d->m_strName;
1244 s << a.d->m_strText;
1245 return s;
1248 QDataStream & operator>> ( QDataStream & s, KFileItem & a )
1250 s >> a.d->m_url;
1251 s >> a.d->m_strName;
1252 s >> a.d->m_strText;
1253 a.d->m_bIsLocalUrl = a.d->m_url.isLocalFile();
1254 a.d->m_bMimeTypeKnown = false;
1255 a.refresh();
1256 return s;
1259 KUrl KFileItem::url() const
1261 return d->m_url;
1264 mode_t KFileItem::permissions() const
1266 return d->m_permissions;
1269 mode_t KFileItem::mode() const
1271 return d->m_fileMode;
1274 bool KFileItem::isLink() const
1276 return d->m_bLink;
1279 bool KFileItem::isLocalFile() const
1281 return d->m_bIsLocalUrl;
1284 QString KFileItem::text() const
1286 return d->m_strText;
1289 QString KFileItem::name( bool lowerCase ) const
1291 if ( !lowerCase )
1292 return d->m_strName;
1293 else
1294 if ( d->m_strLowerCaseName.isNull() )
1295 d->m_strLowerCaseName = d->m_strName.toLower();
1296 return d->m_strLowerCaseName;
1299 KUrl KFileItem::targetUrl() const
1301 const QString targetUrlStr = d->m_entry.stringValue( KIO::UDSEntry::UDS_TARGET_URL );
1302 if (!targetUrlStr.isEmpty())
1303 return KUrl(targetUrlStr);
1304 else
1305 return url();
1309 * Mimetype handling.
1311 * Initial state: m_pMimeType = 0.
1312 * When mimeTypePtr() is called first: fast mimetype determination,
1313 * might either find an accurate mimetype (-> Final state), otherwise we
1314 * set m_pMimeType but not m_bMimeTypeKnown (-> Intermediate state)
1315 * Intermediate state: determineMimeType() does the real determination -> Final state.
1317 * If delayedMimeTypes isn't set, then we always go to the Final state directly.
1320 KMimeType::Ptr KFileItem::mimeTypePtr() const
1322 if (!d->m_pMimeType) {
1323 // On-demand fast (but not always accurate) mimetype determination
1324 Q_ASSERT(!d->m_url.isEmpty());
1325 bool isLocalUrl;
1326 KUrl url = mostLocalUrl(isLocalUrl);
1327 int accuracy;
1328 d->m_pMimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl,
1329 // use fast mode if delayed mimetype determination can refine it later
1330 d->m_delayedMimeTypes, &accuracy );
1331 // If we used the "fast mode" (no sniffing), and we didn't get a perfect (extension-based) match,
1332 // then determineMimeType will be able to do better.
1333 const bool canDoBetter = d->m_delayedMimeTypes && accuracy < 100;
1334 //kDebug() << "finding mimetype for" << url << ":" << d->m_pMimeType->name() << "canDoBetter=" << canDoBetter;
1335 d->m_bMimeTypeKnown = !canDoBetter;
1337 return d->m_pMimeType;
1340 KIO::UDSEntry KFileItem::entry() const
1342 return d->m_entry;
1345 bool KFileItem::isMarked() const
1347 return d->m_bMarked;
1350 void KFileItem::mark()
1352 d->m_bMarked = true;
1355 void KFileItem::unmark()
1357 d->m_bMarked = false;
1360 KFileItem& KFileItem::operator=(const KFileItem& other)
1362 d = other.d;
1363 return *this;
1366 bool KFileItem::isNull() const
1368 return d == 0;
1371 KFileItemList::KFileItemList()
1375 KFileItemList::KFileItemList( const QList<KFileItem> &items )
1376 : QList<KFileItem>( items )
1380 KFileItem KFileItemList::findByName( const QString& fileName ) const
1382 const_iterator it = begin();
1383 const const_iterator itend = end();
1384 for ( ; it != itend ; ++it ) {
1385 if ( (*it).name() == fileName ) {
1386 return *it;
1389 return KFileItem();
1392 KFileItem KFileItemList::findByUrl( const KUrl& url ) const {
1393 const_iterator it = begin();
1394 const const_iterator itend = end();
1395 for ( ; it != itend ; ++it ) {
1396 if ( (*it).url() == url ) {
1397 return *it;
1400 return KFileItem();
1403 KUrl::List KFileItemList::urlList() const {
1404 KUrl::List lst;
1405 const_iterator it = begin();
1406 const const_iterator itend = end();
1407 for ( ; it != itend ; ++it ) {
1408 lst.append( (*it).url() );
1410 return lst;
1413 KUrl::List KFileItemList::targetUrlList() const {
1414 KUrl::List lst;
1415 const_iterator it = begin();
1416 const const_iterator itend = end();
1417 for ( ; it != itend ; ++it ) {
1418 lst.append( (*it).targetUrl() );
1420 return lst;
1423 bool KFileItem::isDesktopFile() const
1425 // only local files
1426 bool isLocal;
1427 const KUrl url = mostLocalUrl(isLocal);
1428 if (!isLocal)
1429 return false;
1431 // only regular files
1432 if (!S_ISREG(d->m_fileMode))
1433 return false;
1435 // only if readable
1436 if (!isReadable())
1437 return false;
1439 // return true if desktop file
1440 return determineMimeType()->is("application/x-desktop");