diff --git a/src/lib/util/kuser_unix.cpp b/src/lib/util/kuser_unix.cpp index c42ea99..216dfb7 100644 --- a/src/lib/util/kuser_unix.cpp +++ b/src/lib/util/kuser_unix.cpp @@ -1,564 +1,564 @@ /* * KUser - represent a user/account * Copyright (C) 2002 Tim Jansen * Copyright (C) 2014 Alex Richardson * * 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 "kuser.h" #include "config-getgrouplist.h" #include "config-accountsservice.h" #include #include #include #include #include #include #include #include #include // std::find #include // std::function -#if defined(__BIONIC__) +#if defined(__BIONIC__) && __ANDROID_API__ < 26 static inline struct passwd * getpwent() { return nullptr; } inline void setpwent() { } static inline void setgrent() { } static inline struct group * getgrent() { return nullptr; } inline void endpwent() { } static inline void endgrent() { } #endif class Q_DECL_HIDDEN KUser::Private : public QSharedData { public: uid_t uid; gid_t gid; QString loginName; QString homeDir, shell; QMap properties; Private() : uid(uid_t(-1)), gid(gid_t(-1)) {} Private(const char *name) : uid(uid_t(-1)), gid(gid_t(-1)) { fillPasswd(name ? ::getpwnam(name) : nullptr); } Private(const passwd *p) : uid(uid_t(-1)), gid(gid_t(-1)) { fillPasswd(p); } void fillPasswd(const passwd *p) { if (p) { #ifndef __BIONIC__ QString gecos = QString::fromLocal8Bit(p->pw_gecos); #else QString gecos = QString(); #endif QStringList gecosList = gecos.split(QLatin1Char(',')); // fill up the list, should be at least 4 entries while (gecosList.size() < 4) { gecosList << QString(); } uid = p->pw_uid; gid = p->pw_gid; loginName = QString::fromLocal8Bit(p->pw_name); properties[KUser::FullName] = QVariant(gecosList[0]); properties[KUser::RoomNumber] = QVariant(gecosList[1]); properties[KUser::WorkPhone] = QVariant(gecosList[2]); properties[KUser::HomePhone] = QVariant(gecosList[3]); if (uid == ::getuid() && uid == ::geteuid()) { homeDir = QFile::decodeName(qgetenv("HOME")); } if (homeDir.isEmpty()) { homeDir = QFile::decodeName(p->pw_dir); } shell = QString::fromLocal8Bit(p->pw_shell); } } }; KUser::KUser(UIDMode mode) { uid_t _uid = ::getuid(), _euid; if (mode == UseEffectiveUID && (_euid = ::geteuid()) != _uid) { d = new Private(::getpwuid(_euid)); } else { d = new Private(qgetenv("LOGNAME").constData()); if (d->uid != _uid) { d = new Private(qgetenv("USER").constData()); if (d->uid != _uid) { d = new Private(::getpwuid(_uid)); } } } } KUser::KUser(K_UID _uid) : d(new Private(::getpwuid(_uid))) { } KUser::KUser(KUserId _uid) : d(new Private(::getpwuid(_uid.nativeId()))) { } KUser::KUser(const QString &name) : d(new Private(name.toLocal8Bit().data())) { } KUser::KUser(const char *name) : d(new Private(name)) { } KUser::KUser(const passwd *p) : d(new Private(p)) { } KUser::KUser(const KUser &user) : d(user.d) { } KUser &KUser::operator =(const KUser &user) { d = user.d; return *this; } bool KUser::operator ==(const KUser &user) const { return isValid() && (d->uid == user.d->uid); } bool KUser::isValid() const { return d->uid != uid_t(-1); } KUserId KUser::userId() const { return KUserId(d->uid); } KGroupId KUser::groupId() const { return KGroupId(d->gid); } bool KUser::isSuperUser() const { return d->uid == 0; } QString KUser::loginName() const { return d->loginName; } QString KUser::homeDir() const { return d->homeDir; } QString KUser::faceIconPath() const { QString pathToFaceIcon; if (!d->loginName.isEmpty()) { pathToFaceIcon = QStringLiteral(ACCOUNTS_SERVICE_ICON_DIR) + QLatin1Char('/') + d->loginName; } if (QFile::exists(pathToFaceIcon)) { return pathToFaceIcon; } pathToFaceIcon = QString(homeDir() + QLatin1Char('/') + QStringLiteral(".face.icon")); if (QFileInfo(pathToFaceIcon).isReadable()) { return pathToFaceIcon; } return QString(); } QString KUser::shell() const { return d->shell; } template static void listGroupsForUser(const char *name, gid_t gid, uint maxCount, Func handleNextGroup) { if (Q_UNLIKELY(maxCount == 0)) { return; } uint found = 0; #if HAVE_GETGROUPLIST QVarLengthArray gid_buffer; gid_buffer.resize(100); int numGroups = gid_buffer.size(); int result = getgrouplist(name, gid, gid_buffer.data(), &numGroups); if (result < 0 && uint(numGroups) < maxCount) { // getgrouplist returns -1 if the buffer was too small to store all entries, the required size is in numGroups qDebug("Buffer was too small: %d, need %d", gid_buffer.size(), numGroups); gid_buffer.resize(numGroups); numGroups = gid_buffer.size(); getgrouplist(name, gid, gid_buffer.data(), &numGroups); } for (int i = 0; i < numGroups && found < maxCount; ++i) { struct group *g = getgrgid(gid_buffer[i]); // should never be null, but better be safe than crash if (g) { found++; handleNextGroup(g); } } #else // fall back to getgrent() and reading gr->gr_mem // This is slower than getgrouplist, but works as well // add the current gid, this is often not part of g->gr_mem (e.g. build.kde.org or my openSuSE 13.1 system) struct group *g = getgrgid(gid); if (g) { handleNextGroup(g); found++; if (found >= maxCount) { return; } } static const auto groupContainsUser = [](struct group * g, const char *name) -> bool { for (char **user = g->gr_mem; *user; user++) { if (strcmp(name, *user) == 0) { return true; } } return false; }; setgrent(); while ((g = getgrent())) { // don't add the current gid again if (g->gr_gid != gid && groupContainsUser(g, name)) { handleNextGroup(g); found++; if (found >= maxCount) { break; } } } endgrent(); #endif } QList KUser::groups(uint maxCount) const { QList result; listGroupsForUser( d->loginName.toLocal8Bit().constData(), d->gid, maxCount, [&](const group * g) { result.append(KUserGroup(g)); } ); return result; } QStringList KUser::groupNames(uint maxCount) const { QStringList result; listGroupsForUser( d->loginName.toLocal8Bit().constData(), d->gid, maxCount, [&](const group * g) { result.append(QString::fromLocal8Bit(g->gr_name)); } ); return result; } QVariant KUser::property(UserProperty which) const { return d->properties.value(which); } QList KUser::allUsers(uint maxCount) { QList result; passwd *p; setpwent(); for (uint i = 0; i < maxCount && (p = getpwent()); ++i) { result.append(KUser(p)); } endpwent(); return result; } QStringList KUser::allUserNames(uint maxCount) { QStringList result; passwd *p; setpwent(); for (uint i = 0; i < maxCount && (p = getpwent()); ++i) { result.append(QString::fromLocal8Bit(p->pw_name)); } endpwent(); return result; } KUser::~KUser() { } class Q_DECL_HIDDEN KUserGroup::Private : public QSharedData { public: gid_t gid; QString name; Private() : gid(gid_t(-1)) {} Private(const char *_name) : gid(gid_t(-1)) { fillGroup(_name ? ::getgrnam(_name) : nullptr); } Private(const ::group *p) : gid(gid_t(-1)) { fillGroup(p); } void fillGroup(const ::group *p) { if (p) { gid = p->gr_gid; name = QString::fromLocal8Bit(p->gr_name); } } }; KUserGroup::KUserGroup(KUser::UIDMode mode) { d = new Private(getgrgid(KUser(mode).groupId().nativeId())); } KUserGroup::KUserGroup(K_GID _gid) : d(new Private(getgrgid(_gid))) { } KUserGroup::KUserGroup(KGroupId _gid) : d(new Private(getgrgid(_gid.nativeId()))) { } KUserGroup::KUserGroup(const QString &_name) : d(new Private(_name.toLocal8Bit().data())) { } KUserGroup::KUserGroup(const char *_name) : d(new Private(_name)) { } KUserGroup::KUserGroup(const ::group *g) : d(new Private(g)) { } KUserGroup::KUserGroup(const KUserGroup &group) : d(group.d) { } KUserGroup &KUserGroup::operator =(const KUserGroup &group) { d = group.d; return *this; } bool KUserGroup::operator ==(const KUserGroup &group) const { return isValid() && (d->gid == group.d->gid); } bool KUserGroup::isValid() const { return d->gid != gid_t(-1); } KGroupId KUserGroup::groupId() const { return KGroupId(d->gid); } QString KUserGroup::name() const { return d->name; } static void listGroupMembers(gid_t gid, uint maxCount, std::function handleNextGroupUser) { if (maxCount == 0) { return; } struct group *g = getgrgid(gid); if (!g) { return; } uint found = 0; QVarLengthArray addedUsers; struct passwd *p = nullptr; for (char **user = g->gr_mem; *user; user++) { if ((p = getpwnam(*user))) { addedUsers.append(p->pw_uid); handleNextGroupUser(p); found++; if (found >= maxCount) { break; } } } //gr_mem doesn't contain users where the primary group == gid -> we have to iterate over all users setpwent(); while ((p = getpwent()) && found < maxCount) { if (p->pw_gid != gid) { continue; // only need primary gid since otherwise gr_mem already contains this user } // make sure we don't list a user twice if (std::find(addedUsers.cbegin(), addedUsers.cend(), p->pw_uid) == addedUsers.cend()) { handleNextGroupUser(p); found++; } } endpwent(); } QList KUserGroup::users(uint maxCount) const { QList result; listGroupMembers(d->gid, maxCount, [&](const passwd *p) { result.append(KUser(p)); }); return result; } QStringList KUserGroup::userNames(uint maxCount) const { QStringList result; listGroupMembers(d->gid, maxCount, [&](const passwd *p) { result.append(QString::fromLocal8Bit(p->pw_name)); }); return result; } QList KUserGroup::allGroups(uint maxCount) { QList result; ::group *g; setgrent(); for (uint i = 0; i < maxCount && (g = getgrent()); ++i) { result.append(KUserGroup(g)); } endgrent(); return result; } QStringList KUserGroup::allGroupNames(uint maxCount) { QStringList result; ::group *g; setgrent(); for (uint i = 0; i < maxCount && (g = getgrent()); ++i) { result.append(QString::fromLocal8Bit(g->gr_name)); } endgrent(); return result; } KUserGroup::~KUserGroup() { } KUserId KUserId::fromName(const QString &name) { if (name.isEmpty()) { return KUserId(); } QByteArray name8Bit = name.toLocal8Bit(); struct passwd *p = ::getpwnam(name8Bit.constData()); if (!p) { qWarning("Failed to lookup user %s: %s", name8Bit.constData(), strerror(errno)); return KUserId(); } return KUserId(p->pw_uid); } KGroupId KGroupId::fromName(const QString &name) { if (name.isEmpty()) { return KGroupId(); } QByteArray name8Bit = name.toLocal8Bit(); struct group *g = ::getgrnam(name8Bit.constData()); if (!g) { qWarning("Failed to lookup group %s: %s", name8Bit.constData(), strerror(errno)); return KGroupId(); } return KGroupId(g->gr_gid); } KUserId KUserId::currentUserId() { return KUserId(getuid()); } KUserId KUserId::currentEffectiveUserId() { return KUserId(geteuid()); } KGroupId KGroupId::currentGroupId() { return KGroupId(getgid()); } KGroupId KGroupId::currentEffectiveGroupId() { return KGroupId(getegid()); }