]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-qt/QLightDM/usersmodel.cpp
9c1fe9f21cb71107242b93c0cfebdfc62bb1059b
[sojka/lightdm.git] / liblightdm-qt / QLightDM / usersmodel.cpp
1 /*
2  * Copyright (C) 2010-2011 David Edmundson.
3  * Author: David Edmundson <kde@davidedmundson.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; either version 3 of the License, or (at your option) any
8  * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
9  * license.
10  */
11
12 #include "usersmodel.h"
13 #include "user.h"
14
15 #include <pwd.h>
16 #include <errno.h>
17
18 #include <QtCore/QString>
19 #include <QtCore/QFileSystemWatcher>
20 #include <QtCore/QFile>
21 #include <QtCore/QDir>
22 #include <QtCore/QSettings>
23 #include <QtCore/QDebug>
24
25 #include <QtGui/QPixmap>
26
27 using namespace QLightDM;
28
29 class UsersModelPrivate {
30 public:
31     QList<User> users;
32 };
33
34 UsersModel::UsersModel(QObject *parent) :
35     QAbstractListModel(parent),
36     d (new UsersModelPrivate())
37 {
38     //load users on startup and if the password file changes.
39     QFileSystemWatcher *watcher = new QFileSystemWatcher(this);
40     watcher->addPath("/etc/passwd"); //FIXME harcoded path
41     connect(watcher, SIGNAL(fileChanged(QString)), SLOT(loadUsers()));
42
43     loadUsers();
44 }
45
46 UsersModel::~UsersModel()
47 {
48     delete d;
49 }
50
51
52 int UsersModel::rowCount(const QModelIndex &parent) const
53 {
54     return d->users.count();
55 }
56
57 QVariant UsersModel::data(const QModelIndex &index, int role) const
58 {
59     if (!index.isValid()) {
60         return QVariant();
61     }
62
63     int row = index.row();
64     switch (role) {
65     case Qt::DisplayRole:
66         return d->users[row].displayName();
67     case Qt::DecorationRole:
68         return QPixmap(d->users[row].image());
69     case UsersModel::NameRole:
70         return d->users[row].name();
71     case UsersModel::RealNameRole:
72         return d->users[row].realName();
73     case UsersModel::LoggedInRole:
74         return d->users[row].isLoggedIn();
75     }
76
77     return QVariant();
78 }
79
80
81 QList<User> UsersModel::getUsers()
82 {
83     QString file = "/etc/lightdm/users.conf";
84     qDebug() << "Loading user configuration from " << file;
85     QSettings *settings = new QSettings(file, QSettings::IniFormat);
86
87     int minimumUid = settings->value("UserAccounts/minimum-uid", QVariant(500)).toInt();
88     QStringList hiddenShells;
89     if (settings->contains("UserAccounts/hidden-shells"))
90         hiddenShells = settings->value("UserAccounts/hidden-shells").toString().split(" ");
91     else
92         hiddenShells = QStringList() << "/bin/false" << "/usr/sbin/nologin";
93     QStringList hiddenUsers;
94     if (settings->contains("UserAccounts/hidden-users"))
95         hiddenUsers = settings->value("UserAccounts/hidden-users").toString().split(" ");
96     else
97         hiddenUsers = QStringList() << "nobody" << "nobody4" << "noaccess";
98
99     QList<User> users;
100
101     setpwent();
102     while(TRUE)
103     {
104         struct passwd *entry;
105         QStringList tokens;
106         QString realName, image;
107         QFile *imageFile;
108         int i;
109
110         errno = 0;
111         entry = getpwent();
112         if(!entry) {
113             break;
114         }
115
116         /* Ignore system users */
117         if(entry->pw_uid < minimumUid) {
118             continue;
119         }
120
121         /* Ignore users disabled by shell */
122         if(entry->pw_shell) {
123             if (hiddenShells.contains(entry->pw_shell)) {
124                 continue;
125             }
126         }
127
128         if (hiddenUsers.contains(entry->pw_name)) {
129             continue;
130         }
131
132         tokens = QString(entry->pw_gecos).split(",");
133         if(tokens.size() > 0 && tokens.at(i) != "")
134             realName = tokens.at(i);
135
136         //replace this with QFile::exists();
137         QDir homeDir(entry->pw_dir);
138         imageFile = new QFile(homeDir.filePath(".face"));
139         if(!imageFile->exists()) {
140             delete imageFile;
141             imageFile = new QFile(homeDir.filePath(".face.icon"));
142         }
143         if(imageFile->exists())
144         {
145             delete imageFile;
146             imageFile = NULL;
147         }
148
149         User user(entry->pw_name, realName, entry->pw_dir, imageFile->fileName(), false);
150         users.append(user);
151     }
152
153     if(errno != 0) {
154         qDebug() << "Failed to read password database: " << strerror(errno);
155     }
156     endpwent();
157
158     delete settings;
159
160     return users;
161 }
162
163 void UsersModel::loadUsers()
164 {
165     QList<User> usersToAdd;
166
167     //FIXME accidently not got the "if contact removed" code. Need to restore that.
168     //should call beginRemoveRows, and then remove the row from the model.
169     //might get rid of "User" object, keep as private object (like sessionsmodel) - or make it copyable.
170
171     //loop through all the new list of users, if it's in the list already update it (or do nothing) otherwise append to list of new users
172     QList<User> newUserList = getUsers();
173
174     foreach(const User &user, newUserList) {
175         bool alreadyInList = false;
176         for(int i=0; i < d->users.size(); i++) {
177             if (user.name() == d->users[i].name()) {
178                 alreadyInList = true;
179                 d->users[i].update(user.name(), user.homeDirectory(), user.image(), user.isLoggedIn());
180                 QModelIndex index = createIndex(i,0);
181                 dataChanged(index, index);
182             }
183         }
184
185         if (!alreadyInList) {
186             usersToAdd.append(user);
187         }
188     }
189
190     //loop through all the existing users, if they're not in the newly gathered list, remove them.
191
192     //FIXME this isn't perfect, looping like this in a mutating list - use mutable iterator.
193     for (int i=0; i < d->users.size() ; i++) {
194         bool found = false;
195         foreach(const User &user, newUserList) {
196             if (d->users[i].name() == user.name()) {
197                 found = true;
198             }
199         }
200
201         if (!found) {
202             beginRemoveRows(QModelIndex(), i, i);
203             d->users.removeAt(i);
204             endRemoveRows();
205         }
206     }
207
208     //append new users
209     if (usersToAdd.size() > 0) {
210         beginInsertRows(QModelIndex(), d->users.size(), d->users.size() + usersToAdd.size() -1);
211         d->users.append(usersToAdd);
212         endInsertRows();
213     }
214 }