source: src-qt4/PCDM/src/pcdm-backend.cpp @ 90dc161

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2
Last change on this file since 90dc161 was 90dc161, checked in by Ken Moore <ken@…>, 12 months ago

Completely revamp how PCDM uses the previous login information. Now you no longer need to specify which desktop to auto-login to if you have auto-login selected. It will first try to check for previous login information for that user, then fall back on the last login for the system in order to try and auto login. Also fix the PCDMd script a bit so that autologins work properly when triggered.

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/* PCDM Login Manager:
2*  Written by Ken Moore (ken@pcbsd.org) 2012/2013
3*  Copyright(c) 2013 by the PC-BSD Project
4*  Available under the 3-clause BSD license
5*/
6
7#include <QProcess>
8#include <QProcessEnvironment>
9
10#include "pcdm-backend.h"
11#include "pcdm-config.h"
12#include "pcbsd-utils.h"
13
14QStringList displaynameList,usernameList,homedirList,instXNameList,instXBinList,instXCommentList,instXIconList;
15QString logFile;
16QString saveX,saveUsername, lastUser, lastDE;
17
18QStringList Backend::getAvailableDesktops(){ 
19  if(instXNameList.isEmpty()){ loadXSessionsData(); }
20  QStringList out = instXNameList;
21  return out;
22}
23
24QString Backend::getDesktopComment(QString xName){
25  if(instXNameList.isEmpty()){ loadXSessionsData(); }
26  int index = instXNameList.indexOf(xName);
27  if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " + xName); return ""; }
28  return instXCommentList[index];
29}
30
31QString Backend::getDesktopIcon(QString xName){
32  if(instXNameList.isEmpty()){ loadXSessionsData(); }
33  int index = instXNameList.indexOf(xName);
34  if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " +xName); return ""; }
35  return instXIconList[index];
36}
37
38QString Backend::getDesktopBinary(QString xName){
39  if(instXNameList.isEmpty()){ loadXSessionsData(); }
40  int index = instXNameList.indexOf(xName);
41  if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " + xName); return ""; }
42  return instXBinList[index];
43}
44
45QStringList Backend::getSystemUsers(){
46  if(usernameList.isEmpty()){
47    readSystemUsers();
48  }
49  return displaynameList;
50}
51
52QString Backend::getALUsername(){
53  //Make sure the requested user is valid
54  readSystemUsers(); //first read the available users on this system
55  QString ruser = Config::autoLoginUsername();
56  int index = usernameList.indexOf(ruser);
57  if(index == -1){ //invalid username
58    //Check if a display name was given instead
59    index = displaynameList.indexOf(ruser);
60    if(index == -1){ //invalid display name
61      log("Invalid Auto-Login user requested - skipping....");
62      ruser.clear();
63    }else{
64      //use the valid username for the given display name
65      ruser = usernameList[index]; 
66    }
67  }
68  return ruser;
69}
70
71QString Backend::getALPassword(){
72  QString rpassword = Config::autoLoginPassword();
73  return rpassword;
74}
75
76QString Backend::getUsernameFromDisplayname(QString dspname){
77  int i = displaynameList.indexOf(dspname);
78  return usernameList[i];
79}
80
81QString Backend::getDisplayNameFromUsername(QString username){
82  int i = usernameList.indexOf(username);
83  return displaynameList[i]; 
84}
85
86QString Backend::getUserHomeDir(QString username){
87  int i = usernameList.indexOf(username);
88  if( i == -1 ){ i = displaynameList.indexOf(username); }
89  return homedirList[i];
90}
91
92QStringList Backend::keyModels()
93{
94    QStringList _models;
95    QString code, desc, line;
96
97    Process p(QStringList() << "xkeyboard-models");
98
99    if (p.waitForFinished()) {
100        while (p.canReadLine()) {
101            line = p.readLine();
102            code = line;
103            code.truncate(line.indexOf(" "));
104            desc = line.remove(0, line.indexOf(" "));
105            _models.append(desc.simplified() + " - (" + code.simplified() + ")");
106        }
107    }
108    return _models;
109}
110
111QStringList Backend::keyLayouts()
112{
113    QStringList _layouts;
114    QString code, desc, line;
115
116    Process p(QStringList() << "xkeyboard-layouts");
117
118    if (p.waitForFinished()) {
119        while (p.canReadLine()) {
120            line = p.readLine();
121            code = line;
122            code.truncate(line.indexOf(" "));
123            desc = line.remove(0, line.indexOf(" "));
124            _layouts.append(desc.simplified() + " - (" + code.simplified() + ")");
125        }
126    }
127    return _layouts;
128}
129
130// Function which gets the key Variants for the target layout
131QStringList Backend::keyVariants(const QString &layout, QStringList &savedKeyVariants)
132{
133    QStringList _variants;
134    QString code, desc, line;
135
136    if ( savedKeyVariants.empty() )
137    {
138      Process p(QStringList() << "xkeyboard-variants");
139      if (p.waitForFinished()) {
140        while (p.canReadLine()) {
141            line = p.readLine();
142            savedKeyVariants << line;
143        }
144      }
145    }
146
147    for (int i = 0; i < savedKeyVariants.size(); ++i) {
148       // Look for variants for this particular layout
149       line = savedKeyVariants.at(i);
150       if ( line.indexOf(" " + layout + ":") != -1 )
151       {
152         code = line.simplified();
153         code.truncate(code.indexOf(" "));
154         desc = line.remove(0, line.indexOf(": ") + 1);
155         _variants.append(desc.simplified() + " - (" + code.simplified() + ")");
156       }
157    }
158
159    return _variants;
160}
161
162// Function which lets us run setxkbmap
163void Backend::changeKbMap(QString model, QString layout, QString variant)
164{
165   QProcess kbp;
166   QStringList args;
167   QString prog;
168   prog = "setxkbmap"; 
169   args << "-model" << model << "-layout" << layout << "-variant" << variant;
170   Backend::log("setxkbmap: " + args.join(" "));
171   kbp.start(prog, args);
172   kbp.waitForFinished();
173}
174
175QStringList Backend::languages()
176{
177    QStringList _languages;
178    QString code, desc, line;
179
180    QFile mFile;
181    mFile.setFileName("/usr/share/pc-sysinstall/conf/avail-langs");
182    if ( ! mFile.open(QIODevice::ReadOnly | QIODevice::Text))
183       return QStringList();
184
185    // Read in the meta-file for categories
186    QTextStream in(&mFile);
187    in.setCodec("UTF-8");
188    while ( !in.atEnd() ) {
189       line = in.readLine();
190       code = line;
191       code.truncate(line.indexOf(" "));
192       desc = line.remove(0, line.indexOf(" "));
193        _languages.append(desc.simplified() + " - (" + code.simplified() + ")");
194    }
195    mFile.close();
196    return _languages;
197}
198
199void Backend::openLogFile(QString logFilePath){
200  //If a log file exists, remove it
201  if(QFile::exists(logFilePath)){ QFile::remove(logFilePath); }
202  //save the path to the logfile
203  logFile = logFilePath;
204}
205
206void Backend::log(QString line){
207  QFile lFile(logFile);
208  lFile.open(QIODevice::Append);
209  QTextStream out(&lFile);
210  out << line << "\n";
211  lFile.close();
212}
213
214void Backend::checkLocalDirs(){
215  //Check for directories first
216  QString base = "/usr/local/share/PCDM";
217  QDir mainDir(base);
218  if(!mainDir.exists()){ mainDir.mkdir(base); }
219  if(!mainDir.exists("themes")){ mainDir.mkdir("themes"); }
220  //Check for sample files
221  if(!mainDir.exists("pcdm.conf.sample")){ QFile::copy(":samples/pcdm.conf",base+"/pcdm.conf.sample"); } 
222}
223
224QString Backend::getLastUser(){
225  //Load the file if necessary
226  if(lastUser.isEmpty()){
227    readSystemLastLogin(); 
228  }
229  //return the value
230  return lastUser;
231}
232
233QString Backend::getLastDE(QString user){
234  if(lastDE.isEmpty()){
235    readSystemLastLogin();
236  }
237  QString de = readUserLastDesktop(user);
238  if(de.isEmpty()){ return lastDE; }
239  else{ return de; }
240 
241}
242
243void Backend::saveLoginInfo(QString user, QString desktop){
244  writeSystemLastLogin(user,desktop); //save the system file (/usr/local/share/PCDM/.lastlogin)
245  writeUserLastDesktop(user,desktop); //save the user file (~/.lastlogin)
246}
247
248//****** PRIVATE FUNCTIONS ******
249
250void Backend::loadXSessionsData(){
251  //Clear the current variables
252  instXNameList.clear(); instXBinList.clear(); 
253  instXCommentList.clear(); instXIconList.clear();
254  //Load the default paths/locale
255  QString xDir = Config::xSessionsDir();
256  if(!xDir.endsWith("/")){ xDir.append("/"); }
257  QString xIconDir = Config::xSessionsImageDir();
258  if(!xIconDir.endsWith("/")){ xIconDir.append("/"); }
259  QString localeCode = QLocale().name(); //gets the current locale code
260  //Find all *.desktop files
261  QDir dir(xDir);
262  QStringList deFiles = dir.entryList(QDir::Files);
263  deFiles = deFiles.filter(".desktop"); //only get *.desktop files
264  //Read each file to see if that desktop is installed
265  for(int i=0; i<deFiles.length(); i++){
266    QStringList tmp = readXSessionsFile(xDir+deFiles[i],localeCode);
267    //tmp[exec, name, comment, icon, tryexec]
268    if(!tmp.isEmpty()){
269      //Complete file paths if necessary
270      //if(!tmp[0].startsWith("/")){ tmp[0] = "/usr/local/bin/"+tmp[0]; }
271      if(!tmp[3].startsWith("/")&&!tmp[3].startsWith(":")&&!tmp[3].isEmpty()){ tmp[3] = xIconDir+tmp[3]; }
272      if(!tmp[4].startsWith("/")){ tmp[4] = "/usr/local/bin/"+tmp[4]; }
273      //Check for valid DE using the "tryexec" line
274        //this allows for special startup commands on the "exec" line
275      if(QFile::exists(tmp[4])){
276        //Add the DE to list of installed xsessions
277        instXBinList << tmp[0];
278        instXNameList << tmp[1];
279        instXCommentList << tmp[2];
280        //Check to make sure we have a valid icon
281        if(!tmp[3].isEmpty() && !QFile::exists(tmp[3]) ){ tmp[3] = ""; }
282        instXIconList << tmp[3];
283        Backend::log( "PCDM: Found xsession: " + tmp.join(" ") );
284      }
285    }
286  }
287  if(instXNameList.isEmpty()){
288    //Create an entry so that we know this function has been run already
289    instXNameList << "None";
290    instXBinList << "none";
291    instXCommentList << "No xSession Environments available in" << xDir;
292    instXIconList << ":images/nodesktop.png";
293  }
294}
295
296QStringList Backend::readXSessionsFile(QString filePath, QString locale){
297//output: [Exec, Localized Name, Localized Comment, Icon, TryExec]
298  QString name, lname, comm, lcomm, icon, exec, tryexec;
299  QStringList output;
300  QString lna = "Name["+locale+"]"; //variable to look at for localized name
301  QString lco = "Comment["+locale+"]"; //variable to look at for localized comment
302  QFile file(filePath);
303  if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
304    QTextStream in(&file);
305    while (!in.atEnd()){
306      QString line = in.readLine().simplified();
307      QString var = line.section("=",0,0,QString::SectionSkipEmpty).simplified();
308      QString val = line.section("=",1,50,QString::SectionSkipEmpty).simplified();
309      if(var.toLower()=="exec"){ exec = val; }
310      else if(var.toLower()=="tryexec"){ tryexec = val; }
311      else if(var.toLower()=="icon"){ icon = val; }
312      else if(var.toLower()=="name"){ name = val; }
313      else if(var.toLower()=="comment"){ comm = val; }
314      else if(var==lna){ lname = val; }
315      else if(var==lco){ lcomm = val; }
316      else{} //do nothing with other lines
317
318    }
319  }
320  //Use the unlocalized name/comment if localized values not detected
321  if(lname.isEmpty()){ lname = name; }
322  if(lcomm.isEmpty()){ lcomm = comm; }
323  //Make sure that we have a name/exec for the session, otherwise invalid file
324  if(lname.isEmpty() || exec.isEmpty() || tryexec.isEmpty()){ return output; }
325  //Check that there is an icon given
326  if(icon.isEmpty()){
327    //Try to use a built in icon if a known DE
328    if(name.toLower().contains("gnome")){icon = ":images/gnome.png"; }
329    if(name.toLower().contains("kde")){icon = ":images/kde.png"; }
330    if(name.toLower().contains("xfce")){icon = ":images/xfce.png"; }
331    if(name.toLower().contains("lxde")){icon = ":images/lxde.png"; }
332    if(name.toLower().contains("fluxbox")){icon = ":images/fluxbox.png"; }
333    if(name.toLower().contains("openbox")){icon = ":images/openbox.png"; }
334  }
335  //Format the results into the output list
336  output.clear();
337  output << exec << lname << lcomm << icon << tryexec;
338  return output;
339
340}
341
342void Backend::readSystemUsers(){
343  //make sure the lists are empty
344  usernameList.clear(); displaynameList.clear(); homedirList.clear();
345  //Get all the users from the file "/etc/passwd"
346  QStringList uList = pcbsd::Utils::runShellCommand("cat /etc/passwd");
347
348  //Remove all users that have:
349  for(int i=0; i<uList.length(); i++){
350    bool bad = FALSE;
351    // "nologin" as their shell
352    if(uList[i].section(":",6,6).contains("nologin")){bad=TRUE;}
353    // "nonexistent" as their user directory
354    else if(uList[i].section(":",5,5).contains("nonexistent")){bad=TRUE;}
355    // uid > 1000
356    else if(uList[i].section(":",2,2).toInt() < 1000){bad=TRUE;}
357
358    //See if it failed any checks
359    if(bad){ uList.removeAt(i); i--; }
360    else{
361      //Add this user to the lists if it is good
362      usernameList << uList[i].section(":",0,0).simplified();
363      displaynameList << uList[i].section(":",4,4).simplified();
364      homedirList << uList[i].section(":",5,5).simplified();
365    }
366  }
367 
368}
369
370void Backend::readSystemLastLogin(){
371    if(!QFile::exists("/usr/local/share/PCDM/.lastlogin")){
372      lastUser.clear();
373      Backend::log("PCDM: No previous login data found");
374    }else{
375      //Load the previous login data
376      QFile file("/usr/local/share/PCDM/.lastlogin");
377      if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
378        Backend::log("PCDM: Unable to open previous login data file");   
379      }else{
380        QTextStream in(&file);
381        lastUser= in.readLine();
382        lastDE= in.readLine();
383        file.close();
384      }
385    } 
386}
387
388void Backend::writeSystemLastLogin(QString user, QString desktop){
389  QFile file1("/usr/local/share/PCDM/.lastlogin");
390  if(!file1.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
391    Backend::log("PCDM: Unable to save last login data to system directory");     
392  }else{
393    QTextStream out(&file1);
394    out << user << "\n" << desktop;
395    file1.close();
396  }
397
398}
399
400QString Backend::readUserLastDesktop(QString user){
401  QString desktop;
402  QString LLpath = Backend::getUserHomeDir(user) + "/.lastlogin";
403  if(!QFile::exists(LLpath)){
404    Backend::log("PCDM: No previous user login data found for user: "+user);
405  }else{
406    //Load the previous login data
407    QFile file(LLpath);
408    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
409      Backend::log("PCDM: Unable to open previous user login file: "+user);   
410    }else{
411      QTextStream in(&file);
412      desktop = in.readLine();
413      file.close();
414    }
415  }
416  return desktop;
417}
418
419void Backend::writeUserLastDesktop(QString user, QString desktop){
420  QFile file2( Backend::getUserHomeDir(user) + "/.lastlogin" );
421  if(!file2.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
422    Backend::log("PCDM: Unable to save last login data for user:"+user);         
423  }else{
424    QTextStream out(&file2);
425    out << desktop;
426    file2.close();
427  }
428}
Note: See TracBrowser for help on using the repository browser.