KCoreAddons

kuser_unix.cpp
1 /*
2  KUser - represent a user/account
3 
4  SPDX-FileCopyrightText: 2002 Tim Jansen <[email protected]>
5  SPDX-FileCopyrightText: 2014 Alex Richardson <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "config-accountsservice.h"
11 #include "config-getgrouplist.h"
12 #include "kcoreaddons_debug.h"
13 #include "kuser.h"
14 
15 #include <QFileInfo>
16 
17 #include <cerrno>
18 #include <grp.h>
19 #include <pwd.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 #include <algorithm> // std::find
24 #include <functional> // std::function
25 
26 #if defined(__BIONIC__) && __ANDROID_API__ < 26
27 static inline struct passwd *getpwent()
28 {
29  return nullptr;
30 }
31 inline void setpwent()
32 {
33 }
34 static inline void setgrent()
35 {
36 }
37 static inline struct group *getgrent()
38 {
39  return nullptr;
40 }
41 inline void endpwent()
42 {
43 }
44 static inline void endgrent()
45 {
46 }
47 #endif
48 
49 class KUserPrivate : public QSharedData
50 {
51 public:
52  uid_t uid;
53  gid_t gid;
54  QString loginName;
55  QString homeDir, shell;
57 
58  KUserPrivate()
59  : uid(uid_t(-1))
60  , gid(gid_t(-1))
61  {
62  }
63  KUserPrivate(const char *name)
64  : uid(uid_t(-1))
65  , gid(gid_t(-1))
66  {
67  fillPasswd(name ? ::getpwnam(name) : nullptr);
68  }
69  KUserPrivate(const passwd *p)
70  : uid(uid_t(-1))
71  , gid(gid_t(-1))
72  {
73  fillPasswd(p);
74  }
75 
76  void fillPasswd(const passwd *p)
77  {
78  if (p) {
79 #ifndef __BIONIC__
80  QString gecos = QString::fromLocal8Bit(p->pw_gecos);
81 #else
82  QString gecos = QString();
83 #endif
84  QStringList gecosList = gecos.split(QLatin1Char(','));
85  // fill up the list, should be at least 4 entries
86  while (gecosList.size() < 4) {
87  gecosList << QString();
88  }
89 
90  uid = p->pw_uid;
91  gid = p->pw_gid;
92  loginName = QString::fromLocal8Bit(p->pw_name);
93  properties[KUser::FullName] = QVariant(gecosList[0]);
94  properties[KUser::RoomNumber] = QVariant(gecosList[1]);
95  properties[KUser::WorkPhone] = QVariant(gecosList[2]);
96  properties[KUser::HomePhone] = QVariant(gecosList[3]);
97  if (uid == ::getuid() && uid == ::geteuid()) {
98  homeDir = QFile::decodeName(qgetenv("HOME"));
99  }
100  if (homeDir.isEmpty()) {
101  homeDir = QFile::decodeName(p->pw_dir);
102  }
103  shell = QString::fromLocal8Bit(p->pw_shell);
104  }
105  }
106 };
107 
109 {
110  uid_t _uid = ::getuid(), _euid;
111  if (mode == UseEffectiveUID && (_euid = ::geteuid()) != _uid) {
112  d = new KUserPrivate(::getpwuid(_euid));
113  } else {
114  d = new KUserPrivate(qgetenv("LOGNAME").constData());
115  if (d->uid != _uid) {
116  d = new KUserPrivate(qgetenv("USER").constData());
117  if (d->uid != _uid) {
118  d = new KUserPrivate(::getpwuid(_uid));
119  }
120  }
121  }
122 }
123 
124 KUser::KUser(K_UID _uid)
125  : d(new KUserPrivate(::getpwuid(_uid)))
126 {
127 }
128 
130  : d(new KUserPrivate(::getpwuid(_uid.nativeId())))
131 {
132 }
133 
134 KUser::KUser(const QString &name)
135  : d(new KUserPrivate(name.toLocal8Bit().data()))
136 {
137 }
138 
139 KUser::KUser(const char *name)
140  : d(new KUserPrivate(name))
141 {
142 }
143 
144 KUser::KUser(const passwd *p)
145  : d(new KUserPrivate(p))
146 {
147 }
148 
149 KUser::KUser(const KUser &user)
150  : d(user.d)
151 {
152 }
153 
155 {
156  d = user.d;
157  return *this;
158 }
159 
160 bool KUser::operator==(const KUser &user) const
161 {
162  return isValid() && (d->uid == user.d->uid);
163 }
164 
165 bool KUser::isValid() const
166 {
167  return d->uid != uid_t(-1);
168 }
169 
171 {
172  return KUserId(d->uid);
173 }
174 
176 {
177  return KGroupId(d->gid);
178 }
179 
180 bool KUser::isSuperUser() const
181 {
182  return d->uid == 0;
183 }
184 
186 {
187  return d->loginName;
188 }
189 
191 {
192  return d->homeDir;
193 }
194 
196 {
197  QString pathToFaceIcon;
198  if (!d->loginName.isEmpty()) {
199  pathToFaceIcon = QStringLiteral(ACCOUNTS_SERVICE_ICON_DIR) + QLatin1Char('/') + d->loginName;
200  }
201 
202  if (QFile::exists(pathToFaceIcon)) {
203  return pathToFaceIcon;
204  }
205 
206  pathToFaceIcon = homeDir() + QLatin1Char('/') + QLatin1String(".face.icon");
207 
208  if (QFileInfo(pathToFaceIcon).isReadable()) {
209  return pathToFaceIcon;
210  }
211 
212  return QString();
213 }
214 
216 {
217  return d->shell;
218 }
219 
220 template<class Func>
221 static void listGroupsForUser(const char *name, gid_t gid, uint maxCount, Func handleNextGroup)
222 {
223  if (Q_UNLIKELY(maxCount == 0)) {
224  return;
225  }
226  uint found = 0;
227 #if HAVE_GETGROUPLIST
228  QVarLengthArray<gid_t, 100> gid_buffer;
229  gid_buffer.resize(100);
230  int numGroups = gid_buffer.size();
231  int result = getgrouplist(name, gid, gid_buffer.data(), &numGroups);
232  if (result < 0 && uint(numGroups) < maxCount) {
233  // getgrouplist returns -1 if the buffer was too small to store all entries, the required size is in numGroups
234  qCDebug(KCOREADDONS_DEBUG, "Buffer was too small: %d, need %d", gid_buffer.size(), numGroups);
235  gid_buffer.resize(numGroups);
236  numGroups = gid_buffer.size();
237  getgrouplist(name, gid, gid_buffer.data(), &numGroups);
238  }
239  for (int i = 0; i < numGroups && found < maxCount; ++i) {
240  struct group *g = getgrgid(gid_buffer[i]);
241  // should never be null, but better be safe than crash
242  if (g) {
243  found++;
244  handleNextGroup(g);
245  }
246  }
247 #else
248  // fall back to getgrent() and reading gr->gr_mem
249  // This is slower than getgrouplist, but works as well
250  // add the current gid, this is often not part of g->gr_mem (e.g. build.kde.org or my openSuSE 13.1 system)
251  struct group *g = getgrgid(gid);
252  if (g) {
253  handleNextGroup(g);
254  found++;
255  if (found >= maxCount) {
256  return;
257  }
258  }
259 
260  static const auto groupContainsUser = [](struct group *g, const char *name) -> bool {
261  for (char **user = g->gr_mem; *user; user++) {
262  if (strcmp(name, *user) == 0) {
263  return true;
264  }
265  }
266  return false;
267  };
268 
269  setgrent();
270  while ((g = getgrent())) {
271  // don't add the current gid again
272  if (g->gr_gid != gid && groupContainsUser(g, name)) {
273  handleNextGroup(g);
274  found++;
275  if (found >= maxCount) {
276  break;
277  }
278  }
279  }
280  endgrent();
281 #endif
282 }
283 
284 QList<KUserGroup> KUser::groups(uint maxCount) const
285 {
286  QList<KUserGroup> result;
287  listGroupsForUser(d->loginName.toLocal8Bit().constData(), d->gid, maxCount, [&](const group *g) {
288  result.append(KUserGroup(g));
289  });
290  return result;
291 }
292 
293 QStringList KUser::groupNames(uint maxCount) const
294 {
295  QStringList result;
296  listGroupsForUser(d->loginName.toLocal8Bit().constData(), d->gid, maxCount, [&](const group *g) {
297  result.append(QString::fromLocal8Bit(g->gr_name));
298  });
299  return result;
300 }
301 
302 QVariant KUser::property(UserProperty which) const
303 {
304  return d->properties.value(which);
305 }
306 
308 {
309  QList<KUser> result;
310 
311  passwd *p;
312  setpwent();
313 
314  for (uint i = 0; i < maxCount && (p = getpwent()); ++i) {
315  result.append(KUser(p));
316  }
317 
318  endpwent();
319 
320  return result;
321 }
322 
324 {
325  QStringList result;
326 
327  passwd *p;
328  setpwent();
329 
330  for (uint i = 0; i < maxCount && (p = getpwent()); ++i) {
331  result.append(QString::fromLocal8Bit(p->pw_name));
332  }
333 
334  endpwent();
335  return result;
336 }
337 
339 {
340 }
341 
342 class KUserGroupPrivate : public QSharedData
343 {
344 public:
345  gid_t gid;
346  QString name;
347 
348  KUserGroupPrivate()
349  : gid(gid_t(-1))
350  {
351  }
352  KUserGroupPrivate(const char *_name)
353  : gid(gid_t(-1))
354  {
355  fillGroup(_name ? ::getgrnam(_name) : nullptr);
356  }
357  KUserGroupPrivate(const ::group *p)
358  : gid(gid_t(-1))
359  {
360  fillGroup(p);
361  }
362 
363  void fillGroup(const ::group *p)
364  {
365  if (p) {
366  gid = p->gr_gid;
367  name = QString::fromLocal8Bit(p->gr_name);
368  }
369  }
370 };
371 
373 {
374  d = new KUserGroupPrivate(getgrgid(KUser(mode).groupId().nativeId()));
375 }
376 
378  : d(new KUserGroupPrivate(getgrgid(_gid)))
379 {
380 }
381 
383  : d(new KUserGroupPrivate(getgrgid(_gid.nativeId())))
384 {
385 }
386 
388  : d(new KUserGroupPrivate(_name.toLocal8Bit().data()))
389 {
390 }
391 
392 KUserGroup::KUserGroup(const char *_name)
393  : d(new KUserGroupPrivate(_name))
394 {
395 }
396 
397 KUserGroup::KUserGroup(const ::group *g)
398  : d(new KUserGroupPrivate(g))
399 {
400 }
401 
403  : d(group.d)
404 {
405 }
406 
408 {
409  d = group.d;
410  return *this;
411 }
412 
413 bool KUserGroup::operator==(const KUserGroup &group) const
414 {
415  return isValid() && (d->gid == group.d->gid);
416 }
417 
419 {
420  return d->gid != gid_t(-1);
421 }
422 
424 {
425  return KGroupId(d->gid);
426 }
427 
429 {
430  return d->name;
431 }
432 
433 static void listGroupMembers(gid_t gid, uint maxCount, std::function<void(passwd *)> handleNextGroupUser)
434 {
435  if (maxCount == 0) {
436  return;
437  }
438  struct group *g = getgrgid(gid);
439  if (!g) {
440  return;
441  }
442  uint found = 0;
443  QVarLengthArray<uid_t> addedUsers;
444  struct passwd *p = nullptr;
445  for (char **user = g->gr_mem; *user; user++) {
446  if ((p = getpwnam(*user))) {
447  addedUsers.append(p->pw_uid);
448  handleNextGroupUser(p);
449  found++;
450  if (found >= maxCount) {
451  break;
452  }
453  }
454  }
455 
456  // gr_mem doesn't contain users where the primary group == gid -> we have to iterate over all users
457  setpwent();
458  while ((p = getpwent()) && found < maxCount) {
459  if (p->pw_gid != gid) {
460  continue; // only need primary gid since otherwise gr_mem already contains this user
461  }
462  // make sure we don't list a user twice
463  if (std::find(addedUsers.cbegin(), addedUsers.cend(), p->pw_uid) == addedUsers.cend()) {
464  handleNextGroupUser(p);
465  found++;
466  }
467  }
468  endpwent();
469 }
470 
471 QList<KUser> KUserGroup::users(uint maxCount) const
472 {
473  QList<KUser> result;
474  listGroupMembers(d->gid, maxCount, [&](const passwd *p) {
475  result.append(KUser(p));
476  });
477  return result;
478 }
479 
480 QStringList KUserGroup::userNames(uint maxCount) const
481 {
482  QStringList result;
483  listGroupMembers(d->gid, maxCount, [&](const passwd *p) {
484  result.append(QString::fromLocal8Bit(p->pw_name));
485  });
486  return result;
487 }
488 
490 {
491  QList<KUserGroup> result;
492 
493  ::group *g;
494  setgrent();
495 
496  for (uint i = 0; i < maxCount && (g = getgrent()); ++i) {
497  result.append(KUserGroup(g));
498  }
499 
500  endgrent();
501 
502  return result;
503 }
504 
506 {
507  QStringList result;
508 
509  ::group *g;
510  setgrent();
511 
512  for (uint i = 0; i < maxCount && (g = getgrent()); ++i) {
513  result.append(QString::fromLocal8Bit(g->gr_name));
514  }
515 
516  endgrent();
517 
518  return result;
519 }
520 
522 {
523 }
524 
526 {
527  if (name.isEmpty()) {
528  return KUserId();
529  }
530  QByteArray name8Bit = name.toLocal8Bit();
531  struct passwd *p = ::getpwnam(name8Bit.constData());
532  if (!p) {
533  qCWarning(KCOREADDONS_DEBUG, "Failed to lookup user %s: %s", name8Bit.constData(), strerror(errno));
534  return KUserId();
535  }
536  return KUserId(p->pw_uid);
537 }
538 
540 {
541  if (name.isEmpty()) {
542  return KGroupId();
543  }
544  QByteArray name8Bit = name.toLocal8Bit();
545  struct group *g = ::getgrnam(name8Bit.constData());
546  if (!g) {
547  qCWarning(KCOREADDONS_DEBUG, "Failed to lookup group %s: %s", name8Bit.constData(), strerror(errno));
548  return KGroupId();
549  }
550  return KGroupId(g->gr_gid);
551 }
552 
554 {
555  return KUserId(getuid());
556 }
557 
559 {
560  return KUserId(geteuid());
561 }
562 
564 {
565  return KGroupId(getgid());
566 }
567 
569 {
570  return KGroupId(getegid());
571 }
KUserGroup(const QString &name)
Create an object from a group name.
Definition: kuser_unix.cpp:387
QVarLengthArray::const_iterator cend() const const
static QList< KUser > allUsers(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:307
bool isValid() const
Returns whether the group is valid.
Definition: kuser_unix.cpp:418
static KGroupId currentGroupId()
Definition: kuser_unix.cpp:563
QString name() const
The name of the group.
Definition: kuser_unix.cpp:428
void append(const T &t)
static KGroupId currentEffectiveGroupId()
Definition: kuser_unix.cpp:568
const T * constData() const const
QVariant property(UserProperty which) const
Returns an extended property.
Definition: kuser_unix.cpp:302
Represents a group on your system.
Definition: kuser.h:457
static KUserId currentUserId()
Definition: kuser_unix.cpp:553
bool exists() const const
static KGroupId fromName(const QString &name)
Definition: kuser_unix.cpp:539
int size() const const
static QStringList allUserNames(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:323
Represents a user on your system.
Definition: kuser.h:225
bool operator==(const KUser &user) const
Two KUser objects are equal if the userId() are identical.
Definition: kuser_unix.cpp:160
static KUserId fromName(const QString &name)
Definition: kuser_unix.cpp:525
QString fromLocal8Bit(const char *str, int size)
void append(const T &value)
KGuiItem properties()
QList< KUser > users(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:471
QString loginName() const
The login name of the user.
Definition: kuser_unix.cpp:185
bool isEmpty() const const
const char * constData() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool operator==(const KUserGroup &group) const
Two KUserGroup objects are equal if their gid()s are identical.
Definition: kuser_unix.cpp:413
KUserGroup & operator=(const KUserGroup &group)
Copies a group.
Definition: kuser_unix.cpp:407
QStringList userNames(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:480
KGroupId groupId() const
Definition: kuser_unix.cpp:175
static KUserId currentEffectiveUserId()
Definition: kuser_unix.cpp:558
KUserId userId() const
Definition: kuser_unix.cpp:170
QByteArray toLocal8Bit() const const
UIDMode
Definition: kuser.h:228
QString homeDir() const
The path to the user&#39;s home directory.
Definition: kuser_unix.cpp:190
A platform independent group ID.
Definition: kuser.h:168
QString shell() const
The path to the user&#39;s login shell.
Definition: kuser_unix.cpp:215
bool isSuperUser() const
Checks whether the user is the super user (root).
Definition: kuser_unix.cpp:180
~KUserGroup()
Destructor.
Definition: kuser_unix.cpp:521
QStringList groupNames(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:293
~KUser()
Destructor.
Definition: kuser_unix.cpp:338
QList< KUserGroup > groups(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:284
static QList< KUserGroup > allGroups(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:489
KGroupId groupId() const
Definition: kuser_unix.cpp:423
K_GID gid() const
Returns the group id of the user.
Definition: kuser.h:333
KUser & operator=(const KUser &user)
Copies a user.
Definition: kuser_unix.cpp:154
bool isValid() const
Returns true if the user is valid.
Definition: kuser_unix.cpp:165
static QStringList allGroupNames(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:505
QVarLengthArray::const_iterator cbegin() const const
KUser(UIDMode mode=UseEffectiveUID)
Creates an object that contains information about the current user.
Definition: kuser_unix.cpp:108
int size() const const
void resize(int size)
QString faceIconPath() const
The path to the user&#39;s face file.
Definition: kuser_unix.cpp:195
QString decodeName(const QByteArray &localFileName)
A platform independent user ID.
Definition: kuser.h:133
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Apr 10 2021 23:01:55 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.