source: src-qt4/PCDM/src/pcdm-backend.cpp @ 4eecf13

releng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1releng/10.1.1
Last change on this file since 4eecf13 was 4eecf13, checked in by Ken Moore <ken@…>, 13 months ago

Add a bit of debugging to the PCDM keyboard layout switcher.

  • Property mode set to 100644
File size: 16.2 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,usershellList,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  if(i == -1){ i = usernameList.indexOf(dspname); }
79 
80  if(i == -1){ return ""; }
81  else{ return usernameList[i]; }
82}
83
84QString Backend::getDisplayNameFromUsername(QString username){
85  int i = usernameList.indexOf(username);
86  return displaynameList[i]; 
87}
88
89QString Backend::getUserHomeDir(QString username){
90  int i = usernameList.indexOf(username);
91  if( i == -1 ){ i = displaynameList.indexOf(username); }
92  return homedirList[i];
93}
94
95QString Backend::getUserShell(QString username){
96  int i = usernameList.indexOf(username);
97  if( i == -1 ){ i = displaynameList.indexOf(username); }
98  return usershellList[i];     
99}
100
101QStringList Backend::keyModels()
102{
103    QStringList _models;
104    QString code, desc, line;
105
106    Process p(QStringList() << "xkeyboard-models");
107
108    if (p.waitForFinished()) {
109        while (p.canReadLine()) {
110            line = p.readLine();
111            code = line;
112            code.truncate(line.indexOf(" "));
113            desc = line.remove(0, line.indexOf(" "));
114            _models.append(desc.simplified() + " - (" + code.simplified() + ")");
115        }
116    }
117    return _models;
118}
119
120QStringList Backend::keyLayouts()
121{
122    QStringList _layouts;
123    QString code, desc, line;
124
125    Process p(QStringList() << "xkeyboard-layouts");
126
127    if (p.waitForFinished()) {
128        while (p.canReadLine()) {
129            line = p.readLine();
130            code = line;
131            code.truncate(line.indexOf(" "));
132            desc = line.remove(0, line.indexOf(" "));
133            _layouts.append(desc.simplified() + " - (" + code.simplified() + ")");
134        }
135    }
136    return _layouts;
137}
138
139// Function which gets the key Variants for the target layout
140QStringList Backend::keyVariants(const QString &layout, QStringList &savedKeyVariants)
141{
142    QStringList _variants;
143    QString code, desc, line;
144
145    if ( savedKeyVariants.empty() )
146    {
147      Process p(QStringList() << "xkeyboard-variants");
148      if (p.waitForFinished()) {
149        while (p.canReadLine()) {
150            line = p.readLine();
151            savedKeyVariants << line;
152        }
153      }
154    }
155
156    for (int i = 0; i < savedKeyVariants.size(); ++i) {
157       // Look for variants for this particular layout
158       line = savedKeyVariants.at(i);
159       if ( line.indexOf(" " + layout + ":") != -1 )
160       {
161         code = line.simplified();
162         code.truncate(code.indexOf(" "));
163         desc = line.remove(0, line.indexOf(": ") + 1);
164         _variants.append(desc.simplified() + " - (" + code.simplified() + ")");
165       }
166    }
167
168    return _variants;
169}
170
171// Function which lets us run setxkbmap
172bool Backend::changeKbMap(QString model, QString layout, QString variant)
173{
174   QProcess kbp;
175        kbp.setProcessChannelMode(QProcess::MergedChannels);
176   QStringList args;
177   QString prog;
178   prog = "setxkbmap"; 
179   if(!model.isEmpty()){ args << "-model" << model; }
180   if(!layout.isEmpty()){ args << "-layout" << layout; }
181   if(!variant.isEmpty()){ args << "-variant" << variant; }
182   Backend::log("setxkbmap: " + args.join(" "));
183   kbp.start(prog, args);
184   kbp.waitForFinished();
185   bool ok = (kbp.exitCode() == 0);
186   if(!ok){
187     qDebug() << "Change KB Map Failed: "<<prog+" "+args.join(" ");
188     qDebug() << kbp.readAllStandardOutput();
189   }
190   return ok;
191}
192
193QStringList Backend::languages()
194{
195    QStringList _languages;
196    _languages.append("Default - (en_US)"); //make sure this is always at the top of the list
197    QString code, desc, line;
198
199    QFile mFile;
200    mFile.setFileName("/usr/share/pc-sysinstall/conf/avail-langs");
201    if ( ! mFile.open(QIODevice::ReadOnly | QIODevice::Text))
202       return QStringList();
203
204    // Read in the meta-file for categories
205    QTextStream in(&mFile);
206    in.setCodec("UTF-8");
207    while ( !in.atEnd() ) {
208       line = in.readLine();
209       code = line;
210       code.truncate(line.indexOf(" "));
211       desc = line.remove(0, line.indexOf(" "));
212        _languages.append(desc.simplified() + " - (" + code.simplified() + ")");
213    }
214    mFile.close();
215    return _languages;
216}
217
218void Backend::openLogFile(QString logFilePath){
219  //If a log file exists, remove it
220  if(QFile::exists(logFilePath)){ QFile::remove(logFilePath); }
221  //save the path to the logfile
222  logFile = logFilePath;
223}
224
225void Backend::log(QString line){
226  QFile lFile(logFile);
227  lFile.open(QIODevice::Append);
228  QTextStream out(&lFile);
229  out << line << "\n";
230  lFile.close();
231}
232
233void Backend::checkLocalDirs(){
234  //Check for directories first
235  QString base = "/usr/local/share/PCDM";
236  QDir mainDir(base);
237  if(!mainDir.exists()){ mainDir.mkdir(base); }
238  if(!mainDir.exists("themes")){ mainDir.mkdir("themes"); }
239  //Check for sample files
240  if(!mainDir.exists("pcdm.conf.sample")){ QFile::copy(":samples/pcdm.conf",base+"/pcdm.conf.sample"); } 
241  //Check for the PCDM runtime directory
242  mainDir.cd("/var/db/pcdm");
243  if(!mainDir.exists()){ mainDir.mkdir("/var/db/pcdm"); }
244}
245
246QString Backend::getLastUser(){
247  //Load the file if necessary
248  if(lastUser.isEmpty()){
249    readSystemLastLogin(); 
250  }
251  //return the value
252  return lastUser;
253}
254
255QString Backend::getLastDE(QString user){
256  if(lastDE.isEmpty()){
257    readSystemLastLogin();
258  }
259  QString de = readUserLastDesktop(user);
260  if(de.isEmpty()){ return lastDE; }
261  else{ return de; }
262 
263}
264
265void Backend::saveLoginInfo(QString user, QString desktop){
266  writeSystemLastLogin(user,desktop); //save the system file (/usr/local/share/PCDM/.lastlogin)
267  writeUserLastDesktop(user,desktop); //save the user file (~/.lastlogin)
268}
269
270void Backend::readDefaultSysEnvironment(QString &lang, QString &keymodel, QString &keylayout, QString &keyvariant){
271  //Set the default values
272    lang = "en_US";
273    keymodel = "pc104";
274    keylayout = "us";
275    keyvariant = "";
276  //Read the current inputs file and overwrite default values
277  QFile file("/var/db/pcdm/defaultInputs");
278  bool goodFile=false;
279  if(file.exists()){
280    if(file.open(QIODevice::ReadOnly | QIODevice::Text) ){
281      QTextStream in(&file);
282      while(!in.atEnd()){
283        QString line = in.readLine();
284        QString var = line.section("=",0,0).simplified();
285        QString val = line.section("=",1,1).simplified();
286        if(var=="Lang"){ lang = val;}
287        else if(var=="KeyModel"){ keymodel = val; }
288        else if(var=="KeyLayout"){ keylayout = val; }
289        else if(var=="KeyVariant"){ keyvariant = val; }
290      }
291      file.close();
292    }
293  }
294  if(!goodFile){
295    //Save our own defaults
296    saveDefaultSysEnvironment(lang, keymodel, keylayout, keyvariant);
297  }     
298}
299
300void Backend::saveDefaultSysEnvironment(QString lang, QString keymodel, QString keylayout, QString keyvariant){
301  QFile file("/var/db/pcdm/defaultInputs");
302    if(file.open(QIODevice::WriteOnly | QIODevice::Text) ){
303      QTextStream out(&file);
304      out << "Lang=" + lang + "\n";
305      out << "KeyModel=" + keymodel + " \n";
306      out << "KeyLayout=" + keylayout + " \n";
307      out << "KeyVariant=" + keyvariant + " \n";
308      file.close();
309    }
310}
311
312//****** PRIVATE FUNCTIONS ******
313
314void Backend::loadXSessionsData(){
315  //Clear the current variables
316  instXNameList.clear(); instXBinList.clear(); 
317  instXCommentList.clear(); instXIconList.clear();
318  //Load the default paths/locale
319  QString xDir = Config::xSessionsDir();
320  QStringList paths = QString(getenv("PATH")).split(":");
321  if(paths.isEmpty()){ paths <<"/usr/local/bin" << "/usr/local/sbin" << "/usr/bin" << "/usr/sbin" << "/bin" << "/sbin"; }
322  if(!xDir.endsWith("/")){ xDir.append("/"); }
323  QString xIconDir = Config::xSessionsImageDir();
324  if(!xIconDir.endsWith("/")){ xIconDir.append("/"); }
325  QString localeCode = QLocale().name(); //gets the current locale code
326  //Find all *.desktop files
327  QDir dir(xDir);
328  QStringList deFiles = dir.entryList(QDir::Files);
329  deFiles = deFiles.filter(".desktop"); //only get *.desktop files
330  //Read each file to see if that desktop is installed
331  for(int i=0; i<deFiles.length(); i++){
332    QStringList tmp = readXSessionsFile(xDir+deFiles[i],localeCode);
333    //tmp[exec, name, comment, icon, tryexec]
334    if(!tmp.isEmpty()){
335      //Complete file paths if necessary
336      //if(!tmp[0].startsWith("/")){ tmp[0] = "/usr/local/bin/"+tmp[0]; }
337      if(!tmp[3].startsWith("/")&&!tmp[3].startsWith(":")&&!tmp[3].isEmpty()){ tmp[3] = xIconDir+tmp[3]; }
338      if(!tmp[4].startsWith("/") && !QFile::exists(tmp[4])){ 
339        for(int p=0; p<paths.length(); p++){
340          if(QFile::exists(paths[p]+"/"+tmp[4])){
341            tmp[4] = paths[p]+"/"+tmp[4];
342          }
343        }
344      }
345      //Check for valid DE using the "tryexec" line
346        //this allows for special startup commands on the "exec" line
347      if(QFile::exists(tmp[4])){
348        //Add the DE to list of installed xsessions
349        instXBinList << tmp[0];
350        instXNameList << tmp[1];
351        instXCommentList << tmp[2];
352        //Check to make sure we have a valid icon
353        if(!tmp[3].isEmpty() && !QFile::exists(tmp[3]) ){ tmp[3] = ""; }
354        instXIconList << tmp[3];
355        Backend::log( "PCDM: Found xsession: " + tmp.join(" ") );
356      }
357    }
358  }
359  if(instXNameList.isEmpty()){
360    //Create an entry so that we know this function has been run already
361    instXNameList << "None";
362    instXBinList << "none";
363    instXCommentList << "No xSession Environments available in" << xDir;
364    instXIconList << ":images/nodesktop.png";
365  }
366}
367
368QStringList Backend::readXSessionsFile(QString filePath, QString locale){
369//output: [Exec, Localized Name, Localized Comment, Icon, TryExec]
370  QString name, lname, comm, lcomm, icon, exec, tryexec;
371  QStringList output;
372  QString lna = "Name["+locale+"]"; //variable to look at for localized name
373  QString lco = "Comment["+locale+"]"; //variable to look at for localized comment
374  QFile file(filePath);
375  if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
376    QTextStream in(&file);
377    while (!in.atEnd()){
378      QString line = in.readLine().simplified();
379      QString var = line.section("=",0,0,QString::SectionSkipEmpty).simplified();
380      QString val = line.section("=",1,50,QString::SectionSkipEmpty).simplified();
381      if(var.toLower()=="exec"){ exec = val; }
382      else if(var.toLower()=="tryexec"){ tryexec = val; }
383      else if(var.toLower()=="icon"){ icon = val; }
384      else if(var.toLower()=="name"){ name = val; }
385      else if(var.toLower()=="comment"){ comm = val; }
386      else if(var==lna){ lname = val; }
387      else if(var==lco){ lcomm = val; }
388      else{} //do nothing with other lines
389
390    }
391  }
392  //Use the unlocalized name/comment if localized values not detected
393  if(lname.isEmpty()){ lname = name; }
394  if(lcomm.isEmpty()){ lcomm = comm; }
395  //Make sure that we have a name/exec for the session, otherwise invalid file
396  if(lname.isEmpty() || exec.isEmpty() || tryexec.isEmpty()){ return output; }
397  //Check that there is an icon given
398  if(icon.isEmpty()){
399    //Try to use a built in icon if a known DE
400    if(name.toLower().contains("gnome")){icon = ":images/gnome.png"; }
401    if(name.toLower().contains("kde")){icon = ":images/kde.png"; }
402    if(name.toLower().contains("xfce")){icon = ":images/xfce.png"; }
403    if(name.toLower().contains("lxde")){icon = ":images/lxde.png"; }
404    if(name.toLower().contains("fluxbox")){icon = ":images/fluxbox.png"; }
405    if(name.toLower().contains("openbox")){icon = ":images/openbox.png"; }
406  }
407  //Format the results into the output list
408  output.clear();
409  output << exec << lname << lcomm << icon << tryexec;
410  return output;
411
412}
413
414void Backend::readSystemUsers(){
415  //make sure the lists are empty
416  usernameList.clear(); displaynameList.clear(); homedirList.clear();
417  //Get all the users from the file "/etc/passwd"
418  QStringList uList = pcbsd::Utils::runShellCommand("cat /etc/passwd");
419
420  //Remove all users that have:
421  for(int i=0; i<uList.length(); i++){
422    bool bad = FALSE;
423    // "nologin" as their shell
424    if(uList[i].section(":",6,6).contains("nologin")){bad=TRUE;}
425    // "nonexistent" as their user directory
426    else if(uList[i].section(":",5,5).contains("nonexistent")){bad=TRUE;}
427    // uid > 1000
428    else if(uList[i].section(":",2,2).toInt() < 1000){bad=TRUE;}
429
430    //See if it failed any checks
431    if(bad){ uList.removeAt(i); i--; }
432    else{
433      //Add this user to the lists if it is good
434      usernameList << uList[i].section(":",0,0).simplified();
435      displaynameList << uList[i].section(":",4,4).simplified();
436      homedirList << uList[i].section(":",5,5).simplified();
437      usershellList << uList[i].section(":",6,6).simplified();
438    }
439  }
440 
441}
442
443void Backend::readSystemLastLogin(){
444    if(!QFile::exists("/usr/local/share/PCDM/.lastlogin")){
445      lastUser.clear();
446      Backend::log("PCDM: No previous login data found");
447    }else{
448      //Load the previous login data
449      QFile file("/usr/local/share/PCDM/.lastlogin");
450      if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
451        Backend::log("PCDM: Unable to open previous login data file");   
452      }else{
453        QTextStream in(&file);
454        lastUser= in.readLine();
455        lastDE= in.readLine();
456        file.close();
457      }
458    } 
459}
460
461void Backend::writeSystemLastLogin(QString user, QString desktop){
462  QFile file1("/usr/local/share/PCDM/.lastlogin");
463  if(!file1.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
464    Backend::log("PCDM: Unable to save last login data to system directory");     
465  }else{
466    QTextStream out(&file1);
467    out << user << "\n" << desktop;
468    file1.close();
469  }
470
471}
472
473QString Backend::readUserLastDesktop(QString user){
474  QString desktop;
475  QString LLpath = Backend::getUserHomeDir(user) + "/.lastlogin";
476  if(!QFile::exists(LLpath)){
477    Backend::log("PCDM: No previous user login data found for user: "+user);
478  }else{
479    //Load the previous login data
480    QFile file(LLpath);
481    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
482      Backend::log("PCDM: Unable to open previous user login file: "+user);   
483    }else{
484      QTextStream in(&file);
485      desktop = in.readLine();
486      file.close();
487    }
488  }
489  return desktop;
490}
491
492void Backend::writeUserLastDesktop(QString user, QString desktop){
493  QFile file2( Backend::getUserHomeDir(user) + "/.lastlogin" );
494  if(!file2.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
495    Backend::log("PCDM: Unable to save last login data for user:"+user);         
496  }else{
497    QTextStream out(&file2);
498    out << desktop;
499    file2.close();
500  }
501}
Note: See TracBrowser for help on using the repository browser.