1 /*************************************************************************** 2 * Copyright (c) 2013 Abdurrahman AVCI <abdurrahmanavci@gmail.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the 16 * Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 ***************************************************************************/ 19 20 #include "UserModel.h" 21 22 #include "Constants.h" 23 #include "Configuration.h" 24 25 #include <QFile> 26 #include <QList> 27 #include <QTextStream> 28 #include <QStringList> 29 30 #include <memory> 31 #include <pwd.h> 32 33 namespace SDDM { 34 class User { 35 public: User(const struct passwd * data,const QString icon)36 User(const struct passwd *data, const QString icon) : 37 name(QString::fromLocal8Bit(data->pw_name)), 38 realName(QString::fromLocal8Bit(data->pw_gecos).split(QLatin1Char(',')).first()), 39 homeDir(QString::fromLocal8Bit(data->pw_dir)), 40 uid(data->pw_uid), 41 gid(data->pw_gid), 42 // if shadow is used pw_passwd will be 'x' nevertheless, so this 43 // will always be true 44 needsPassword(strcmp(data->pw_passwd, "") != 0), 45 icon(icon) 46 {} 47 48 QString name; 49 QString realName; 50 QString homeDir; 51 int uid { 0 }; 52 int gid { 0 }; 53 bool needsPassword { false }; 54 QString icon; 55 }; 56 57 typedef std::shared_ptr<User> UserPtr; 58 59 class UserModelPrivate { 60 public: 61 int lastIndex { 0 }; 62 QList<UserPtr> users; 63 bool containsAllUsers { true }; 64 }; 65 UserModel(bool needAllUsers,QObject * parent)66 UserModel::UserModel(bool needAllUsers, QObject *parent) : QAbstractListModel(parent), d(new UserModelPrivate()) { 67 const QString facesDir = mainConfig.Theme.FacesDir.get(); 68 const QString themeDir = mainConfig.Theme.ThemeDir.get(); 69 const QString currentTheme = mainConfig.Theme.Current.get(); 70 const QString themeDefaultFace = QStringLiteral("%1/%2/faces/.face.icon").arg(themeDir).arg(currentTheme); 71 const QString defaultFace = QStringLiteral("%1/.face.icon").arg(facesDir); 72 const QString iconURI = QStringLiteral("file://%1").arg( 73 QFile::exists(themeDefaultFace) ? themeDefaultFace : defaultFace); 74 75 bool lastUserFound = false; 76 77 struct passwd *current_pw; 78 setpwent(); 79 while ((current_pw = getpwent()) != nullptr) { 80 81 // skip entries with uids smaller than minimum uid 82 if (int(current_pw->pw_uid) < mainConfig.Users.MinimumUid.get()) 83 continue; 84 85 // skip entries with uids greater than maximum uid 86 if (int(current_pw->pw_uid) > mainConfig.Users.MaximumUid.get()) 87 continue; 88 // skip entries with user names in the hide users list 89 if (mainConfig.Users.HideUsers.get().contains(QString::fromLocal8Bit(current_pw->pw_name))) 90 continue; 91 92 // skip entries with shells in the hide shells list 93 if (mainConfig.Users.HideShells.get().contains(QString::fromLocal8Bit(current_pw->pw_shell))) 94 continue; 95 96 // create user 97 UserPtr user { new User(current_pw, iconURI) }; 98 99 // add user 100 d->users << user; 101 102 if (user->name == lastUser()) 103 lastUserFound = true; 104 105 if (!needAllUsers && d->users.count() > mainConfig.Theme.DisableAvatarsThreshold.get()) { 106 struct passwd *lastUserData; 107 // If the theme doesn't require that all users are present, try to add the data for lastUser at least 108 if(!lastUserFound && (lastUserData = getpwnam(qPrintable(lastUser())))) 109 d->users << UserPtr(new User(lastUserData, themeDefaultFace)); 110 111 d->containsAllUsers = false; 112 break; 113 } 114 } 115 116 endpwent(); 117 118 // sort users by username 119 std::sort(d->users.begin(), d->users.end(), [&](const UserPtr &u1, const UserPtr &u2) { return u1->name < u2->name; }); 120 // Remove duplicates in case we have several sources specified 121 // in nsswitch.conf(5). 122 auto newEnd = std::unique(d->users.begin(), d->users.end(), [&](const UserPtr &u1, const UserPtr &u2) { return u1->name == u2->name; }); 123 d->users.erase(newEnd, d->users.end()); 124 125 bool avatarsEnabled = mainConfig.Theme.EnableAvatars.get(); 126 if (avatarsEnabled && mainConfig.Theme.EnableAvatars.isDefault()) { 127 if (d->users.count() > mainConfig.Theme.DisableAvatarsThreshold.get()) avatarsEnabled=false; 128 } 129 130 // find out index of the last user 131 for (int i = 0; i < d->users.size(); ++i) { 132 UserPtr user { d->users.at(i) }; 133 if (user->name == stateConfig.Last.User.get()) 134 d->lastIndex = i; 135 136 if (avatarsEnabled) { 137 const QString userFace = QStringLiteral("%1/.face.icon").arg(user->homeDir); 138 const QString systemFace = QStringLiteral("%1/%2.face.icon").arg(facesDir).arg(user->name); 139 QString accountsServiceFace = QStringLiteral("/var/lib/AccountsService/icons/%1").arg(user->name); 140 141 if (QFile::exists(userFace)) 142 user->icon = QStringLiteral("file://%1").arg(userFace); 143 else if (QFile::exists(accountsServiceFace)) 144 user->icon = accountsServiceFace; 145 else if (QFile::exists(systemFace)) 146 user->icon = QStringLiteral("file://%1").arg(systemFace); 147 } 148 } 149 } 150 ~UserModel()151 UserModel::~UserModel() { 152 delete d; 153 } 154 roleNames() const155 QHash<int, QByteArray> UserModel::roleNames() const { 156 // set role names 157 QHash<int, QByteArray> roleNames; 158 roleNames[NameRole] = QByteArrayLiteral("name"); 159 roleNames[RealNameRole] = QByteArrayLiteral("realName"); 160 roleNames[HomeDirRole] = QByteArrayLiteral("homeDir"); 161 roleNames[IconRole] = QByteArrayLiteral("icon"); 162 roleNames[NeedsPasswordRole] = QByteArrayLiteral("needsPassword"); 163 164 return roleNames; 165 } 166 lastIndex() const167 const int UserModel::lastIndex() const { 168 return d->lastIndex; 169 } 170 lastUser() const171 QString UserModel::lastUser() const { 172 return stateConfig.Last.User.get(); 173 } 174 rowCount(const QModelIndex & parent) const175 int UserModel::rowCount(const QModelIndex &parent) const { 176 return d->users.length(); 177 } 178 data(const QModelIndex & index,int role) const179 QVariant UserModel::data(const QModelIndex &index, int role) const { 180 if (index.row() < 0 || index.row() > d->users.count()) 181 return QVariant(); 182 183 // get user 184 UserPtr user = d->users[index.row()]; 185 186 // return correct value 187 if (role == NameRole) 188 return user->name; 189 else if (role == RealNameRole) 190 return user->realName; 191 else if (role == HomeDirRole) 192 return user->homeDir; 193 else if (role == IconRole) 194 return user->icon; 195 else if (role == NeedsPasswordRole) 196 return user->needsPassword; 197 198 // return empty value 199 return QVariant(); 200 } 201 disableAvatarsThreshold() const202 int UserModel::disableAvatarsThreshold() const { 203 return mainConfig.Theme.DisableAvatarsThreshold.get(); 204 } 205 containsAllUsers() const206 bool UserModel::containsAllUsers() const { 207 return d->containsAllUsers; 208 } 209 } 210