diff --git a/src/core/authinfo.cpp b/src/core/authinfo.cpp index e7d0bb53..b76e6c85 100644 --- a/src/core/authinfo.cpp +++ b/src/core/authinfo.cpp @@ -1,481 +1,481 @@ /* * This file is part of the KDE libraries * Copyright (C) 2000-2001 Dawit Alemayehu * * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "authinfo.h" #include #include #include #include #include #include #include #include using namespace KIO; ////// class ExtraField { public: ExtraField() : flags(AuthInfo::ExtraFieldNoFlags) { } ExtraField(const ExtraField &other) : customTitle(other.customTitle), flags(other.flags), value(other.value) { } ExtraField &operator=(const ExtraField &other) { customTitle = other.customTitle; flags = other.flags; value = other.value; return *this; } QString customTitle; // reserved for future use AuthInfo::FieldFlags flags; QVariant value; }; Q_DECLARE_METATYPE(ExtraField) static QDataStream &operator<< (QDataStream &s, const ExtraField &extraField) { s << extraField.customTitle; s << static_cast(extraField.flags); s << extraField.value; return s; } static QDataStream &operator>> (QDataStream &s, ExtraField &extraField) { s >> extraField.customTitle; int i; s >> i; extraField.flags = AuthInfo::FieldFlags(i); s >> extraField.value; return s; } static QDBusArgument &operator<<(QDBusArgument &argument, const ExtraField &extraField) { argument.beginStructure(); argument << extraField.customTitle << static_cast(extraField.flags) << QDBusVariant(extraField.value); argument.endStructure(); return argument; } static const QDBusArgument &operator>>(const QDBusArgument &argument, ExtraField &extraField) { QDBusVariant value; int flag; argument.beginStructure(); argument >> extraField.customTitle >> flag >> value; argument.endStructure(); extraField.value = value.variant(); extraField.flags = KIO::AuthInfo::FieldFlags(flag); return argument; } class KIO::AuthInfoPrivate { public: QMap extraFields; }; ////// AuthInfo::AuthInfo() : d(new AuthInfoPrivate()) { modified = false; readOnly = false; verifyPath = false; keepPassword = false; AuthInfo::registerMetaTypes(); } AuthInfo::AuthInfo(const AuthInfo &info) : d(new AuthInfoPrivate()) { (*this) = info; AuthInfo::registerMetaTypes(); } AuthInfo::~AuthInfo() { delete d; } AuthInfo &AuthInfo::operator= (const AuthInfo &info) { url = info.url; username = info.username; password = info.password; prompt = info.prompt; caption = info.caption; comment = info.comment; commentLabel = info.commentLabel; realmValue = info.realmValue; digestInfo = info.digestInfo; verifyPath = info.verifyPath; readOnly = info.readOnly; keepPassword = info.keepPassword; modified = info.modified; d->extraFields = info.d->extraFields; return *this; } bool AuthInfo::isModified() const { return modified; } void AuthInfo::setModified(bool flag) { modified = flag; } ///// void AuthInfo::setExtraField(const QString &fieldName, const QVariant &value) { d->extraFields[fieldName].value = value; } void AuthInfo::setExtraFieldFlags(const QString &fieldName, const FieldFlags flags) { d->extraFields[fieldName].flags = flags; } QVariant AuthInfo::getExtraField(const QString &fieldName) const { if (!d->extraFields.contains(fieldName)) { return QVariant(); } return d->extraFields[fieldName].value; } AuthInfo::FieldFlags AuthInfo::getExtraFieldFlags(const QString &fieldName) const { if (!d->extraFields.contains(fieldName)) { return AuthInfo::ExtraFieldNoFlags; } return d->extraFields[fieldName].flags; } void AuthInfo::registerMetaTypes() { qRegisterMetaType(); qRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); } ///// QDataStream &KIO::operator<< (QDataStream &s, const AuthInfo &a) { s << quint8(1) << a.url << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue << a.digestInfo << a.verifyPath << a.readOnly << a.keepPassword << a.modified << a.d->extraFields; return s; } QDataStream &KIO::operator>> (QDataStream &s, AuthInfo &a) { quint8 version; s >> version >> a.url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo >> a.verifyPath >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields; return s; } QDBusArgument &KIO::operator<<(QDBusArgument &argument, const AuthInfo &a) { argument.beginStructure(); argument << quint8(1) << a.url.toString() << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue << a.digestInfo << a.verifyPath << a.readOnly << a.keepPassword << a.modified << a.d->extraFields; argument.endStructure(); return argument; } const QDBusArgument &KIO::operator>>(const QDBusArgument &argument, AuthInfo &a) { QString url; quint8 version; argument.beginStructure(); argument >> version >> url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo >> a.verifyPath >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields; argument.endStructure(); a.url = QUrl(url); return argument; } typedef QList LoginList; typedef QMap LoginMap; -class NetRC::NetRCPrivate +class Q_DECL_HIDDEN NetRC::NetRCPrivate { public: NetRCPrivate() : isDirty(false), index(-1) {} QString extract(const QString &buf, const QString &key); void getMachinePart(const QString &line); void getMacdefPart(const QString &line); bool isDirty; LoginMap loginMap; QTextStream fstream; QString type; int index; }; NetRC *NetRC::instance = nullptr; NetRC::NetRC() : d(new NetRCPrivate) { } NetRC::~NetRC() { delete instance; instance = nullptr; delete d; } NetRC *NetRC::self() { if (!instance) { instance = new NetRC; } return instance; } bool NetRC::lookup(const QUrl &url, AutoLogin &login, bool userealnetrc, const QString &_type, LookUpMode mode) { //qDebug() << "AutoLogin lookup for: " << url.host(); if (!url.isValid()) { return false; } QString type = _type; if (type.isEmpty()) { type = url.scheme(); } if (d->loginMap.isEmpty() || d->isDirty) { d->loginMap.clear(); QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + QLatin1String("kionetrc"); bool kionetrcStatus = parse(filename); bool netrcStatus = false; if (userealnetrc) { filename = QDir::homePath() + QLatin1String("/.netrc"); netrcStatus = parse(filename); } if (!(kionetrcStatus || netrcStatus)) { return false; } } if (!d->loginMap.contains(type)) { return false; } const LoginList &l = d->loginMap[type]; if (l.isEmpty()) { return false; } for (LoginList::ConstIterator it = l.begin(); it != l.end(); ++it) { const AutoLogin &log = *it; if ((mode & defaultOnly) == defaultOnly && log.machine == QLatin1String("default") && (login.login.isEmpty() || login.login == log.login)) { login.type = log.type; login.machine = log.machine; login.login = log.login; login.password = log.password; login.macdef = log.macdef; } if ((mode & presetOnly) == presetOnly && log.machine == QLatin1String("preset") && (login.login.isEmpty() || login.login == log.login)) { login.type = log.type; login.machine = log.machine; login.login = log.login; login.password = log.password; login.macdef = log.macdef; } if ((mode & exactOnly) == exactOnly && log.machine == url.host() && (login.login.isEmpty() || login.login == log.login)) { login.type = log.type; login.machine = log.machine; login.login = log.login; login.password = log.password; login.macdef = log.macdef; break; } } return true; } void NetRC::reload() { d->isDirty = true; } bool NetRC::parse(const QString &fileName) { QFile file(fileName); if (file.permissions() != (QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser)) { return false; } if (!file.open(QIODevice::ReadOnly)) { return false; } d->fstream.setDevice(&file); QString line; while (!d->fstream.atEnd()) { line = d->fstream.readLine().simplified(); // If line is a comment or is empty, read next line if ((line.startsWith(QLatin1String("#")) || line.isEmpty())) { continue; } // If line refers to a machine, maybe it is spread in more lines. // getMachinePart() will take care of getting all the info and putting it into loginMap. if ((line.startsWith(QLatin1String("machine")) || line.startsWith(QLatin1String("default")) || line.startsWith(QLatin1String("preset")))) { d->getMachinePart(line); continue; } // If line refers to a macdef, it will be more than one line. // getMacdefPart() will take care of getting all the lines of the macro // and putting them into loginMap if (line.startsWith(QLatin1String("macdef"))) { d->getMacdefPart(line); continue; } } return true; } QString NetRC::NetRCPrivate::extract(const QString &buf, const QString &key) { QStringList stringList = buf.split(QLatin1Char(' '), QString::SkipEmptyParts); int i = stringList.indexOf(key); if ((i != -1) && (i + 1 < stringList.size())) { return stringList.at(i + 1); } else { return QString(); } } void NetRC::NetRCPrivate::getMachinePart(const QString &line) { QString buf = line; while (!(buf.contains(QStringLiteral("login")) && (buf.contains(QStringLiteral("password")) || buf.contains(QStringLiteral("account")) || buf.contains(QStringLiteral("type"))))) { buf += QStringLiteral(" "); buf += fstream.readLine().simplified(); } // Once we've got all the info, process it. AutoLogin l; l.machine = extract(buf, QStringLiteral("machine")); if (l.machine.isEmpty()) { if (buf.contains(QStringLiteral("default"))) { l.machine = QStringLiteral("default"); } else if (buf.contains(QStringLiteral("preset"))) { l.machine = QStringLiteral("preset"); } } l.login = extract(buf, QStringLiteral("login")); l.password = extract(buf, QStringLiteral("password")); if (l.password.isEmpty()) { l.password = extract(buf, QStringLiteral("account")); } type = l.type = extract(buf, QStringLiteral("type")); if (l.type.isEmpty() && !l.machine.isEmpty()) { type = l.type = QStringLiteral("ftp"); } loginMap[l.type].append(l); index = loginMap[l.type].count() - 1; } void NetRC::NetRCPrivate::getMacdefPart(const QString &line) { QString buf = line; QString macro = extract(buf, QStringLiteral("macdef")); QString newLine; while (!fstream.atEnd()) { newLine = fstream.readLine().simplified(); if (!newLine.isEmpty()) { buf += QStringLiteral("\n"); buf += newLine; } else { break; } } loginMap[type][index].macdef[macro].append(buf); } diff --git a/src/core/kacl.cpp b/src/core/kacl.cpp index 5c6e3309..38e3aff9 100644 --- a/src/core/kacl.cpp +++ b/src/core/kacl.cpp @@ -1,722 +1,722 @@ /* This file is part of the KDE project Copyright (C) 2005 - 2007 Till Adam 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $ #include "kacl.h" #include #include #if HAVE_POSIX_ACL #include #include #endif #include #include #include #include #include #include #include "kiocoredebug.h" -class KACL::KACLPrivate +class Q_DECL_HIDDEN KACL::KACLPrivate { public: KACLPrivate() #if HAVE_POSIX_ACL : m_acl(nullptr) #endif {} #if HAVE_POSIX_ACL KACLPrivate(acl_t acl) : m_acl(acl) {} #endif #if HAVE_POSIX_ACL ~KACLPrivate() { if (m_acl) { acl_free(m_acl); } } #endif // helpers #if HAVE_POSIX_ACL bool setMaskPermissions(unsigned short v); QString getUserName(uid_t uid) const; QString getGroupName(gid_t gid) const; bool setAllUsersOrGroups(const QList< QPair > &list, acl_tag_t type); bool setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type); acl_t m_acl; mutable QHash m_usercache; mutable QHash m_groupcache; #endif }; KACL::KACL(const QString &aclString) : d(new KACLPrivate) { setACL(aclString); } KACL::KACL(mode_t basePermissions) #if HAVE_POSIX_ACL : d(new KACLPrivate(acl_from_mode(basePermissions))) #else : d(new KACLPrivate) #endif { #if !HAVE_POSIX_ACL Q_UNUSED(basePermissions); #endif } KACL::KACL() : d(new KACLPrivate) { } KACL::KACL(const KACL &rhs) : d(new KACLPrivate) { setACL(rhs.asString()); } KACL::~KACL() { delete d; } KACL &KACL::operator=(const KACL &rhs) { if (this != &rhs) { setACL(rhs.asString()); } return *this; } bool KACL::operator==(const KACL &rhs) const { #if HAVE_POSIX_ACL return (acl_cmp(d->m_acl, rhs.d->m_acl) == 0); #else Q_UNUSED(rhs); return true; #endif } bool KACL::operator!=(const KACL &rhs) const { return !operator==(rhs); } bool KACL::isValid() const { bool valid = false; #if HAVE_POSIX_ACL if (d->m_acl) { valid = (acl_valid(d->m_acl) == 0); } #endif return valid; } bool KACL::isExtended() const { #if HAVE_POSIX_ACL return (acl_equiv_mode(d->m_acl, nullptr) != 0); #else return false; #endif } #if HAVE_POSIX_ACL 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, ¤tTag); if (currentTag == tag) { return entry; } ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); } return nullptr; } static unsigned short entryToPermissions(acl_entry_t entry) { if (entry == nullptr) { 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 == nullptr) { 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); } } #if HAVE_POSIX_ACL #if 0 static void printACL(acl_t acl, const QString &comment) { const char *txt = acl_to_text(acl); qCDebug(KIO_CORE) << comment << txt; acl_free(txt); } #endif #endif static int getUidForName(const QString &name) { struct passwd *user = getpwnam(name.toLocal8Bit()); if (user) { return user->pw_uid; } else { return -1; } } static int getGidForName(const QString &name) { struct group *group = getgrnam(name.toLocal8Bit()); if (group) { return group->gr_gid; } else { return -1; } } #endif // ------------------ begin API implementation ------------ unsigned short KACL::ownerPermissions() const { #if HAVE_POSIX_ACL return entryToPermissions(entryForTag(d->m_acl, ACL_USER_OBJ)); #else return 0; #endif } bool KACL::setOwnerPermissions(unsigned short v) { #if HAVE_POSIX_ACL permissionsToEntry(entryForTag(d->m_acl, ACL_USER_OBJ), v); #else Q_UNUSED(v); #endif return true; } unsigned short KACL::owningGroupPermissions() const { #if HAVE_POSIX_ACL return entryToPermissions(entryForTag(d->m_acl, ACL_GROUP_OBJ)); #else return 0; #endif } bool KACL::setOwningGroupPermissions(unsigned short v) { #if HAVE_POSIX_ACL permissionsToEntry(entryForTag(d->m_acl, ACL_GROUP_OBJ), v); #else Q_UNUSED(v); #endif return true; } unsigned short KACL::othersPermissions() const { #if HAVE_POSIX_ACL return entryToPermissions(entryForTag(d->m_acl, ACL_OTHER)); #else return 0; #endif } bool KACL::setOthersPermissions(unsigned short v) { #if HAVE_POSIX_ACL permissionsToEntry(entryForTag(d->m_acl, ACL_OTHER), v); #else Q_UNUSED(v); #endif return true; } mode_t KACL::basePermissions() const { mode_t perms(0); #if HAVE_POSIX_ACL 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; } #endif return perms; } unsigned short KACL::maskPermissions(bool &exists) const { exists = true; #if HAVE_POSIX_ACL acl_entry_t entry = entryForTag(d->m_acl, ACL_MASK); if (entry == nullptr) { exists = false; return 0; } return entryToPermissions(entry); #else return 0; #endif } #if HAVE_POSIX_ACL bool KACL::KACLPrivate::setMaskPermissions(unsigned short v) { acl_entry_t entry = entryForTag(m_acl, ACL_MASK); if (entry == nullptr) { acl_create_entry(&m_acl, &entry); acl_set_tag_type(entry, ACL_MASK); } permissionsToEntry(entry, v); return true; } #endif bool KACL::setMaskPermissions(unsigned short v) { #if HAVE_POSIX_ACL return d->setMaskPermissions(v); #else Q_UNUSED(v); return true; #endif } #if HAVE_POSIX_ACL using unique_ptr_acl_free = std::unique_ptr; #endif /************************** * Deal with named users * **************************/ unsigned short KACL::namedUserPermissions(const QString &name, bool *exists) const { #if HAVE_POSIX_ACL acl_entry_t entry; *exists = false; int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); while (ret == 1) { acl_tag_t currentTag; acl_get_tag_type(entry, ¤tTag); if (currentTag == ACL_USER) { const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); const uid_t id = *(static_cast(idptr.get())); if (d->getUserName(id) == name) { *exists = true; return entryToPermissions(entry); } } ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); } #else Q_UNUSED(name); Q_UNUSED(exists); #endif return 0; } #if HAVE_POSIX_ACL bool KACL::KACLPrivate::setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type) { bool allIsWell = true; acl_t newACL = acl_dup(m_acl); acl_entry_t entry; 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, ¤tTag); if (currentTag == type) { const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); const int id = * (static_cast(idptr.get())); // We assume that sizeof(uid_t) == sizeof(gid_t) 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, if the user hasn't explicitly set one. if (entryForTag(newACL, ACL_MASK) == nullptr) { 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; } #endif bool KACL::setNamedUserPermissions(const QString &name, unsigned short permissions) { #if HAVE_POSIX_ACL return d->setNamedUserOrGroupPermissions(name, permissions, ACL_USER); #else Q_UNUSED(name); Q_UNUSED(permissions); return true; #endif } ACLUserPermissionsList KACL::allUserPermissions() const { ACLUserPermissionsList list; #if HAVE_POSIX_ACL acl_entry_t entry; int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); while (ret == 1) { acl_tag_t currentTag; acl_get_tag_type(entry, ¤tTag); if (currentTag == ACL_USER) { const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); const uid_t id = *(static_cast(idptr.get())); QString name = d->getUserName(id); unsigned short permissions = entryToPermissions(entry); ACLUserPermissions pair = qMakePair(name, permissions); list.append(pair); } ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); } #endif return list; } #if HAVE_POSIX_ACL bool KACL::KACLPrivate::setAllUsersOrGroups(const QList< QPair > &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, ¤tTag); 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 QList< QPair >::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, if the user hasn't explicitly set one. if (entryForTag(newACL, ACL_MASK) == nullptr) { acl_calc_mask(&newACL); } } if (allIsWell && (acl_valid(newACL) == 0)) { acl_free(m_acl); m_acl = newACL; } else { acl_free(newACL); } return allIsWell; } #endif bool KACL::setAllUserPermissions(const ACLUserPermissionsList &users) { #if HAVE_POSIX_ACL return d->setAllUsersOrGroups(users, ACL_USER); #else Q_UNUSED(users); return true; #endif } /************************** * Deal with named groups * **************************/ unsigned short KACL::namedGroupPermissions(const QString &name, bool *exists) const { *exists = false; #if HAVE_POSIX_ACL acl_entry_t entry; int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); while (ret == 1) { acl_tag_t currentTag; acl_get_tag_type(entry, ¤tTag); if (currentTag == ACL_GROUP) { const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); const gid_t id = *(static_cast(idptr.get())); if (d->getGroupName(id) == name) { *exists = true; return entryToPermissions(entry); } } ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); } #else Q_UNUSED(name); #endif return 0; } bool KACL::setNamedGroupPermissions(const QString &name, unsigned short permissions) { #if HAVE_POSIX_ACL return d->setNamedUserOrGroupPermissions(name, permissions, ACL_GROUP); #else Q_UNUSED(name); Q_UNUSED(permissions); return true; #endif } ACLGroupPermissionsList KACL::allGroupPermissions() const { ACLGroupPermissionsList list; #if HAVE_POSIX_ACL acl_entry_t entry; int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); while (ret == 1) { acl_tag_t currentTag; acl_get_tag_type(entry, ¤tTag); if (currentTag == ACL_GROUP) { const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); const gid_t id = *(static_cast(idptr.get())); QString name = d->getGroupName(id); unsigned short permissions = entryToPermissions(entry); ACLGroupPermissions pair = qMakePair(name, permissions); list.append(pair); } ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); } #endif return list; } bool KACL::setAllGroupPermissions(const ACLGroupPermissionsList &groups) { #if HAVE_POSIX_ACL return d->setAllUsersOrGroups(groups, ACL_GROUP); #else Q_UNUSED(groups); return true; #endif } /************************** * from and to string * **************************/ bool KACL::setACL(const QString &aclStr) { bool ret = false; #if HAVE_POSIX_ACL acl_t temp = acl_from_text(aclStr.toLatin1()); if (acl_valid(temp) != 0) { // TODO errno is set, what to do with it here? acl_free(temp); } else { if (d->m_acl) { acl_free(d->m_acl); } d->m_acl = temp; ret = true; } #else Q_UNUSED(aclStr); #endif return ret; } QString KACL::asString() const { #if HAVE_POSIX_ACL ssize_t size = 0; char *txt = acl_to_text(d->m_acl, &size); const QString ret = QString::fromLatin1(txt, size); acl_free(txt); return ret; #else return QString(); #endif } // helpers #if HAVE_POSIX_ACL QString KACL::KACLPrivate::getUserName(uid_t uid) const { if (!m_usercache.contains(uid)) { struct passwd *user = getpwuid(uid); if (user) { m_usercache.insert(uid, QString::fromLatin1(user->pw_name)); } else { return QString::number(uid); } } return m_usercache[uid]; } QString KACL::KACLPrivate::getGroupName(gid_t gid) const { if (!m_groupcache.contains(gid)) { struct group *grp = getgrgid(gid); if (grp) { m_groupcache.insert(gid, QString::fromLatin1(grp->gr_name)); } else { return QString::number(gid); } } return m_groupcache[gid]; } #endif 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; } diff --git a/src/core/kcoredirlister_p.h b/src/core/kcoredirlister_p.h index e0770a97..774ed45d 100644 --- a/src/core/kcoredirlister_p.h +++ b/src/core/kcoredirlister_p.h @@ -1,586 +1,586 @@ /* This file is part of the KDE project Copyright (C) 2002-2006 Michael Brade 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef kdirlister_p_h #define kdirlister_p_h #include "kfileitem.h" #include #include #include #include #include #include #include #include #include #include /** * KCoreDirListerCache stores pointers to KFileItems internally and expects that * these pointers remain valid, even if the number of items in the list 'lstItems' * in KCoreDirLister::DirItem changes. Since such changes in a QList * may change the internal memory layout of the QList, pointers to KFileItems in a * QList might become invalid. * * Therefore, we make 'lstItems' a QList, where * NonMovableFileItem is a class that inherits KFileItem, but is not declared as a * Q_MOVABLE_TYPE. This forces QList to never move these items in memory, which is * achieved by allocating space for each individual item, and store pointers to * these items in the contiguous region in memory. * * TODO: Try to get rid of the raw KFileItem pointers in KCoreDirListerCache, and * replace all occurrences of NonMovableFileItem(List) with KFileItem(List). */ class NonMovableFileItem : public KFileItem { public: NonMovableFileItem(const KFileItem &item) : KFileItem(item) {} }; class NonMovableFileItemList : public QList { public: NonMovableFileItemList() {} KFileItem findByName(const QString &fileName) const { const_iterator it = begin(); const const_iterator itend = end(); for (; it != itend; ++it) { if ((*it).name() == fileName) { return *it; } } return KFileItem(); } KFileItemList toKFileItemList() const { KFileItemList result; result.reserve(count()); foreach (const NonMovableFileItem &item, *this) { result.append(item); } return result; } }; class KCoreDirLister; namespace KIO { class Job; class ListJob; } class OrgKdeKDirNotifyInterface; struct KCoreDirListerCacheDirectoryData; -class KCoreDirLister::Private +class Q_DECL_HIDDEN KCoreDirLister::Private { public: Private(KCoreDirLister *parent) : m_parent(parent) { complete = false; autoUpdate = false; delayedMimeTypes = false; rootFileItem = KFileItem(); lstNewItems = nullptr; lstRefreshItems = nullptr; lstMimeFilteredItems = nullptr; lstRemoveItems = nullptr; hasPendingChanges = false; } void _k_emitCachedItems(const QUrl &, bool, bool); void _k_slotInfoMessage(KJob *, const QString &); void _k_slotPercent(KJob *, unsigned long); void _k_slotTotalSize(KJob *, qulonglong); void _k_slotProcessedSize(KJob *, qulonglong); void _k_slotSpeed(KJob *, unsigned long); bool doMimeExcludeFilter(const QString &mimeExclude, const QStringList &filters) const; void connectJob(KIO::ListJob *); void jobDone(KIO::ListJob *); uint numJobs(); void addNewItem(const QUrl &directoryUrl, const KFileItem &item); void addNewItems(const QUrl &directoryUrl, const NonMovableFileItemList &items); void addRefreshItem(const QUrl &directoryUrl, const KFileItem &oldItem, const KFileItem &item); void emitItems(); void emitItemsDeleted(const KFileItemList &items); /** * Redirect this dirlister from oldUrl to newUrl. * @param keepItems if true, keep the fileitems (e.g. when renaming an existing dir); * if false, clear out everything (e.g. when redirecting during listing). */ void redirect(const QUrl &oldUrl, const QUrl &newUrl, bool keepItems); /** * Should this item be visible according to the current filter settings? */ bool isItemVisible(const KFileItem &item) const; void prepareForSettingsChange() { if (!hasPendingChanges) { hasPendingChanges = true; oldSettings = settings; } } void emitChanges(); class CachedItemsJob; CachedItemsJob *cachedItemsJobForUrl(const QUrl &url) const; KCoreDirLister *m_parent; /** * List of dirs handled by this dirlister. The first entry is the base URL. * For a tree view, it contains all the dirs shown. */ QList lstDirs; // toplevel URL QUrl url; bool complete: 1; bool autoUpdate: 1; bool delayedMimeTypes: 1; bool hasPendingChanges: 1; // i.e. settings != oldSettings struct JobData { long unsigned int percent, speed; KIO::filesize_t processedSize, totalSize; }; QMap jobData; // file item for the root itself (".") KFileItem rootFileItem; typedef QHash NewItemsHash; NewItemsHash *lstNewItems; QList > *lstRefreshItems; KFileItemList *lstMimeFilteredItems, *lstRemoveItems; QList m_cachedItemsJobs; QString nameFilter; // parsed into lstFilters struct FilterSettings { FilterSettings() : isShowingDotFiles(false), dirOnlyMode(false) {} bool isShowingDotFiles; bool dirOnlyMode; QList lstFilters; QStringList mimeFilter; QStringList mimeExcludeFilter; }; FilterSettings settings; FilterSettings oldSettings; friend class KCoreDirListerCache; }; /** * Design of the cache: * There is a single KCoreDirListerCache for the whole process. * It holds all the items used by the dir listers (itemsInUse) * as well as a cache of the recently used items (itemsCached). * Those items are grouped by directory (a DirItem represents a whole directory). * * KCoreDirListerCache also runs all the jobs for listing directories, whether they are for * normal listing or for updates. * For faster lookups, it also stores a hash table, which gives for a directory URL: * - the dirlisters holding that URL (listersCurrentlyHolding) * - the dirlisters currently listing that URL (listersCurrentlyListing) */ class KCoreDirListerCache : public QObject { Q_OBJECT public: KCoreDirListerCache(); // only called by K_GLOBAL_STATIC ~KCoreDirListerCache(); void updateDirectory(const QUrl &dir); KFileItem itemForUrl(const QUrl &url) const; NonMovableFileItemList *itemsForDir(const QUrl &dir) const; bool listDir(KCoreDirLister *lister, const QUrl &_url, bool _keep, bool _reload); // stop all running jobs for lister void stop(KCoreDirLister *lister, bool silent = false); // stop just the job listing url for lister void stopListingUrl(KCoreDirLister *lister, const QUrl &_url, bool silent = false); void setAutoUpdate(KCoreDirLister *lister, bool enable); void forgetDirs(KCoreDirLister *lister); void forgetDirs(KCoreDirLister *lister, const QUrl &_url, bool notify); KFileItem findByName(const KCoreDirLister *lister, const QString &_name) const; // findByUrl returns a pointer so that it's possible to modify the item. // See itemForUrl for the version that returns a readonly kfileitem. // @param lister can be 0. If set, it is checked that the url is held by the lister KFileItem *findByUrl(const KCoreDirLister *lister, const QUrl &url) const; // Called by CachedItemsJob: // Emits the cached items, for this lister and this url void emitItemsFromCache(KCoreDirLister::Private::CachedItemsJob *job, KCoreDirLister *lister, const QUrl &_url, bool _reload, bool _emitCompleted); // Called by CachedItemsJob: void forgetCachedItemsJob(KCoreDirLister::Private::CachedItemsJob *job, KCoreDirLister *lister, const QUrl &url); public Q_SLOTS: /** * Notify that files have been added in @p directory * The receiver will list that directory again to find * the new items (since it needs more than just the names anyway). * Connected to the DBus signal from the KDirNotify interface. */ void slotFilesAdded(const QString &urlDirectory); /** * Notify that files have been deleted. * This call passes the exact urls of the deleted files * so that any view showing them can simply remove them * or be closed (if its current dir was deleted) * Connected to the DBus signal from the KDirNotify interface. */ void slotFilesRemoved(const QStringList &fileList); /** * Notify that files have been changed. * At the moment, this is only used for new icon, but it could be * used for size etc. as well. * Connected to the DBus signal from the KDirNotify interface. */ void slotFilesChanged(const QStringList &fileList); void slotFileRenamed(const QString &srcUrl, const QString &dstUrl, const QString &dstPath); private Q_SLOTS: void slotFileDirty(const QString &_file); void slotFileCreated(const QString &_file); void slotFileDeleted(const QString &_file); void slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries); void slotResult(KJob *j); void slotRedirection(KIO::Job *job, const QUrl &url); void slotUpdateEntries(KIO::Job *job, const KIO::UDSEntryList &entries); void slotUpdateResult(KJob *job); void processPendingUpdates(); private: void itemsAddedInDirectory(const QUrl &url); class DirItem; DirItem *dirItemForUrl(const QUrl &dir) const; bool validUrl(KCoreDirLister *lister, const QUrl &_url) const; void stopListJob(const QString &url, bool silent); KIO::ListJob *jobForUrl(const QString &url, KIO::ListJob *not_job = nullptr); const QUrl &joburl(KIO::ListJob *job); void killJob(KIO::ListJob *job); // Called when something tells us that the directory @p url has changed. // Returns true if @p url is held by some lister (meaning: do the update now) // otherwise mark the cached item as not-up-to-date for later and return false bool checkUpdate(const QUrl &url); // Helper method for slotFileDirty void handleFileDirty(const QUrl &url); void handleDirDirty(const QUrl &url); // when there were items deleted from the filesystem all the listers holding // the parent directory need to be notified, the items have to be deleted // and removed from the cache including all the children. void deleteUnmarkedItems(const QList&, NonMovableFileItemList &lstItems, const QHash &itemsToDelete); // Helper method called when we know that a list of items was deleted void itemsDeleted(const QList &listers, const KFileItemList &deletedItems); void slotFilesRemoved(const QList &urls); // common for slotRedirection and slotFileRenamed void renameDir(const QUrl &oldUrl, const QUrl &url); // common for deleteUnmarkedItems and slotFilesRemoved void deleteDir(const QUrl &dirUrl); // remove directory from cache (itemsCached), including all child dirs void removeDirFromCache(const QUrl &dir); // helper for renameDir void emitRedirections(const QUrl &oldUrl, const QUrl &url); /** * Emits refreshItem() in the directories that cared for oldItem. * The caller has to remember to call emitItems in the set of dirlisters returned * (but this allows to buffer change notifications) */ QSet emitRefreshItem(const KFileItem &oldItem, const KFileItem &fileitem); /** * When KDirWatch tells us that something changed in "dir", we need to * also notify the dirlisters that are listing a symlink to "dir" (#213799) */ QList directoriesForCanonicalPath(const QUrl &dir) const; /** * Returns the names listed in dir's ".hidden" file, if it exists. * If a file named ".hidden" exists in the @p dir directory, this method * returns all the file names listed in that file. If it doesn't exist, an * empty set is returned. * @param dir path to the target directory. * @return names listed in the directory's ".hidden" file (empty if it doesn't exist). */ QSet filesInDotHiddenForDir(const QString& dir); #ifndef NDEBUG void printDebug(); #endif class DirItem { public: DirItem(const QUrl &dir, const QString &canonicalPath) : url(dir), m_canonicalPath(canonicalPath) { autoUpdates = 0; complete = false; watchedWhileInCache = false; } ~DirItem() { if (autoUpdates) { if (KDirWatch::exists() && url.isLocalFile()) { KDirWatch::self()->removeDir(m_canonicalPath); } // Since sendSignal goes through D-Bus, QCoreApplication has to be available // which might not be the case anymore from a global static dtor like the // lister cache if (QCoreApplication::instance()) { sendSignal(false, url); } } lstItems.clear(); } void sendSignal(bool entering, const QUrl &url) { // Note that "entering" means "start watching", and "leaving" means "stop watching" // (i.e. it's not when the user leaves the directory, it's when the directory is removed from the cache) if (entering) { org::kde::KDirNotify::emitEnteredDirectory(url); } else { org::kde::KDirNotify::emitLeftDirectory(url); } } void redirect(const QUrl &newUrl) { if (autoUpdates) { if (url.isLocalFile()) { KDirWatch::self()->removeDir(m_canonicalPath); } sendSignal(false, url); if (newUrl.isLocalFile()) { m_canonicalPath = QFileInfo(newUrl.toLocalFile()).canonicalFilePath(); KDirWatch::self()->addDir(m_canonicalPath); } sendSignal(true, newUrl); } url = newUrl; if (!rootItem.isNull()) { rootItem.setUrl(newUrl); } } void incAutoUpdate() { if (autoUpdates++ == 0) { if (url.isLocalFile()) { KDirWatch::self()->addDir(m_canonicalPath); } sendSignal(true, url); } } void decAutoUpdate() { if (--autoUpdates == 0) { if (url.isLocalFile()) { KDirWatch::self()->removeDir(m_canonicalPath); } sendSignal(false, url); } else if (autoUpdates < 0) { autoUpdates = 0; } } // number of KCoreDirListers using autoUpdate for this dir short autoUpdates; // this directory is up-to-date bool complete; // the directory is watched while being in the cache (useful for proper incAutoUpdate/decAutoUpdate count) bool watchedWhileInCache; // the complete url of this directory QUrl url; // the local path, with symlinks resolved, so that KDirWatch works QString m_canonicalPath; // KFileItem representing the root of this directory. // Remember that this is optional. FTP sites don't return '.' in // the list, so they give no root item KFileItem rootItem; NonMovableFileItemList lstItems; }; // definition of the cache of ".hidden" files struct CacheHiddenFile { CacheHiddenFile(const QDateTime& mtime, const QSet& listedFiles) : mtime(mtime), listedFiles(listedFiles) { } QDateTime mtime; QSet listedFiles; }; //static const unsigned short MAX_JOBS_PER_LISTER; QMap runningListJobs; // an item is a complete directory QHash itemsInUse; QCache itemsCached; // cache of ".hidden" files QCache