File indexing completed on 2024-12-01 12:35:59

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2007 Norbert Frese <nf2@scheinwelt.at>
0005     SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
0006     SPDX-FileCopyrightText: 2013-2014 Frank Reininghaus <frank78ac@googlemail.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "udsentry.h"
0012 
0013 #include "../utils_p.h"
0014 
0015 #include <QDataStream>
0016 #include <QDebug>
0017 #include <QString>
0018 #include <QVector>
0019 
0020 #include <KUser>
0021 
0022 using namespace KIO;
0023 
0024 // BEGIN UDSEntryPrivate
0025 /* ---------- UDSEntryPrivate ------------ */
0026 
0027 class KIO::UDSEntryPrivate : public QSharedData
0028 {
0029 public:
0030     void reserve(int size);
0031     void insert(uint udsField, const QString &value);
0032     void replace(uint udsField, const QString &value);
0033     void insert(uint udsField, long long value);
0034     void replace(uint udsField, long long value);
0035     int count() const;
0036     QString stringValue(uint udsField) const;
0037     long long numberValue(uint udsField, long long defaultValue = -1) const;
0038 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 8)
0039     QList<uint> listFields() const;
0040 #endif
0041     QVector<uint> fields() const;
0042     bool contains(uint udsField) const;
0043     void clear();
0044     void save(QDataStream &s) const;
0045     void load(QDataStream &s);
0046     void debugUDSEntry(QDebug &stream) const;
0047     /**
0048      * @param field numeric UDS field id
0049      * @return the name of the field
0050      */
0051     static QString nameOfUdsField(uint field);
0052 
0053 private:
0054     struct Field {
0055         inline Field()
0056         {
0057         }
0058         inline Field(const uint index, const QString &value)
0059             : m_str(value)
0060             , m_index(index)
0061         {
0062         }
0063         inline Field(const uint index, long long value = 0)
0064             : m_long(value)
0065             , m_index(index)
0066         {
0067         }
0068 
0069         QString m_str;
0070         long long m_long = LLONG_MIN;
0071         uint m_index = 0;
0072     };
0073     std::vector<Field> storage;
0074 };
0075 
0076 void UDSEntryPrivate::reserve(int size)
0077 {
0078     storage.reserve(size);
0079 }
0080 
0081 void UDSEntryPrivate::insert(uint udsField, const QString &value)
0082 {
0083     Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING);
0084     Q_ASSERT(std::find_if(storage.cbegin(),
0085                           storage.cend(),
0086                           [udsField](const Field &entry) {
0087                               return entry.m_index == udsField;
0088                           })
0089              == storage.cend());
0090     storage.emplace_back(udsField, value);
0091 }
0092 
0093 void UDSEntryPrivate::replace(uint udsField, const QString &value)
0094 {
0095     Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING);
0096     auto it = std::find_if(storage.begin(), storage.end(), [udsField](const Field &entry) {
0097         return entry.m_index == udsField;
0098     });
0099     if (it != storage.end()) {
0100         it->m_str = value;
0101         return;
0102     }
0103     storage.emplace_back(udsField, value);
0104 }
0105 
0106 void UDSEntryPrivate::insert(uint udsField, long long value)
0107 {
0108     Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER);
0109     Q_ASSERT(std::find_if(storage.cbegin(),
0110                           storage.cend(),
0111                           [udsField](const Field &entry) {
0112                               return entry.m_index == udsField;
0113                           })
0114              == storage.cend());
0115     storage.emplace_back(udsField, value);
0116 }
0117 
0118 void UDSEntryPrivate::replace(uint udsField, long long value)
0119 {
0120     Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER);
0121     auto it = std::find_if(storage.begin(), storage.end(), [udsField](const Field &entry) {
0122         return entry.m_index == udsField;
0123     });
0124     if (it != storage.end()) {
0125         it->m_long = value;
0126         return;
0127     }
0128     storage.emplace_back(udsField, value);
0129 }
0130 
0131 int UDSEntryPrivate::count() const
0132 {
0133     return storage.size();
0134 }
0135 
0136 QString UDSEntryPrivate::stringValue(uint udsField) const
0137 {
0138     auto it = std::find_if(storage.cbegin(), storage.cend(), [udsField](const Field &entry) {
0139         return entry.m_index == udsField;
0140     });
0141     if (it != storage.cend()) {
0142         return it->m_str;
0143     }
0144     return QString();
0145 }
0146 
0147 long long UDSEntryPrivate::numberValue(uint udsField, long long defaultValue) const
0148 {
0149     auto it = std::find_if(storage.cbegin(), storage.cend(), [udsField](const Field &entry) {
0150         return entry.m_index == udsField;
0151     });
0152     if (it != storage.cend()) {
0153         return it->m_long;
0154     }
0155     return defaultValue;
0156 }
0157 
0158 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 8)
0159 QList<uint> UDSEntryPrivate::listFields() const
0160 {
0161     QList<uint> res;
0162     res.reserve(storage.size());
0163     for (const Field &field : storage) {
0164         res.append(field.m_index);
0165     }
0166     return res;
0167 }
0168 #endif
0169 
0170 QVector<uint> UDSEntryPrivate::fields() const
0171 {
0172     QVector<uint> res;
0173     res.reserve(storage.size());
0174     for (const Field &field : storage) {
0175         res.append(field.m_index);
0176     }
0177     return res;
0178 }
0179 
0180 bool UDSEntryPrivate::contains(uint udsField) const
0181 {
0182     auto it = std::find_if(storage.cbegin(), storage.cend(), [udsField](const Field &entry) {
0183         return entry.m_index == udsField;
0184     });
0185     return (it != storage.cend());
0186 }
0187 
0188 void UDSEntryPrivate::clear()
0189 {
0190     storage.clear();
0191 }
0192 
0193 void UDSEntryPrivate::save(QDataStream &s) const
0194 {
0195     s << static_cast<quint32>(storage.size());
0196 
0197     for (const Field &field : storage) {
0198         uint uds = field.m_index;
0199         s << uds;
0200 
0201         if (uds & KIO::UDSEntry::UDS_STRING) {
0202             s << field.m_str;
0203         } else if (uds & KIO::UDSEntry::UDS_NUMBER) {
0204             s << field.m_long;
0205         } else {
0206             Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type");
0207         }
0208     }
0209 }
0210 
0211 void UDSEntryPrivate::load(QDataStream &s)
0212 {
0213     clear();
0214 
0215     quint32 size;
0216     s >> size;
0217     reserve(size);
0218 
0219     // We cache the loaded strings. Some of them, like, e.g., the user,
0220     // will often be the same for many entries in a row. Caching them
0221     // permits to use implicit sharing to save memory.
0222     thread_local QVector<QString> cachedStrings;
0223     if (quint32(cachedStrings.size()) < size) {
0224         cachedStrings.resize(size);
0225     }
0226 
0227     for (quint32 i = 0; i < size; ++i) {
0228         quint32 uds;
0229         s >> uds;
0230 
0231         if (uds & KIO::UDSEntry::UDS_STRING) {
0232             // If the QString is the same like the one we read for the
0233             // previous UDSEntry at the i-th position, use an implicitly
0234             // shared copy of the same QString to save memory.
0235             QString buffer;
0236             s >> buffer;
0237 
0238             if (buffer != cachedStrings.at(i)) {
0239                 cachedStrings[i] = buffer;
0240             }
0241 
0242             insert(uds, cachedStrings.at(i));
0243         } else if (uds & KIO::UDSEntry::UDS_NUMBER) {
0244             long long value;
0245             s >> value;
0246             insert(uds, value);
0247         } else {
0248             Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type");
0249         }
0250     }
0251 }
0252 
0253 QString UDSEntryPrivate::nameOfUdsField(uint field)
0254 {
0255     switch (field) {
0256     case UDSEntry::UDS_SIZE:
0257         return QStringLiteral("UDS_SIZE");
0258     case UDSEntry::UDS_SIZE_LARGE:
0259         return QStringLiteral("UDS_SIZE_LARGE");
0260     case UDSEntry::UDS_USER:
0261         return QStringLiteral("UDS_USER");
0262     case UDSEntry::UDS_ICON_NAME:
0263         return QStringLiteral("UDS_ICON_NAME");
0264     case UDSEntry::UDS_GROUP:
0265         return QStringLiteral("UDS_GROUP");
0266     case UDSEntry::UDS_NAME:
0267         return QStringLiteral("UDS_NAME");
0268     case UDSEntry::UDS_LOCAL_PATH:
0269         return QStringLiteral("UDS_LOCAL_PATH");
0270     case UDSEntry::UDS_HIDDEN:
0271         return QStringLiteral("UDS_HIDDEN");
0272     case UDSEntry::UDS_ACCESS:
0273         return QStringLiteral("UDS_ACCESS");
0274     case UDSEntry::UDS_MODIFICATION_TIME:
0275         return QStringLiteral("UDS_MODIFICATION_TIME");
0276     case UDSEntry::UDS_ACCESS_TIME:
0277         return QStringLiteral("UDS_ACCESS_TIME");
0278     case UDSEntry::UDS_CREATION_TIME:
0279         return QStringLiteral("UDS_CREATION_TIME");
0280     case UDSEntry::UDS_FILE_TYPE:
0281         return QStringLiteral("UDS_FILE_TYPE");
0282     case UDSEntry::UDS_LINK_DEST:
0283         return QStringLiteral("UDS_LINK_DEST");
0284     case UDSEntry::UDS_URL:
0285         return QStringLiteral("UDS_URL");
0286     case UDSEntry::UDS_MIME_TYPE:
0287         return QStringLiteral("UDS_MIME_TYPE");
0288     case UDSEntry::UDS_GUESSED_MIME_TYPE:
0289         return QStringLiteral("UDS_GUESSED_MIME_TYPE");
0290     case UDSEntry::UDS_XML_PROPERTIES:
0291         return QStringLiteral("UDS_XML_PROPERTIES");
0292     case UDSEntry::UDS_EXTENDED_ACL:
0293         return QStringLiteral("UDS_EXTENDED_ACL");
0294     case UDSEntry::UDS_ACL_STRING:
0295         return QStringLiteral("UDS_ACL_STRING");
0296     case UDSEntry::UDS_DEFAULT_ACL_STRING:
0297         return QStringLiteral("UDS_DEFAULT_ACL_STRING");
0298     case UDSEntry::UDS_DISPLAY_NAME:
0299         return QStringLiteral("UDS_DISPLAY_NAME");
0300     case UDSEntry::UDS_TARGET_URL:
0301         return QStringLiteral("UDS_TARGET_URL");
0302     case UDSEntry::UDS_DISPLAY_TYPE:
0303         return QStringLiteral("UDS_DISPLAY_TYPE");
0304     case UDSEntry::UDS_ICON_OVERLAY_NAMES:
0305         return QStringLiteral("UDS_ICON_OVERLAY_NAMES");
0306     case UDSEntry::UDS_COMMENT:
0307         return QStringLiteral("UDS_COMMENT");
0308     case UDSEntry::UDS_DEVICE_ID:
0309         return QStringLiteral("UDS_DEVICE_ID");
0310     case UDSEntry::UDS_INODE:
0311         return QStringLiteral("UDS_INODE");
0312     case UDSEntry::UDS_EXTRA:
0313         return QStringLiteral("UDS_EXTRA");
0314     case UDSEntry::UDS_EXTRA_END:
0315         return QStringLiteral("UDS_EXTRA_END");
0316     default:
0317         return QStringLiteral("Unknown uds field %1").arg(field);
0318     }
0319 }
0320 
0321 void UDSEntryPrivate::debugUDSEntry(QDebug &stream) const
0322 {
0323     QDebugStateSaver saver(stream);
0324     stream.nospace() << "[";
0325     for (const Field &field : storage) {
0326         stream << " " << nameOfUdsField(field.m_index) << "=";
0327         if (field.m_index & KIO::UDSEntry::UDS_STRING) {
0328             stream << field.m_str;
0329         } else if (field.m_index & KIO::UDSEntry::UDS_NUMBER) {
0330             stream << field.m_long;
0331         } else {
0332             Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type");
0333         }
0334     }
0335     stream << " ]";
0336 }
0337 // END UDSEntryPrivate
0338 
0339 // BEGIN UDSEntry
0340 /* ---------- UDSEntry ------------ */
0341 
0342 UDSEntry::UDSEntry()
0343     : d(new UDSEntryPrivate())
0344 {
0345 }
0346 
0347 // BUG: this API doesn't allow to handle symlinks correctly (we need buff from QT_LSTAT for most things, but buff from QT_STAT for st_mode and st_size)
0348 UDSEntry::UDSEntry(const QT_STATBUF &buff, const QString &name)
0349     : d(new UDSEntryPrivate())
0350 {
0351 #ifndef Q_OS_WIN
0352     d->reserve(10);
0353 #else
0354     d->reserve(8);
0355 #endif
0356     d->insert(UDS_NAME, name);
0357     d->insert(UDS_SIZE, buff.st_size);
0358     d->insert(UDS_DEVICE_ID, buff.st_dev);
0359     d->insert(UDS_INODE, buff.st_ino);
0360     d->insert(UDS_FILE_TYPE, buff.st_mode & QT_STAT_MASK); // extract file type
0361     d->insert(UDS_ACCESS, buff.st_mode & 07777); // extract permissions
0362     d->insert(UDS_MODIFICATION_TIME, buff.st_mtime);
0363     d->insert(UDS_ACCESS_TIME, buff.st_atime);
0364 #ifndef Q_OS_WIN
0365     d->insert(UDS_USER, KUser(buff.st_uid).loginName());
0366     d->insert(UDS_GROUP, KUserGroup(buff.st_gid).name());
0367 #endif
0368 }
0369 
0370 UDSEntry::UDSEntry(const UDSEntry &) = default;
0371 UDSEntry::~UDSEntry() = default;
0372 UDSEntry::UDSEntry(UDSEntry &&) = default;
0373 UDSEntry &UDSEntry::operator=(const UDSEntry &) = default;
0374 UDSEntry &UDSEntry::operator=(UDSEntry &&) = default;
0375 
0376 QString UDSEntry::stringValue(uint field) const
0377 {
0378     return d->stringValue(field);
0379 }
0380 
0381 long long UDSEntry::numberValue(uint field, long long defaultValue) const
0382 {
0383     return d->numberValue(field, defaultValue);
0384 }
0385 
0386 bool UDSEntry::isDir() const
0387 {
0388     return Utils::isDirMask(numberValue(UDS_FILE_TYPE));
0389 }
0390 
0391 bool UDSEntry::isLink() const
0392 {
0393     return !stringValue(UDS_LINK_DEST).isEmpty();
0394 }
0395 
0396 void UDSEntry::reserve(int size)
0397 {
0398     d->reserve(size);
0399 }
0400 
0401 void UDSEntry::fastInsert(uint field, const QString &value)
0402 {
0403     d->insert(field, value);
0404 }
0405 
0406 void UDSEntry::fastInsert(uint field, long long value)
0407 {
0408     d->insert(field, value);
0409 }
0410 
0411 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 48)
0412 void UDSEntry::insert(uint field, const QString &value)
0413 {
0414     d->replace(field, value);
0415 }
0416 #endif
0417 
0418 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 48)
0419 void UDSEntry::insert(uint field, long long value)
0420 {
0421     d->replace(field, value);
0422 }
0423 #endif
0424 
0425 void UDSEntry::replace(uint field, const QString &value)
0426 {
0427     d->replace(field, value);
0428 }
0429 
0430 void UDSEntry::replace(uint field, long long value)
0431 {
0432     d->replace(field, value);
0433 }
0434 
0435 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 8)
0436 QList<uint> UDSEntry::listFields() const
0437 {
0438     return d->listFields();
0439 }
0440 #endif
0441 
0442 QVector<uint> UDSEntry::fields() const
0443 {
0444     return d->fields();
0445 }
0446 
0447 int UDSEntry::count() const
0448 {
0449     return d->count();
0450 }
0451 
0452 bool UDSEntry::contains(uint field) const
0453 {
0454     return d->contains(field);
0455 }
0456 
0457 void UDSEntry::clear()
0458 {
0459     d->clear();
0460 }
0461 // END UDSEntry
0462 
0463 KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry)
0464 {
0465     entry.d->debugUDSEntry(stream);
0466     return stream;
0467 }
0468 
0469 KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KIO::UDSEntry &a)
0470 {
0471     a.d->save(s);
0472     return s;
0473 }
0474 
0475 KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KIO::UDSEntry &a)
0476 {
0477     a.d->load(s);
0478     return s;
0479 }
0480 
0481 KIOCORE_EXPORT bool operator==(const KIO::UDSEntry &entry, const KIO::UDSEntry &other)
0482 {
0483     if (entry.count() != other.count()) {
0484         return false;
0485     }
0486 
0487     const QVector<uint> fields = entry.fields();
0488     for (uint field : fields) {
0489         if (!other.contains(field)) {
0490             return false;
0491         }
0492 
0493         if (field & UDSEntry::UDS_STRING) {
0494             if (entry.stringValue(field) != other.stringValue(field)) {
0495                 return false;
0496             }
0497         } else {
0498             if (entry.numberValue(field) != other.numberValue(field)) {
0499                 return false;
0500             }
0501         }
0502     }
0503 
0504     return true;
0505 }
0506 
0507 KIOCORE_EXPORT bool operator!=(const KIO::UDSEntry &entry, const KIO::UDSEntry &other)
0508 {
0509     return !(entry == other);
0510 }