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

releng/10.0releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since a7474ab was a7474ab, checked in by Ken Moore <ken@…>, 10 months ago

Make sure that when running setxkbmap we only pass in the flags for values that are not empty

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