//=========================================== // Lumina-desktop source code // Copyright (c) 2017, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== #include "DesktopSettings.h" #include <QFile> #include <QDir> #include <QDebug> #include <unistd.h> #include <pwd.h> #include <grp.h> #define FILEPREFIX QString("/lumina-desktop/desktop/") // === PUBLIC === DesktopSettings::DesktopSettings(QObject *parent) : QObject(parent){ watcher = 0; runmode = DesktopSettings::UserFull; } DesktopSettings::~DesktopSettings(){ if(!files.isEmpty()){ stop(); } } //Start/stop routines void DesktopSettings::start(){ files.clear(); settings.clear(); //clear the internal hashes (just in case) if(watcher==0){ watcher = new QFileSystemWatcher(this); connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChanged(QString)) ); connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)) ); } parseSystemSettings(); //set the runmode appropriately locateFiles(); // } void DesktopSettings::stop(){ QStringList watch; watch << watcher->files() << watcher->directories(); if(!watch.isEmpty()){ watcher->removePaths(watch); } files.clear(); //clear the internal hash settings.clear(); } //Main Read/Write functions QVariant DesktopSettings::value(DesktopSettings::File file, QString variable, QVariant defaultvalue){ if(!files.contains(file)){ return defaultvalue; } for(int i=0; i<files[file].length(); i++){ if( settings.contains(files[file][i])){ //make sure this file is in the settings hash if(settings[files[file][i]]->contains(variable)){ //if this file does not have the variable - go to the next one return settings[files[file][i]]->value(variable, defaultvalue); } } } return defaultvalue; //none of the files contain the variable - return the default } bool DesktopSettings::setValue(DesktopSettings::File file, QString variable, QVariant value){ if(!files.contains(file)){ return false; } for(int i=0; i<files[file].length(); i++){ if( settings.contains(files[file][i])){ //make sure this file is in the settings hash if(settings[files[file][i]]->isWritable() ){ //Check write permissions settings[files[file][i]]->setValue(variable, value); settings[files[file][i]]->sync(); //make sure this is synced to disk ASAP return true; } } } return false; } QStringList DesktopSettings::keys(DesktopSettings::File file){ if(!files.contains(file)){ return QStringList(); } QStringList keyList; for(int i=0; i<files[file].length(); i++){ if( settings.contains(files[file][i])){ //make sure this file is in the settings hash keyList << settings[files[file][i]]->allKeys(); } } keyList.removeDuplicates(); return keyList; } //Information functions QStringList DesktopSettings::filePaths(DesktopSettings::File file){ if(!files.contains(file)){ return QStringList(); } return files.value(file); } //=== PRIVATE === void DesktopSettings::parseSystemSettings(){ //run at start - determine the RunMode for this user/session //This will also load the DesktopSettings::System file into the hashes runmode = DesktopSettings::UserFull; //default value unless otherwise specified QStringList dirs; dirs << QString(getenv("XDG_CONFIG_DIRS")).split(":") << QString(getenv("XDG_DATA_DIRS")).split(":"); for(int i=0; i<dirs.length(); i++){ if(dirs[i].endsWith("/xdg")){ dirs[i] = dirs[i].section("/",0,-2); } //Chop off the xdg subdir - need the generic configuration directory QString path = dirs[i]+rel_path(DesktopSettings::System); if(!QFile::exists(path)){ continue; } //Load the system file into the hashes files.insert(DesktopSettings::System, QStringList() << path); settings.insert(path, new QSettings(path, QSettings::IniFormat, this) ); //Read off the default mode for the system QString defMode = settings[path]->value("default_mode","fulluser").toString().toLower(); if(defMode=="fullsystem"){ runmode= DesktopSettings::SystemFull; } else if(defMode=="staticinterface"){ runmode = DesktopSettings::SystemInterface; } //Now determine the runmode for this user struct passwd *pw = getpwuid(getuid()); if(pw!=0){ QString cuser = QString(pw->pw_name); free(pw); //done with this structure if( settings[path]->value("fulluser_users", QStringList()).toStringList().contains(cuser) ){ runmode = DesktopSettings::UserFull; } else if( settings[path]->value("fullsystem_users", QStringList()).toStringList().contains(cuser) ){ runmode = DesktopSettings::SystemFull; } else if( settings[path]->value("staticinterface_users", QStringList()).toStringList().contains(cuser) ){ runmode = DesktopSettings::SystemInterface; } else{ //No rule found for this specific user - check the group rules //Read off the list of groups gid_t grpList[100]; int grpSize = 100; if( getgrouplist(cuser.toLocal8Bit(), getgid(), grpList, &grpSize) > 0 ){ QStringList groups; for(int i=0; i<grpSize; i++){ struct group *g = getgrgid(grpList[i]); if(g!=0){ groups << QString(g->gr_name); free(g); } } QStringList fromfile = settings[path]->value("fulluser_groups", QStringList()).toStringList(); fromfile.removeDuplicates(); if( (fromfile+groups).removeDuplicates() > 0 ){ runmode = DesktopSettings::UserFull; } else{ fromfile = settings[path]->value("fullsystem_groups", QStringList()).toStringList(); fromfile.removeDuplicates(); if((fromfile+groups).removeDuplicates() > 0 ){ runmode = DesktopSettings::SystemFull; } else{ fromfile = settings[path]->value("staticinterface_groups", QStringList()).toStringList(); fromfile.removeDuplicates(); if((fromfile+groups).removeDuplicates() > 0 ){ runmode = DesktopSettings::SystemInterface; } } } } //end group list read } }else{ runmode = DesktopSettings::SystemFull; //could not read user name - assume system files only } break; //found this file - go ahead and stop now (no hierarchy for this special file) } //Now report the run mode that was detected QString mode; if(runmode == DesktopSettings::UserFull){ mode = "Full User"; } else if(runmode == DesktopSettings::SystemFull){ mode = "Full System"; } else if(runmode == DesktopSettings::SystemInterface){ mode = "System Interface"; } qDebug() << "Detected Lumina Runtime Mode:" << mode; } void DesktopSettings::locateFiles(){ //run at start - finds the locations of the various files (based on RunMode) QString userdir; QStringList systemdirs; userdir = QString(getenv("XDG_CONFIG_HOME")); systemdirs << QString(getenv("XDG_CONFIG_DIRS")).split(":") << QString(getenv("XDG_DATA_DIRS")).split(":"); //Load all the user-level files for this run mode QList< DesktopSettings::File > tmp; if(runmode == DesktopSettings::UserFull){ tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Keys << DesktopSettings::Theme; } else if(runmode == DesktopSettings::SystemInterface){ tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Keys << DesktopSettings::Theme; } for(int i=0; i<tmp.length(); i++){ QString path = userdir+rel_path(tmp[i]); touchFile(path); files.insert(tmp[i], QStringList() << path); settings.insert(path, new QSettings(path, QSettings::IniFormat, this) ); watcher->addPath(path); } //Now load all the system-level files tmp.clear(); tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Keys << DesktopSettings::Theme; for(int i=0; i<systemdirs.length(); i++){ if(systemdirs[i].endsWith("/xdg")){ systemdirs[i] = systemdirs[i].section("/",0,-2); } if( !QFile::exists(systemdirs[i]+"/lumina-desktop") ){ continue; } for(int j=0; j<tmp.length(); j++){ QString path = systemdirs[i]+rel_path(tmp[j]); if(QFile::exists(path)){ files.insert(tmp[j], QStringList() << path); settings.insert(path, new QSettings(path, QSettings::IniFormat, this) ); watcher->addPath(path); } } } } void DesktopSettings::touchFile(QString path){ if(QFile::exists(path)){ return; } //already exists //Make sure the parent directory exists if(!QFile::exists(path.section("/",0,-2)) ){ QDir dir; dir.mkpath(path.section("/",0,-2)); } //Now create the empty file QFile file(path); if( file.open(QIODevice::ReadWrite) ){ //if it opens successfully, then it has created the file file.close(); } } QString DesktopSettings::rel_path(DesktopSettings::File file){ QString name; switch(file){ case DesktopSettings::System: name="system"; break; case DesktopSettings::Favorites: name="favorites"; break; case DesktopSettings::Environment: name="environment"; break; case DesktopSettings::Session: name="session"; break; case DesktopSettings::Desktop: name="desktop"; break; case DesktopSettings::ContextMenu: name="contextmenu"; break; case DesktopSettings::Keys: name="keys"; break; case DesktopSettings::Theme: name="theme"; break; } return FILEPREFIX+name+".conf"; } //=== PRIVATE SLOTS === void DesktopSettings::fileChanged(QString file){ //QFileSystemWatcher change detected if(!watcher->files().contains(file)){ //Make sure this file stays watched for changes touchFile(file); watcher->addPath(file); } //Make sure the settings structure for this file is updated to match what is on disk if(settings.contains(file)){ settings[file]->sync(); } //Find which file type this is and send out the signal about it QList< DesktopSettings::File > types = files.keys(); for(int i=0; i<types.length(); i++){ if(files[types[i]].contains(file)){ emit FileModified(types[i]); break; } } } void DesktopSettings::dirChanged(QString){ //Not used yet - placeholder in case we need it later on }