This repository has been archived by the owner on Dec 4, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 60
/
pcdm-backend.cpp
607 lines (548 loc) · 19.6 KB
/
pcdm-backend.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
/* PCDM Login Manager:
* Written by Ken Moore (ken@pcbsd.org) 2012/2013
* Copyright(c) 2013 by the PC-BSD Project
* Available under the 3-clause BSD license
*/
#include <QProcess>
#include <QProcessEnvironment>
#include "pcdm-backend.h"
#include "pcdm-config.h"
#include "pcbsd-utils.h"
QStringList displaynameList,usernameList,homedirList,usershellList,instXNameList,instXBinList,instXCommentList,instXIconList,instXDEList;
QString logFile;
QString saveX,saveUsername, lastUser, lastDE;
QStringList Backend::getAvailableDesktops(){
if(instXNameList.isEmpty()){ loadXSessionsData(); }
QStringList out = instXNameList;
return out;
}
QString Backend::getDesktopComment(QString xName){
if(instXNameList.isEmpty()){ loadXSessionsData(); }
int index = instXNameList.indexOf(xName);
if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " + xName); return ""; }
return instXCommentList[index];
}
QString Backend::getNLDesktopName(QString xName){
//Get the non-localized desktop name from the localized version
if(instXNameList.isEmpty()){ loadXSessionsData(); }
int index = instXNameList.indexOf(xName);
if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " +xName); return ""; }
return instXDEList[index];
}
QString Backend::getDesktopIcon(QString xName){
if(instXNameList.isEmpty()){ loadXSessionsData(); }
int index = instXNameList.indexOf(xName);
if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " +xName); return ""; }
return instXIconList[index];
}
QString Backend::getDesktopBinary(QString xName){
if(instXNameList.isEmpty()){ loadXSessionsData(); }
int index = instXNameList.indexOf(xName);
if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " + xName); return ""; }
return instXBinList[index];
}
QStringList Backend::getSystemUsers(){
if(usernameList.isEmpty()){
readSystemUsers();
}
return displaynameList;
}
QString Backend::getALUsername(){
//Make sure the requested user is valid
readSystemUsers(); //first read the available users on this system
QString ruser = Config::autoLoginUsername();
int index = usernameList.indexOf(ruser);
if(index == -1){ //invalid username
//Check if a display name was given instead
index = displaynameList.indexOf(ruser);
if(index == -1){ //invalid display name
log("Invalid Auto-Login user requested - skipping....");
ruser.clear();
}else{
//use the valid username for the given display name
ruser = usernameList[index];
}
}
return ruser;
}
QString Backend::getALPassword(){
QString rpassword = Config::autoLoginPassword();
return rpassword;
}
QString Backend::getUsernameFromDisplayname(QString dspname){
int i = displaynameList.indexOf(dspname);
if(i == -1){ i = usernameList.indexOf(dspname); }
if(i == -1){ return ""; }
else{ return usernameList[i]; }
}
QString Backend::getDisplayNameFromUsername(QString username){
int i = usernameList.indexOf(username);
if(i==-1){ i = displaynameList.indexOf(username); } //make sure it was not a display name passed in
if(i==-1){ return ""; }
else{
return displaynameList[i];
}
}
QString Backend::getUserHomeDir(QString username){
int i = usernameList.indexOf(username);
if( i == -1 ){ i = displaynameList.indexOf(username); }
return homedirList[i];
}
QString Backend::getUserShell(QString username){
int i = usernameList.indexOf(username);
if( i == -1 ){ i = displaynameList.indexOf(username); }
return usershellList[i];
}
QStringList Backend::keyModels()
{
QStringList _models;
QString code, desc, line;
Process p(QStringList() << "xkeyboard-models");
if (p.waitForFinished()) {
while (p.canReadLine()) {
line = p.readLine();
code = line;
code.truncate(line.indexOf(" "));
desc = line.remove(0, line.indexOf(" "));
_models.append(desc.simplified() + " - (" + code.simplified() + ")");
}
}
return _models;
}
QStringList Backend::keyLayouts()
{
QStringList _layouts;
QString code, desc, line;
Process p(QStringList() << "xkeyboard-layouts");
if (p.waitForFinished()) {
while (p.canReadLine()) {
line = p.readLine();
code = line;
code.truncate(line.indexOf(" "));
desc = line.remove(0, line.indexOf(" "));
_layouts.append(desc.simplified() + " - (" + code.simplified() + ")");
}
}
return _layouts;
}
// Function which gets the key Variants for the target layout
QStringList Backend::keyVariants(const QString &layout, QStringList &savedKeyVariants)
{
QStringList _variants;
QString code, desc, line;
if ( savedKeyVariants.empty() )
{
Process p(QStringList() << "xkeyboard-variants");
if (p.waitForFinished()) {
while (p.canReadLine()) {
line = p.readLine();
savedKeyVariants << line;
}
}
}
for (int i = 0; i < savedKeyVariants.size(); ++i) {
// Look for variants for this particular layout
line = savedKeyVariants.at(i);
if ( line.indexOf(" " + layout + ":") != -1 )
{
code = line.simplified();
code.truncate(code.indexOf(" "));
desc = line.remove(0, line.indexOf(": ") + 1);
_variants.append(desc.simplified() + " - (" + code.simplified() + ")");
}
}
return _variants;
}
// Function which lets us run setxkbmap
bool Backend::changeKbMap(QString model, QString layout, QString variant)
{
QProcess kbp;
kbp.setProcessChannelMode(QProcess::MergedChannels);
QStringList args;
QString prog;
prog = "setxkbmap";
if(!model.isEmpty()){ args << "-model" << model; }
if(!layout.isEmpty()){ args << "-layout" << layout; }
if(!variant.isEmpty()){ args << "-variant" << variant; }
Backend::log("setxkbmap: " + args.join(" "));
kbp.start(prog, args);
kbp.waitForFinished();
bool ok = (kbp.exitCode() == 0);
if(!ok){
Backend::log("setxkbmap Failed: "+QString(kbp.readAllStandardOutput()) );
}
return ok;
}
QStringList Backend::languages()
{
QStringList _languages;
_languages.append("Default - (en_US)"); //make sure this is always at the top of the list
QString code, desc, line;
QFile mFile;
mFile.setFileName("/usr/share/pc-sysinstall/conf/avail-langs");
if ( ! mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return QStringList();
// Read in the meta-file for categories
QTextStream in(&mFile);
in.setCodec("UTF-8");
while ( !in.atEnd() ) {
line = in.readLine();
code = line;
code.truncate(line.indexOf(" "));
desc = line.remove(0, line.indexOf(" "));
_languages.append(desc.simplified() + " - (" + code.simplified() + ")");
}
mFile.close();
return _languages;
}
void Backend::openLogFile(QString logFilePath){
//If a log file exists, move it to *.old
if(QFile::exists(logFilePath)){
if(QFile::exists(logFilePath+".old")){ QFile::remove(logFilePath+".old"); }
QFile::rename(logFilePath, logFilePath+".old");
}
//save the path to the logfile
logFile = logFilePath;
}
void Backend::log(QString line){
QFile lFile(logFile);
lFile.open(QIODevice::Append);
QTextStream out(&lFile);
out << line << "\n";
lFile.close();
}
void Backend::checkLocalDirs(){
//Check for directories first
QString base = "/usr/local/share/PCDM";
QDir mainDir(base);
if(!mainDir.exists()){ mainDir.mkdir(base); }
if(!mainDir.exists("themes")){ mainDir.mkdir("themes"); }
//Check for sample files
if(!mainDir.exists("pcdm.conf.sample")){ QFile::copy(":samples/pcdm.conf",base+"/pcdm.conf.sample"); }
//Check for the PCDM runtime directory
mainDir.cd(DBDIR);
if(!mainDir.exists()){ mainDir.mkpath(DBDIR); }
}
QString Backend::getLastUser(){
//Load the file if necessary
if(lastUser.isEmpty()){
readSystemLastLogin();
}
//return the value
QString user = getDisplayNameFromUsername(lastUser);
return user;
}
QString Backend::getLastDE(QString user){
if(lastDE.isEmpty()){
readSystemLastLogin();
}
QString de = readUserLastDesktop(user);
if(de.isEmpty()){ return lastDE; }
else{ return de; }
}
void Backend::saveLoginInfo(QString user, QString desktop){
writeSystemLastLogin(user,desktop); //save the system file (DBDIR/lastlogin)
writeUserLastDesktop(user,desktop); //save the user file (~/.lastlogin)
}
void Backend::readDefaultSysEnvironment(QString &lang, QString &keymodel, QString &keylayout, QString &keyvariant){
//Set the default values
lang = "en_US";
keymodel = "pc104";
keylayout = "us";
keyvariant = "";
//Read the current inputs file and overwrite default values
QFile file(DBDIR+"defaultInputs");
bool goodFile=false;
if(file.exists()){
if(file.open(QIODevice::ReadOnly | QIODevice::Text) ){
QTextStream in(&file);
while(!in.atEnd()){
QString line = in.readLine();
QString var = line.section("=",0,0).simplified();
QString val = line.section("=",1,1).simplified();
if(var=="Lang"){ lang = val;}
else if(var=="KeyModel"){ keymodel = val; }
else if(var=="KeyLayout"){ keylayout = val; }
else if(var=="KeyVariant"){ keyvariant = val; }
}
file.close();
}
}
if(!goodFile){
//Save our own defaults
saveDefaultSysEnvironment(lang, keymodel, keylayout, keyvariant);
}
}
void Backend::saveDefaultSysEnvironment(QString lang, QString keymodel, QString keylayout, QString keyvariant){
QFile file(DBDIR+"defaultInputs");
//Make sure the containing directory exists
if(!QFile::exists(DBDIR)){
QDir dir;
dir.mkpath(DBDIR);
}
//Now save the file
if(file.open(QIODevice::WriteOnly | QIODevice::Text) ){
QTextStream out(&file);
out << "Lang=" + lang + "\n";
out << "KeyModel=" + keymodel + " \n";
out << "KeyLayout=" + keylayout + " \n";
out << "KeyVariant=" + keyvariant + " \n";
file.close();
}
}
bool Backend::writeFile(QString fileName, QStringList contents){
//Open the file with .tmp extension
QFile file(fileName+".tmp");
if( !file.open(QIODevice::WriteOnly | QIODevice::Text) ){
qDebug() << fileName+".tmp: Failure -- Could not open file";
return false;
}
//Write the file
QTextStream ofile(&file); //start the output stream
for(int i=0; i<contents.length(); i++){
ofile << contents[i];
ofile << "\n";
}
//Close the File
file.close();
//Remove any existing file with the final name/location
if( QFile::exists(fileName) ){
if( !QFile::remove(fileName) ){
qDebug() << fileName+": Error -- Could not overwrite existing file";
QFile::remove(fileName+".tmp");
return false;
}
}
//Move the temporary file into its final location
if( !file.rename(fileName) ){
qDebug() << fileName+": Error: Could not rename "+fileName+".tmp as "+fileName;
return false;
}
//Return success
QString extra = QDir::homePath(); //remove this from the filename display
qDebug() << "Saved:" << fileName.replace(extra,"~");
return true;;
}
//****** PRIVATE FUNCTIONS ******
void Backend::loadXSessionsData(){
//Clear the current variables
instXNameList.clear(); instXBinList.clear();
instXCommentList.clear(); instXIconList.clear();
instXDEList.clear();
//Load the default paths/locale
QString xDir = Config::xSessionsDir();
QStringList paths = QString(getenv("PATH")).split(":");
if(paths.isEmpty()){ paths <<"/usr/local/bin" << "/usr/local/sbin" << "/usr/bin" << "/usr/sbin" << "/bin" << "/sbin"; }
if(!xDir.endsWith("/")){ xDir.append("/"); }
QString xIconDir = Config::xSessionsImageDir();
if(!xIconDir.endsWith("/")){ xIconDir.append("/"); }
QString localeCode = QLocale().name(); //gets the current locale code
//Find all *.desktop files
QDir dir(xDir);
QStringList deFiles = dir.entryList(QDir::Files);
deFiles = deFiles.filter(".desktop"); //only get *.desktop files
//Read each file to see if that desktop is installed
for(int i=0; i<deFiles.length(); i++){
QStringList tmp = readXSessionsFile(xDir+deFiles[i],localeCode);
//tmp[exec, name, comment, icon, tryexec]
if(!tmp.isEmpty()){
//Complete file paths if necessary
//if(!tmp[0].startsWith("/")){ tmp[0] = "/usr/local/bin/"+tmp[0]; }
if(!tmp[3].startsWith("/")&&!tmp[3].startsWith(":")&&!tmp[3].isEmpty()){ tmp[3] = xIconDir+tmp[3]; }
if(!tmp[4].startsWith("/") && !QFile::exists(tmp[4])){
for(int p=0; p<paths.length(); p++){
if(QFile::exists(paths[p]+"/"+tmp[4])){
tmp[4] = paths[p]+"/"+tmp[4];
}
}
}
//Check for valid DE using the "tryexec" line
//this allows for special startup commands on the "exec" line
if(QFile::exists(tmp[4])){
//Add the DE to list of installed xsessions
instXBinList << tmp[0];
instXNameList << tmp[1];
instXCommentList << tmp[2];
instXDEList << tmp[5]; //Non-localized name of the DE
//Check to make sure we have a valid icon
if(!tmp[3].isEmpty() && !QFile::exists(tmp[3]) ){ tmp[3] = ""; }
instXIconList << tmp[3];
Backend::log( "PCDM: Found xsession: " + tmp.join(" ") );
}
}
}
if(instXNameList.isEmpty()){
//Create an entry so that we know this function has been run already
instXNameList << "None";
instXBinList << "none";
instXCommentList << "No xSession Environments available in" << xDir;
instXIconList << ":images/nodesktop.png";
}
}
QStringList Backend::readXSessionsFile(QString filePath, QString locale){
//output: [Exec, Localized Name, Localized Comment, Icon, TryExec]
QString name, lname, comm, lcomm, icon, exec, tryexec;
QStringList output;
QString lna = "Name["+locale+"]"; //variable to look at for localized name
QString lco = "Comment["+locale+"]"; //variable to look at for localized comment
QFile file(filePath);
if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream in(&file);
while (!in.atEnd()){
QString line = in.readLine().simplified();
QString var = line.section("=",0,0,QString::SectionSkipEmpty).simplified();
QString val = line.section("=",1,50,QString::SectionSkipEmpty).simplified();
if(var.toLower()=="exec"){ exec = val; }
else if(var.toLower()=="tryexec"){ tryexec = val; }
else if(var.toLower()=="icon"){ icon = val; }
else if(var.toLower()=="name"){ name = val; }
else if(var.toLower()=="comment"){ comm = val; }
else if(var==lna){ lname = val; }
else if(var==lco){ lcomm = val; }
else{} //do nothing with other lines
}
}
//Use the unlocalized name/comment if localized values not detected
if(lname.isEmpty()){ lname = name; }
if(lcomm.isEmpty()){ lcomm = comm; }
//Make sure that we have a name/exec for the session, otherwise invalid file
if(lname.isEmpty() || exec.isEmpty() || tryexec.isEmpty()){ return output; }
//Check that there is an icon given
if(icon.isEmpty()){
//Try to use a built in icon if a known DE
if(name.toLower().contains("gnome")){icon = ":images/gnome.png"; }
if(name.toLower().contains("kde")){icon = ":images/kde.png"; }
if(name.toLower().contains("xfce")){icon = ":images/xfce.png"; }
if(name.toLower().contains("lxde")){icon = ":images/lxde.png"; }
if(name.toLower().contains("fluxbox")){icon = ":images/fluxbox.png"; }
if(name.toLower().contains("openbox")){icon = ":images/openbox.png"; }
}
//Format the results into the output list
output.clear();
output << exec << lname << lcomm << icon << tryexec << name;
return output;
}
void Backend::readSystemUsers(){
//make sure the lists are empty
usernameList.clear(); displaynameList.clear(); homedirList.clear();
QStringList uList;
bool usepw = true; //for testing purposes
if(usepw){
//Use "getent" to get all possible users
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("MM_CHARSET","UTF-8");
p.setProcessEnvironment(env);
p.start("getent passwd");
while(p.state()==QProcess::Starting || p.state() == QProcess::Running){
p.waitForFinished(200);
QCoreApplication::processEvents();
}
uList = QString( p.readAllStandardOutput() ).split("\n");
//Remove all users that have:
for(int i=0; i<uList.length(); i++){
bool bad = FALSE;
// "nologin" as their shell
if(uList[i].section(":",6,6).contains("nologin")){bad=TRUE;}
// "nonexistent" as their user directory
else if(uList[i].section(":",5,5).contains("nonexistent")){bad=TRUE;}
// uid > 1000
else if(uList[i].section(":",2,2).toInt() < 1000){bad=TRUE;}
//See if it failed any checks
if(bad){ uList.removeAt(i); i--; }
else{
//Add this user to the lists if it is good
usernameList << uList[i].section(":",0,0).simplified();
displaynameList << uList[i].section(":",4,4).simplified();
homedirList << uList[i].section(":",5,5).simplified();
usershellList << uList[i].section(":",6,6).simplified();
}
}
}else{
//Get all the users from the file "/etc/passwd"
QFile PWF("/etc/passwd");
if( PWF.open(QIODevice::ReadOnly | QIODevice::Text) ){
QTextStream in(&PWF);
in.setCodec( "UTF-8" );
while( !in.atEnd() ){
uList << QString( in.readLine() );
}
PWF.close();
}
//Remove all users that have:
for(int i=0; i<uList.length(); i++){
bool bad = FALSE;
// "nologin" as their shell
if(uList[i].section(":",6,6).contains("nologin")){bad=TRUE;}
// "nonexistent" as their user directory
else if(uList[i].section(":",5,5).contains("nonexistent")){bad=TRUE;}
// uid > 1000
else if(uList[i].section(":",2,2).toInt() < 1000){bad=TRUE;}
//See if it failed any checks
if(bad){ uList.removeAt(i); i--; }
else{
//Add this user to the lists if it is good
usernameList << uList[i].section(":",0,0).simplified();
displaynameList << uList[i].section(":",4,4).simplified();
homedirList << uList[i].section(":",5,5).simplified();
usershellList << uList[i].section(":",6,6).simplified();
}
}
}
}
void Backend::readSystemLastLogin(){
if(!QFile::exists(DBDIR+"lastlogin")){
lastUser.clear();
Backend::log("PCDM: No previous login data found");
}else{
//Load the previous login data
QFile file(DBDIR+"lastlogin");
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
Backend::log("PCDM: Unable to open previous login data file");
}else{
QTextStream in(&file);
lastUser= in.readLine();
lastDE= in.readLine();
file.close();
}
}
}
void Backend::writeSystemLastLogin(QString user, QString desktop){
QFile file1(DBDIR+"lastlogin");
if(!file1.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
Backend::log("PCDM: Unable to save last login data to system directory");
}else{
QTextStream out(&file1);
out << user << "\n" << desktop;
file1.close();
}
}
QString Backend::readUserLastDesktop(QString user){
QString desktop;
QString LLpath = Backend::getUserHomeDir(user) + "/.lastlogin";
if(!QFile::exists(LLpath)){
Backend::log("PCDM: No previous user login data found for user: "+user);
}else{
//Load the previous login data
QFile file(LLpath);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
Backend::log("PCDM: Unable to open previous user login file: "+user);
}else{
QTextStream in(&file);
desktop = in.readLine();
file.close();
}
}
return desktop;
}
void Backend::writeUserLastDesktop(QString user, QString desktop){
QFile file2( Backend::getUserHomeDir(user) + "/.lastlogin" );
if(!file2.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
Backend::log("PCDM: Unable to save last login data for user:"+user);
}else{
QTextStream out(&file2);
out << desktop;
file2.close();
}
}