From 281b2113b209bd51bd923ad8f289f4041ff03a0f Mon Sep 17 00:00:00 2001 From: tpearson Date: Sat, 15 Oct 2011 09:14:47 +0000 Subject: Initial import of kiosktool This source tree will most likely not even compile yet--stay tuned for updates! git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kiosktool@1258966 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kiosktool/kioskrun.cpp | 1687 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1687 insertions(+) create mode 100644 kiosktool/kioskrun.cpp (limited to 'kiosktool/kioskrun.cpp') diff --git a/kiosktool/kioskrun.cpp b/kiosktool/kioskrun.cpp new file mode 100644 index 0000000..f243cfc --- /dev/null +++ b/kiosktool/kioskrun.cpp @@ -0,0 +1,1687 @@ +/* + * kioskrun.cpp + * + * Copyright (C) 2004 Waldo Bastian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "kioskrun.h" + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kiosksync.h" + +#include +#define NETACCESS KIO::NetAccess + +#undef DEBUG_ENTRIES + +KioskRun *KioskRun::s_self = 0; + +KioskRun::KioskRun( QObject* parent, const char* name) + : QObject(parent, name), m_dcopClient(0), m_instance(0), m_localKdercConfig(0) +{ + m_noRestrictions = false; + m_forceSycocaUpdate = false; + s_self = this; + m_saveConfigCache.setAutoDelete(true); + m_immutableStatusCache.setAutoDelete(true); + m_homeDir = QDir::homeDirPath()+"/.kde-test"; + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + m_kderc = QFile::decodeName(args->getOption("kderc")); + m_isRoot = (getuid() == 0); +} + +KioskRun::~KioskRun() +{ + shutdownRuntimeEnv(); + s_self = 0; +} + +void +KioskRun::setUser(const QString &user) +{ + if (m_user == user) return; + + shutdownRuntimeEnv(); + shutdownConfigEnv(); + m_user = user; +} + +static void filterDupes(QStringList &list) +{ + QStringList tmp; + for(QStringList::ConstIterator it = list.begin(); + it != list.end(); ++it) + { + if (!tmp.contains(*it)) + tmp.append(*it); + } + list = tmp; +} + +void +KioskRun::setKdeDirs(const QStringList &dirs) +{ + if (m_kdeDirs == dirs) return; + + shutdownRuntimeEnv(); + shutdownConfigEnv(); + m_kdeDirs = dirs; + QStringList xdgDataDirs = QStringList::split(':', QFile::decodeName(getenv("XDG_DATA_DIRS"))); + if (xdgDataDirs.isEmpty()) + { + xdgDataDirs = QStringList::split(':', KGlobal::dirs()->kfsstnd_prefixes()); + xdgDataDirs.pop_front(); + for(QStringList::Iterator it = xdgDataDirs.begin(); + it != xdgDataDirs.end(); ++it) + { + *it += "share"; + } + xdgDataDirs << "/usr/local/share" << "/usr/share"; + } + + m_xdgDataDirs.clear(); + for(QStringList::ConstIterator it = dirs.begin(); + it != dirs.end(); ++it) + { + m_xdgDataDirs.append(*it+"/share"); + } + m_xdgDataDirs += xdgDataDirs; + filterDupes(m_xdgDataDirs); + + QStringList xdgConfigDirs = QStringList::split(':', QFile::decodeName(getenv("XDG_CONFIG_DIRS"))); + if (xdgConfigDirs.isEmpty()) + { + xdgConfigDirs << "/etc/xdg"; + QString sysconfMenuDir = KGlobal::dirs()->findDirs("xdgconf-menu", QString::null).last(); + if (sysconfMenuDir.endsWith("/menus/")) + xdgConfigDirs << sysconfMenuDir.left(sysconfMenuDir.length()-7); + + } + + m_xdgConfigDirs.clear(); + for(QStringList::ConstIterator it = dirs.begin(); + it != dirs.end(); ++it) + { + m_xdgConfigDirs.append(*it+"/etc/xdg"); + } + + m_xdgConfigDirs += xdgConfigDirs; + filterDupes(m_xdgConfigDirs); +} + +void +KioskRun::deleteDir(const QString &dir) +{ + if (dir.length() <= 1) // Safety + return; + if (!dir.startsWith("/")) // Safety + return; + Q_ASSERT(dir.startsWith(m_homeDir)); + + KProcess proc; + proc << "rm" << "-rf" << dir; + proc.start(KProcess::Block); +} + +void +KioskRun::applyEnvironment(KProcess *p) +{ + p->setEnvironment("HOME", m_homeDir); + p->setEnvironment("KDEHOME", m_homeDir+"/.kde"); + p->setEnvironment("KDEROOTHOME", m_homeDir+"/.kde"); + p->setEnvironment("KDEDIRS", m_kdeDirs.join(":")); + p->setEnvironment("XDG_DATA_HOME", m_homeDir+"/.local/share"); + p->setEnvironment("XDG_DATA_DIRS", m_xdgDataDirs.join(":")); + p->setEnvironment("XDG_CONFIG_HOME", m_homeDir+"/.config"); + p->setEnvironment("XDG_CONFIG_DIRS", m_xdgConfigDirs.join(":")); + p->setEnvironment("DCOPAUTHORITY", m_homeDir+"/.kde/DCOPserver"); + p->setEnvironment("KDE_KIOSK_NO_PROFILES", "true"); + if (m_noRestrictions) + p->setEnvironment("KDE_KIOSK_NO_RESTRICTIONS", "true"); +} + +bool +KioskRun::prepare() +{ + bool result = setupRuntimeEnv(); + + deleteDir(m_configDir); + deleteDir(locateLocal("data")); + deleteDir(m_desktopPath); + deleteDir(m_homeDir+"/.config"); + deleteDir(m_homeDir+"/.local/share"); + return result; +} + +void +KioskRun::updateSycoca() +{ + // Force update + QString sycocaUpdateFile = KioskRun::self()->locateLocal("services", "update_ksycoca"); + QFile file(sycocaUpdateFile); + file.remove(); + file.open(IO_WriteOnly); + file.close(); + + dcopRef("kded", "kbuildsycoca").call("recreate"); +} + +KProcess* +KioskRun::run(const QString &cmd, const QStringList &args) +{ + KProcess *proc = new KProcess(this); + + applyEnvironment(proc); + + *proc << cmd; + *proc << args; + + proc->start(KProcess::NotifyOnExit); + return proc; +} + +class SetEnv +{ +public: + SetEnv(const char *key, const QString &value) : m_key(key) + { + m_oldValue = getenv(m_key); + setenv(m_key, QFile::encodeName(value), 1); + } + + ~SetEnv() + { + if (m_oldValue.isEmpty()) + setenv(m_key,"",1); + else + setenv(m_key,m_oldValue.data(),1); + } + +private: + const char* m_key; + QCString m_oldValue; +}; + +void +KioskRun::setupConfigEnv() +{ + if (m_instance) return; + + // ::locateLocal must be called before we change the env. vars! + QString newTmpDir = ::locateLocal("tmp", "kioskdir"); + QString newSocketDir = ::locateLocal("socket", "kioskdir"); + + SetEnv home("HOME", m_homeDir); + QString kdeHome = m_homeDir+"/.kde"; + SetEnv kdehome("KDEHOME", kdeHome); + SetEnv kderoothome("KDEROOTHOME", kdeHome); + SetEnv kdedirs("KDEDIRS", m_kdeDirs.join(":")); + SetEnv xdgDataHome("XDG_DATA_HOME", m_homeDir+"/.local/share"); + SetEnv xdgDataDirs("XDG_DATA_DIRS", m_xdgDataDirs.join(":")); + SetEnv xdgConfigHome("XDG_CONFIG_HOME", m_homeDir+"/.config"); + SetEnv xdgConfigDirs("XDG_CONFIG_DIRS", m_xdgConfigDirs.join(":")); + + ::mkdir(QFile::encodeName(m_homeDir), 0700); + ::mkdir(QFile::encodeName(kdeHome), 0700); + + // Create temp & socket dirs. + char hostname[256]; + hostname[0] = 0; + gethostname(hostname, 255); + + QString tmpDir = QString("%1/%2-%3").arg(kdeHome).arg("tmp").arg(hostname); + deleteDir(tmpDir); + ::mkdir(QFile::encodeName(newTmpDir), 0700); + ::symlink(QFile::encodeName(newTmpDir), QFile::encodeName(tmpDir)); + + QString socketDir = QString("%1/%2-%3").arg(kdeHome).arg("socket").arg(hostname); + deleteDir(socketDir); + ::mkdir(QFile::encodeName(newSocketDir), 0700); + ::symlink(QFile::encodeName(newSocketDir), QFile::encodeName(socketDir)); + + m_configDir = QString("%1/.kde/share/config/").arg(m_homeDir); + + m_instance = new KInstance("kioskrun"); + (void) m_instance->dirs(); // Create KStandardDirs obj + + m_desktopPath = m_homeDir + "/Desktop/"; + m_desktopPath = m_instance->config()->readPathEntry( "Desktop", m_desktopPath); + m_desktopPath = QDir::cleanDirPath( m_desktopPath ); + if ( !m_desktopPath.endsWith("/") ) + m_desktopPath.append('/'); + + { + SetEnv kdehome("KDEHOME", "-"); + SetEnv kderoothome("KDEROOTHOME", "-"); + SetEnv xdgDataHome("XDG_DATA_HOME", m_xdgDataDirs.first()); + SetEnv xdgConfigHome("XDG_CONFIG_HOME", m_xdgConfigDirs.first()); + + m_saveInstance = new KInstance("kioskrun"); + (void) m_saveInstance->dirs(); // Create KStandardDirs obj + } +} + +QString +KioskRun::locate(const char *resource, const QString &filename) +{ + setupConfigEnv(); + + return m_saveInstance->dirs()->findResource(resource, filename); +} + +QString +KioskRun::locateSave(const char *resource, const QString &filename) +{ + setupConfigEnv(); + + // split path from filename + int slash = filename.findRev('/')+1; + QString dir = filename.left(slash); + QString file = filename.mid(slash); + return m_saveInstance->dirs()->saveLocation(resource, dir, false) + file; +} + +QString +KioskRun::locateLocal(const char *resource, const QString &filename) +{ + setupConfigEnv(); + + // split path from filename + int slash = filename.findRev('/')+1; + QString dir = filename.left(slash); + QString file = filename.mid(slash); + return m_instance->dirs()->saveLocation(resource, dir, true) + file; +} + + +void +KioskRun::shutdownConfigEnv() +{ + if (!m_instance) return; + + delete m_instance; + m_instance = 0; +} + +class ImmutableStatus +{ +public: + bool m_fileScope; + QDict m_lines; + QString m_tmpFile; + bool m_dirty; +}; + + +bool +KioskRun::isConfigImmutable(const QString &filename, const QString &group) +{ + (void) configFile(filename); + ImmutableStatus *status = m_immutableStatusCache.find(filename); + assert(status); + if (group.isEmpty()) + return status->m_fileScope; + + return status->m_lines.find(group); +} + +void +KioskRun::setConfigImmutable(const QString &filename, const QString &_group, bool bImmutable) +{ + (void) configFile(filename); + ImmutableStatus *status = m_immutableStatusCache.find(filename); + assert(status); + if (_group.isEmpty()) + { + if (status->m_fileScope != bImmutable) + { + status->m_fileScope = bImmutable; + status->m_dirty = true; + m_forceSycocaUpdate = true; + } + } + else + { + QString group = QString("[%1]").arg(_group); + if (status->m_lines.find(group)) + { + if (!bImmutable) + { + status->m_lines.remove(group); + status->m_dirty = true; + m_forceSycocaUpdate = true; + } + } + else + { + if (bImmutable) + { + status->m_lines.insert(group, (int *) 1); + status->m_dirty = true; + m_forceSycocaUpdate = true; + } + } + } +} + +static void stripImmutable(QString &ext) +{ + ext.replace("i", ""); + if (ext == "[$]") + ext = QString::null; +} + +static void addImmutable(QString &ext) +{ + ext.replace("[$", "[$i"); +} + +QString +KioskRun::saveImmutableStatus(const QString &filename) +{ + ImmutableStatus *status = new ImmutableStatus; + status->m_fileScope = false; + status->m_dirty = false; + m_immutableStatusCache.insert(filename, status); + + KTempFile tmp; + tmp.close(); + + QString newPath = tmp.name(); + status->m_tmpFile = tmp.name(); + + QString path = m_saveInstance->dirs()->findResource("config", filename); + if (path.isEmpty()) + return newPath; // Nothing to do + + QFile oldCfg(path); + + if (!oldCfg.open( IO_ReadOnly )) + return newPath; // Error + + QFile newCfg(newPath); + if (!newCfg.open( IO_WriteOnly )) + return newPath; // Error + + QTextStream txtIn(&oldCfg); + txtIn.setEncoding(QTextStream::UnicodeUTF8); + + QTextStream pTxtOut(&newCfg); + pTxtOut.setEncoding(QTextStream::UnicodeUTF8); + + QRegExp immutable("(\\[\\$e?ie?\\])$"); + + // TODO: Use "group+key" instead of "key" as index, otherwise it might not be unique + + while(! txtIn.atEnd()) + { + QString line = txtIn.readLine().stripWhiteSpace(); + + if (line.startsWith("#")) + { + // Comment, do nothing... + } + else if (line.startsWith("[")) + { + int pos = immutable.searchRev(line); + if (pos != -1) + { + QString group = line.left(pos); + QString ext = immutable.cap(0); + stripImmutable(ext); + if (pos == 0) + { + status->m_fileScope = true; + continue; + } + status->m_lines.replace(group, (int *)1 ); + line = group + ext; + } + } + else + { + int equal = line.find('='); + if (equal != -1) + { + QString key = line.left(equal).stripWhiteSpace(); + int pos = immutable.searchRev(key); + if (pos != -1) + { + key = key.left(pos); + QString ext = immutable.cap(0); + stripImmutable(ext); + status->m_lines.replace(key, (int *)1 ); + line = key + ext + line.mid(equal); + } + } + } + + pTxtOut << line << endl; + } + oldCfg.close(); + newCfg.close(); + + if (newCfg.status() != IO_Ok ) + { + kdWarning() << "Error writing " << newPath << endl; + return newPath; + } + return newPath; +} + +bool +KioskRun::restoreImmutableStatus(const QString &filename, bool force) +{ + ImmutableStatus *status = m_immutableStatusCache.take(filename); + if (!status) + { + kdDebug() << "KioskRun::restoreImmutableStatus(" << filename << ") status info missing" << endl; + return true; + } + if (!force && !status->m_dirty) + { + kdDebug() << "KioskRun::restoreImmutableStatus(" << filename << ") not changed" << endl; + delete status; + return true; + } + kdDebug() << "KioskRun::restoreImmutableStatus(" << filename << ") restoring" << endl; + + QString path = status->m_tmpFile; + + KSaveFile newCfg(path); + if (newCfg.status() != 0) + { + delete status; + return true; // Continue + } + + QTextStream *pTxtOut = newCfg.textStream(); + pTxtOut->setEncoding(QTextStream::UnicodeUTF8); + + QRegExp option("(\\[\\$e\\])$"); + + if (status->m_fileScope) + { + kdDebug() << "Marking file " << filename << " immutable" << endl; + (*pTxtOut) << "[$i]" << endl; + } + + QFile oldCfg(path); + if (oldCfg.open( IO_ReadOnly )) + { + + QTextStream txtIn(&oldCfg); + txtIn.setEncoding(QTextStream::UnicodeUTF8); + + while(! txtIn.atEnd()) + { + QString line = txtIn.readLine().stripWhiteSpace(); + + if (line.startsWith("#")) + { + // Comment, do nothing... + } + else if (line.startsWith("[")) + { + if (status->m_lines.take(line)) + line += "[$i]"; + } + else + { + int equal = line.find('='); + if (equal != -1) + { + QString key = line.left(equal).stripWhiteSpace(); + int pos = option.searchRev(key); + if (pos != -1) + { + key = key.left(pos); + QString ext = option.cap(0); + if (status->m_lines.take(key)) + addImmutable(ext); + line = key + ext + line.mid(equal); + } + else + { + if (status->m_lines.take(key)) + line = key + "[$i]" + line.mid(equal); + } + } + } + + (*pTxtOut) << line << endl; + } + oldCfg.close(); + } + + // Create remaining groups that were marked as immutable + QDictIterator it( status->m_lines ); + for( ; it.current(); ++it ) + { + QString group = it.currentKey(); + if ( it.current() ) + (*pTxtOut) << endl << group << "[$i]" << endl; + } + + if (!newCfg.close()) + { + kdWarning() << "Error writing" << path << endl; + delete status; + return true; // Continue + } + + QString installLocation = m_saveInstance->dirs()->saveLocation("config", QString::null, false) + filename; + if (!install(path, installLocation)) + { + m_immutableStatusCache.insert(filename, status); // Keep it around + return false; + } + delete status; + return true; +} + +bool +KioskRun::flushConfigCache() +{ + while ( !m_saveConfigCache.isEmpty() ) + { + QDictIterator it( m_saveConfigCache ); + QString file = it.currentKey(); + KConfig *config = it.current(); + bool dirty = config->isDirty(); + config->sync(); // Save + if (!restoreImmutableStatus(file, dirty)) + return false; + m_saveConfigCache.remove(file); + } + + if (m_forceSycocaUpdate) + forceSycocaUpdate(); + return true; +} + +KConfig * +KioskRun::configFile(const QString &filename) +{ + KConfig *config = m_saveConfigCache.find(filename); + if (config) + return config; + + kdDebug() << "KioskRun::configFile(" << filename << ") loading file" << endl; + + setupConfigEnv(); + + QString saveLocation = saveImmutableStatus(filename); + config = new KSimpleConfig(saveLocation); + m_saveConfigCache.insert(filename, config); + + return config; +} + +void +KioskRun::makeMutable(bool bMutable) +{ + KConfig *config = configFile("kdeglobals"); + + m_noRestrictions = bMutable; + if (KDE::version() < KDE_MAKE_VERSION(3,2,4)) + { + config->setGroup("KDE Action Restrictions"); + if (bMutable) + { + KUser thisUser; + config->writeEntry("kiosk_exception", thisUser.loginName()+":"); // This user, all hosts + } + else + { + config->writeEntry("kiosk_exception", QString::null); + } + } + // Propagate to kdeinit + dcopRef("klauncher", "klauncher").call("setLaunchEnv", + QCString("KDE_KIOSK_NO_RESTRICTIONS"), QCString(m_noRestrictions ? "true" : "")); + + setConfigImmutable("kdeglobals", "KDE Action Restrictions", true); +} + +QStringList +KioskRun::newConfigFiles() +{ + setupConfigEnv(); + + QStringList exceptions; + exceptions << "kconf_updaterc"; + + QStringList result; + QDir dir(m_configDir); + dir.setFilter( QDir::Files | QDir::NoSymLinks ); + + const QFileInfoList *list = dir.entryInfoList(); + if (!list) return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + while ( (fi = it.current()) != 0 ) + { + QString file = fi->fileName(); + if (!file.endsWith("~") && !exceptions.contains(file)) // Skip backup files & exceptions + result.append(file); + ++it; + } + return result; +} + +void +KioskRun::mergeConfigFile(const QString &filename) +{ + KConfig *saveCfg = configFile(filename); + + kdDebug() << "KioskRun::mergeConfigFile(" << (m_configDir + filename) << ")" << endl; + KSimpleConfig newCfg(m_configDir + filename); + + QStringList groups = newCfg.groupList(); + for(QStringList::ConstIterator it = groups.begin(); + it != groups.end(); ++it) + { + saveCfg->setGroup(*it); + QMap map = newCfg.entryMap(*it); + for(QMap::Iterator it2 = map.begin(); + it2 != map.end(); ++it2) + { +#ifdef DEBUG_ENTRIES +qWarning("[%s] %s --> %s", (*it).latin1(), it2.key().latin1(), it2.data().latin1()); +#endif + saveCfg->writeEntry(it2.key(), it2.data()); + } + } +} + +bool +KioskRun::setupRuntimeEnv() +{ + if (m_dcopClient) return true; + + KioskRunProgressDialog dlg(kapp->mainWidget(), "kioskrun_progress", + i18n("Setting Up Configuration Environment"), + i18n("Setting up configuration environment.")); + + char hostname[256]; + hostname[0] = 0; + gethostname(hostname, 255); + QString cacheDir = QString("%1/.kde/cache-%2").arg(m_homeDir).arg(hostname); + + deleteDir(cacheDir); + KStandardDirs::makeDir(cacheDir); + deleteDir(m_homeDir+"/.qt"); + ::unlink(QFile::encodeName(m_homeDir+".kderc")); + + + QString iceAuth = QString("%1/.ICEauthority").arg(QDir::homeDirPath()); + setenv("ICEAUTHORITY", QFile::encodeName(iceAuth), 0); // Don't overwrite existing setting + + QString xAuth = QString("%1/.Xauthority").arg(QDir::homeDirPath()); + setenv("XAUTHORITY", QFile::encodeName(xAuth), 0); // Don't overwrite existing setting + + QString dcopServerFile = m_homeDir+"/.kde/DCOPserver"; + + KProcess kdeinit; + + applyEnvironment(&kdeinit); + + kdeinit << "kdeinit"; + + connect(&kdeinit, SIGNAL(processExited(KProcess *)), &dlg, SLOT(slotFinished())); + + kdeinit.start(KProcess::NotifyOnExit); + + dlg.exec(); + + QCString dcopSrv; + QFile f(dcopServerFile); + if (f.open(IO_ReadOnly)) + { + int size = QMIN( 1024, f.size() ); // protection against a huge file + QCString contents( size+1 ); + if ( f.readBlock( contents.data(), size ) == size ) + { + contents[size] = '\0'; + int pos = contents.find('\n'); + if ( pos == -1 ) // Shouldn't happen + dcopSrv = contents; + else + dcopSrv = contents.left( pos ); + } + } + + if (dcopSrv.isEmpty()) + { + kdWarning() << "Error reading " << dcopServerFile << endl; + m_dcopClient = new DCOPClient; + shutdownRuntimeEnv(); + return false; + } + + m_dcopClient = new DCOPClient; + m_dcopClient->setServerAddress(dcopSrv); + unsetenv("DCOPSERVER"); // Don't propagate it + m_dcopClient->attach(); + return true; +} + +void +KioskRun::shutdownRuntimeEnv() +{ + if (!m_dcopClient) return; + + delete m_dcopClient; + m_dcopClient = 0; + + KProcess kdeinit; + applyEnvironment(&kdeinit); + + kdeinit << "kdeinit_shutdown"; + + kdeinit.start(KProcess::Block); + + KProcess dcopserver; + applyEnvironment(&dcopserver); + + dcopserver << "dcopserver_shutdown"; + + dcopserver.start(KProcess::Block); +} + +DCOPRef +KioskRun::dcopRef(const QCString &appId, const QCString &objId) +{ + if (!setupRuntimeEnv()) + return DCOPRef(); + DCOPRef ref(appId, objId); + ref.setDCOPClient(m_dcopClient); + return ref; +} + +// Lookup the setting for a custom action +bool +KioskRun::lookupCustomAction(const QString &action) +{ + KConfig *cfg = KioskRun::self()->configFile("kdeglobals"); + cfg->setGroup("KDE Custom Restrictions"); + return cfg->readBoolEntry(action, false); +} + +// Change the setting for a custom action +void +KioskRun::setCustomAction(const QString &action, bool checked) +{ + KConfig *cfg = KioskRun::self()->configFile("kdeglobals"); + cfg->setGroup("KDE Custom Restrictions"); + if (cfg->readBoolEntry(action, false) != checked) + { + cfg->writeEntry(action, checked); + KioskRun::self()->scheduleSycocaUpdate(); + + if (action == "restrict_file_browsing") + { + setCustomRestrictionFileBrowsing(checked); + } + } +} + +// Create directory +bool +KioskRun::createDir(const QString &dir) +{ + if (QDir(dir).exists()) + return true; // Exists already + + KURL dest; + if (!m_isRoot || (m_user != "root")) + { + dest.setProtocol("fish"); + dest.setHost("localhost"); + dest.setUser(m_user); + } + dest.setPath(dir); + + if (dir.length() > 1) + { + KURL parent = dest.upURL(); + + bool result = createDir(parent.path()); + if (!result) + return false; + } + + do + { + if (NETACCESS::exists(dest, false, kapp->mainWidget())) + return true; + + bool result = NETACCESS::mkdir(dest, kapp->mainWidget(), 0755); + if (result == true) + return true; + + QString error = NETACCESS::lastErrorString(); + QString msg; + + if (error.isEmpty()) + msg = i18n("The directory %1 could not be created because of an unspecified problem.

") + .arg(dir); + else + msg = i18n("The directory %1 could not be created because of the following problem:" + "

%2

") + .arg(dir, NETACCESS::lastErrorString()); + + msg += i18n("Without this directory your changes can not be saved.

" + "Do you want to retry creating the directory or abort the saving of changes?"); + + int msgResult = KMessageBox::warningYesNo(kapp->mainWidget(), msg, QString::null, + i18n("&Retry"), i18n("&Abort")); + + if (msgResult == KMessageBox::No) + return false; + + // Maybe the user created it in the meantime + if (QDir(dir).exists()) + return true; // Exists already + } + while (true); + return false; +} + +// Create directory +bool +KioskRun::createRemoteDirRecursive(const KURL &dest, bool ask) +{ + if (NETACCESS::exists(dest, false, kapp->mainWidget())) + return true; + + KURL parent = dest.upURL(); + + if (NETACCESS::exists(dest, false, kapp->mainWidget())) + { + return createRemoteDir(dest); + } + + if (ask) + { + // Parent doesn't exist, + int result = KMessageBox::warningContinueCancel(kapp->mainWidget(), + i18n("The directory %1 does not yet exist. " + "Do you want to create it?").arg(parent.prettyURL()), QString::null, + i18n("Create &Dir")); + if (result != KMessageBox::Continue) + return false; + } + + QString path = dest.path(1); + int i = 0; + while ( (i = path.find('/', i+1)) != -1) + { + parent.setPath(path.left(i+1)); + if (! createRemoteDir(parent)) + return false; + } + return true; +} + +// Create directory +bool +KioskRun::createRemoteDir(const KURL &dest) +{ + do + { + if (NETACCESS::exists(dest, false, kapp->mainWidget())) + return true; + + if (NETACCESS::mkdir(dest, kapp->mainWidget(), 0755)) + return true; + +#if KDE_IS_VERSION(3,2,91) + if (NETACCESS::lastError() == KIO::ERR_DIR_ALREADY_EXIST) + return true; +#endif + + //TODO Check directory already exists error + QString error = NETACCESS::lastErrorString(); + QString msg; + + if (error.isEmpty()) + msg = i18n("The directory %1 could not be created because of an unspecified problem.

") + .arg(dest.prettyURL()); + else + msg = i18n("The directory %1 could not be created because of the following problem:" + "

%2

") + .arg(dest.prettyURL(), NETACCESS::lastErrorString()); + + msg += i18n("Without this directory your files can not be uploaded.

" + "Do you want to retry creating the directory or abort uploading?"); + + int msgResult = KMessageBox::warningYesNo(kapp->mainWidget(), msg, QString::null, + i18n("&Retry"), i18n("&Abort")); + + if (msgResult == KMessageBox::No) + return false; + } + while (true); + return false; +} + +// Install file +bool +KioskRun::install(const QString &file, const QString &destination) +{ + KURL dest; + if (!m_isRoot || (m_user != "root")) + { + dest.setProtocol("fish"); + dest.setHost("localhost"); + dest.setUser(m_user); + } + dest.setPath(destination); + + if (!createDir(dest.upURL().path())) + return false; + + do + { + KURL src; + src.setPath(file); + bool result = NETACCESS::file_copy(src, dest, 0644, true, false, kapp->mainWidget()); + if (result == true) + { + ::unlink(QFile::encodeName(file)); + return true; + } + + QString error = NETACCESS::lastErrorString(); + QString msg; + if (error.isEmpty()) + msg = i18n("The file %1 could not be installed because of an unspecified problem.") + .arg(destination); + else + msg = i18n("The file %1 could not be installed because of the following problem:" + "

%2

") + .arg(destination, NETACCESS::lastErrorString()); + + msg += i18n("Do you want to retry the installation or abort the saving of changes?"); + + int msgResult = KMessageBox::warningYesNo(kapp->mainWidget(), msg, QString::null, + i18n("&Retry"), i18n("&Abort")); + + if (msgResult == KMessageBox::No) + return false; + } + while (true); + return false; +} + +// Upload file +bool +KioskRun::uploadRemote(const QString &file, const KURL &dest) +{ + do + { + KURL src; + src.setPath(file); + bool result = NETACCESS::file_copy(src, dest, 0644, true, false, kapp->mainWidget()); + if (result == true) + return true; + + QString error = NETACCESS::lastErrorString(); + QString msg; + if (error.isEmpty()) + msg = i18n("The file %1 could not be uploaded to %2 because of an unspecified problem.") + .arg(file, dest.prettyURL()); + else + msg = i18n("The file %1 could not be uploaded to %2 because of the following problem:" + "

%3

") + .arg(file, dest.prettyURL(),NETACCESS::lastErrorString()); + + msg += i18n("Do you want to retry or abort the uploading?"); + + int msgResult = KMessageBox::warningYesNo(kapp->mainWidget(), msg, QString::null, + i18n("&Retry"), i18n("&Abort")); + + if (msgResult == KMessageBox::No) + return false; + } + while (true); + return false; +} + +// Remove file +bool +KioskRun::remove(const QString &destination) +{ + KURL dest; + if (!m_isRoot || (m_user != "root")) + { + dest.setProtocol("fish"); + dest.setHost("localhost"); + dest.setUser(m_user); + } + dest.setPath(destination); + + return NETACCESS::del(dest, kapp->mainWidget()); +} + +// Move file or directory +bool +KioskRun::move(const QString &source, const QString &destination, const QStringList &files) +{ + KURL src; + KURL dest; + if (!m_isRoot || (m_user != "root")) + { + dest.setProtocol("fish"); + dest.setHost("localhost"); + dest.setUser(m_user); + src.setProtocol("fish"); + src.setHost("localhost"); + src.setUser(m_user); + } + + for(QStringList::ConstIterator it = files.begin(); + it != files.end(); ++it) + { + src.setPath(source + *it); + dest.setPath(destination + *it); + +kdDebug() << "Moving " << src << " --> " << dest << endl; + if (!createRemoteDirRecursive(dest.upURL(), false)) + return false; + + if (!NETACCESS::file_move(src, dest, -1, true, false, kapp->mainWidget())) + { + // TODO add error message + retry + return false; + } + } + return true; +} + +// Read information of profile @p profile +void +KioskRun::getProfileInfo(const QString &profile, QString &description, QString &installDir, QString &installUser) +{ + KConfig *config = kapp->config(); + + QString defaultInstallDir = getProfilePrefix(); + if (defaultInstallDir.isEmpty()) + { + defaultInstallDir = "/etc/kde-profile/"; + } + if (!defaultInstallDir.endsWith("/")) + defaultInstallDir.append("/"); + QString tmp = profile; + tmp.replace(" ", "_"); + tmp.replace(":", "_"); + tmp.replace("/", "_"); + defaultInstallDir += tmp+"/"; + + QString group = QString("Directories-%1").arg(profile); + config->setGroup(group); + + installDir = config->readEntry("prefixes", defaultInstallDir); + if (!installDir.endsWith("/")) + installDir.append("/"); + + QString profileInfoFile = installDir + ".kdeprofile"; + if (QFile::exists(profileInfoFile)) + { + KSimpleConfig profileInfo(profileInfoFile, true); + description = profileInfo.readEntry("Description"); + installUser = profileInfo.readEntry("InstallUser", "root"); + return; + } + + QString defaultDescription; + if (profile == "default") + defaultDescription = i18n("Default profile"); + + description = config->readEntry("ProfileDescription", defaultDescription); + installUser = config->readEntry("ProfileInstallUser", "root"); +} + +KSimpleConfig * +KioskRun::openKderc() +{ + if (m_localKdercConfig) + return m_localKdercConfig; + + KURL settingsUrl; + settingsUrl.setPath(m_kderc); + + m_localKderc = ::locateLocal("tmp", "kderc_"+kapp->randomString(5)); + ::unlink(QFile::encodeName(m_localKderc)); + + KURL localCopyUrl; + localCopyUrl.setPath(m_localKderc); + + if (QFile::exists(settingsUrl.path())) + { + + while (!NETACCESS::copy(settingsUrl, localCopyUrl, kapp->mainWidget())) + { + QString error = NETACCESS::lastErrorString(); + QString msg; + if (error.isEmpty()) + msg = i18n("The file %1 could not be accessed because of an unspecified problem.") + .arg(settingsUrl.path()); + else + msg = i18n("The file %1 could not be accessed because of the following problem:" + "

%2

") + .arg(settingsUrl.path(), error); + + msg += i18n("Do you want to retry the operation or abort the saving of changes?"); + + int msgResult = KMessageBox::warningYesNo(kapp->mainWidget(), msg, QString::null, + i18n("&Retry"), i18n("&Abort")); + + if (msgResult == KMessageBox::No) + return 0; + } + } + + m_localKdercConfig = new KSimpleConfig(m_localKderc); + return m_localKdercConfig; +} + +bool +KioskRun::closeKderc() +{ + if (!m_localKdercConfig) + return false; + m_localKdercConfig->sync(); + delete m_localKdercConfig; + m_localKdercConfig = 0; + + QString saveUser = m_user; + m_user = "root"; + bool result = install(m_localKderc, m_kderc); + m_localKderc = QString::null; + m_user = saveUser; + kapp->config()->reparseConfiguration(); + return result; +} + +// Store information for profile @p profile +bool +KioskRun::setProfileInfo(const QString &profile, const QString &description, const QString &_installDir, const QString &installUser, bool deleteProfile, bool deleteFiles) +{ + QString installDir = _installDir; + if (!installDir.endsWith("/")) + installDir.append("/"); + + QString saveProfileInfo = installDir + ".kdeprofile"; + KSimpleConfig profileInfo(saveProfileInfo, true); + QString oldDescription = profileInfo.readEntry("Description"); + QString oldInstallUser = profileInfo.readEntry("InstallUser"); + if (deleteProfile && !installDir.isEmpty()) + { + bool result = true; + KioskSync profileDir(kapp->mainWidget()); + profileDir.addDir(installDir, KURL()); + QStringList allFiles = profileDir.listFiles(); + allFiles.remove(".kdeprofile"); + if (allFiles.isEmpty()) + { + if (QDir(installDir).exists()) + { + m_user = installUser; + remove(installDir); + m_user = QString::null; + } + } + else if (deleteFiles) + { + int msgResult = KMessageBox::warningYesNoCancelList(kapp->mainWidget(), + i18n("The profile directory %1 contains the following files, " + "do you wish to delete these files?").arg(installDir), + allFiles, + i18n("Deleting Profile"), +#if KDE_IS_VERSION(3,2,91) + KStdGuiItem::del(), +#else + i18n("&Delete"), +#endif + i18n("&Keep Files") + ); + switch(msgResult) + { + case KMessageBox::Yes: + // Delete files + m_user = installUser; + result = remove(installDir); + m_user = QString::null; + if (!result) + return false; + break; + + case KMessageBox::No: + // Keep files + break; + + default: + // Cancel + return false; + } + } + + m_user = installUser; + if (QFile::exists(saveProfileInfo)) + result = remove(saveProfileInfo); + m_user = QString::null; + if (!result) + return false; + } + else if ((description != oldDescription) || + (installUser != oldInstallUser)) + { + QString localProfileInfo = ::locateLocal("tmp", "kdeprofile_"+kapp->randomString(5)); + ::unlink(QFile::encodeName(localProfileInfo)); + KSimpleConfig newProfileInfo(localProfileInfo); + newProfileInfo.writeEntry("Description", description); + newProfileInfo.writeEntry("InstallUser", installUser); + newProfileInfo.sync(); + bool result = install(localProfileInfo, saveProfileInfo); + if (!result) + return false; + } + + KUser thisUser; + QString newAdmin = thisUser.loginName()+":"; // This user, all hosts + + KConfig *config = kapp->config(); + + config->setGroup("Directories"); + QString oldAdmin = config->readEntry("kioskAdmin"); + + QString group = QString("Directories-%1").arg(profile); + config->setGroup(group); + + if ((installDir == config->readEntry("prefixes")) && + (newAdmin == oldAdmin) && + !deleteProfile) + return true; // Nothing to do + + KSimpleConfig *cfg = openKderc(); + if (!cfg) + return false; + + cfg->setGroup("Directories"); + cfg->writeEntry("kioskAdmin", newAdmin); + + if (deleteProfile) + { + cfg->deleteGroup(group); + } + else + { + cfg->setGroup(group); + // TODO: update prefixes + cfg->writeEntry("prefixes", installDir); + } + cfg->sync(); + + return closeKderc(); +} + +bool +KioskRun::deleteProfile(const QString &profile, bool deleteFiles) +{ + QString description; + QString installDir; + QString installUser; + getProfileInfo(profile, description, installDir, installUser); + return setProfileInfo(profile, description, installDir, installUser, true, deleteFiles); +} + +// Read profile prefix +QString +KioskRun::getProfilePrefix() +{ + KConfig *config = kapp->config(); + + config->setGroup("Directories"); + + QString prefix = config->readEntry("profileDirsPrefix"); + if (!prefix.isEmpty() && !prefix.endsWith("/")) + prefix.append('/'); + return prefix; +} + +// Store profile prefix +bool +KioskRun::setProfilePrefix(const QString &_prefix) +{ + QString prefix = _prefix; + + if (!prefix.isEmpty() && !prefix.endsWith("/")) + prefix.append('/'); + + if (prefix == getProfilePrefix()) + return true; // Nothing to do + + KSimpleConfig *cfg = openKderc(); + if (!cfg) + return false; + + cfg->setGroup("Directories"); + cfg->writeEntry("profileDirsPrefix", prefix); + + cfg->sync(); + + return closeKderc(); +} + +QString +KioskRun::newProfile() +{ + QString profilePrefix = getProfilePrefix(); + + KConfig *config = kapp->config(); + for(int p = 1; p; p++) + { + QString profile = QString("profile%1").arg(p); + QString group = QString("Directories-%1").arg(profile); + if (!config->hasGroup(group)) + { + if (profilePrefix.isEmpty()) + return profile; + + QString profileDir = profilePrefix + profile; + if (!QDir(profileDir).exists() && !QFile::exists(profileDir)) + return profile; + + // Keep on looking... + } + } + return QString::null; +} + +QStringList +KioskRun::allProfiles() +{ + KConfig *config = kapp->config(); + QStringList groups = config->groupList(); + QStringList profiles; + QStringList directories; + for(QStringList::ConstIterator it = groups.begin(); + it != groups.end(); ++it) + { + if (!(*it).startsWith("Directories-")) + continue; + profiles.append((*it).mid(12)); + config->setGroup(*it); + QString installDir = config->readEntry("prefixes"); + if (!installDir.endsWith("/")) + installDir.append("/"); + directories.append(installDir); + } + + QString profilePrefix = getProfilePrefix(); + if (!profilePrefix.isEmpty()) + { + QDir dir(profilePrefix, QString::null, QDir::Unsorted, QDir::Dirs); + QStringList profileDirs = dir.entryList(); + for(QStringList::ConstIterator it = profileDirs.begin(); + it != profileDirs.end(); ++it) + { + if ((*it).startsWith(".")) + continue; + QString dir = profilePrefix + *it + "/"; + if (directories.contains(dir)) + { + kdDebug() << "Skipping " << dir << ", dir already listed" << endl; + continue; + } + if (profiles.contains(*it)) + { + kdDebug() << "Skipping " << dir << ", profile [" << (*it) << "] already listed" << endl; + continue; + } + + if (!QFile::exists(dir+".kdeprofile")) + { + kdDebug() << "Skipping " << dir << ", no profile info" << endl; + continue; + } + profiles.append(*it); + directories.append(dir); + } + } + + if (!profiles.contains("default")) + profiles.append("default"); + + return profiles; +} + +void +KioskRun::getUserProfileMappings( ProfileMapping &groups, ProfileMapping &users, QStringList &groupOrder) +{ + groups.clear(); + users.clear(); + + KConfig *config = kapp->config(); + config->setGroup("Directories"); + QString mapFile = config->readEntry("userProfileMapFile"); + + if (mapFile.isEmpty() || !QFile::exists(mapFile)) + return; + + KSimpleConfig mapCfg(mapFile, true); + + mapCfg.setGroup("General"); + groupOrder = mapCfg.readListEntry("groups"); + + mapCfg.setGroup("Groups"); + for ( QStringList::ConstIterator it = groupOrder.begin(); + it != groupOrder.end(); ++it ) + { + QString group = *it; + QStringList profiles = mapCfg.readListEntry(group); + if (!profiles.isEmpty()) + groups.insert(group, profiles); + } + + QMap cfg_users = mapCfg.entryMap("Users"); + for ( QMap::Iterator it = cfg_users.begin(); + it != cfg_users.end(); ++it ) + { + QString user = it.key(); + QStringList profiles = QStringList::split(",", it.data()); + if (!profiles.isEmpty()) + users.insert(user, profiles); + } +} + +bool +KioskRun::setUserProfileMappings( const ProfileMapping &groups, const ProfileMapping &users, const QStringList &groupOrder) +{ + KConfig *config = kapp->config(); + config->setGroup("Directories"); + QString mapFile = config->readEntry("userProfileMapFile"); + if (mapFile.isEmpty()) + { + mapFile = "/etc/kde-user-profile"; + + KSimpleConfig *cfg = openKderc(); + if (!cfg) + return false; + + cfg->setGroup("Directories"); + cfg->writeEntry("userProfileMapFile", mapFile); + if (!closeKderc()) + return false; + } + + QString localMapFile = ::locateLocal("tmp", "kde-user-profile_"+kapp->randomString(5)); + ::unlink(QFile::encodeName(localMapFile)); + + KSimpleConfig mapConfig(localMapFile); + + mapConfig.setGroup("General"); + mapConfig.writeEntry("groups", groupOrder); + + KioskRun::ProfileMapping::ConstIterator it; + + mapConfig.setGroup("Groups"); + for ( it = groups.begin(); it != groups.end(); ++it ) + { + QString group = it.key(); + mapConfig.writeEntry(group, it.data()); + } + mapConfig.setGroup("Users"); + for ( it = users.begin(); it != users.end(); ++it ) + { + QString user = it.key(); + mapConfig.writeEntry(user, it.data()); + } + + mapConfig.sync(); + + QString saveUser = m_user; + m_user = "root"; + bool result = install(localMapFile, mapFile); + m_user = saveUser; + return result; +} + +void +KioskRun::forceSycocaUpdate() +{ + // Touch $KDEDIR/share/services/update_ksycoca + KTempFile tempFile; + tempFile.close(); + QString sycocaUpdateFile = locateSave("services", "update_ksycoca"); + remove(sycocaUpdateFile); + install(tempFile.name(), sycocaUpdateFile); +} + +void +KioskRun::scheduleSycocaUpdate() +{ + m_forceSycocaUpdate = true; +} + +void +KioskRun::setCustomRestrictionFileBrowsing(bool restrict) +{ + QString file = "kdeglobals"; + QString group = "KDE URL Restrictions"; + KConfig *cfg = KioskRun::self()->configFile(file); + cfg->setGroup(group); + int count = cfg->readNumEntry("rule_count"); + QStringList urlRestrictions; + for(int i = 0; i < count; i++) + { + QString key = QString("rule_%1").arg(i+1); + if (cfg->hasKey(key)) + urlRestrictions.append(cfg->readEntry(key)); + } + + QStringList newRestrictions; + newRestrictions << "list,,,,file,,,false"; + newRestrictions << "list,,,,file,,$HOME,true"; + + for(QStringList::ConstIterator it = newRestrictions.begin(); + it != newRestrictions.end(); ++it) + { + urlRestrictions.remove(*it); + } + + if (restrict) + { + newRestrictions += urlRestrictions; + urlRestrictions = newRestrictions; + } + + count = urlRestrictions.count(); + cfg->writeEntry("rule_count", count); + + for(int i = 0; i < count; i++) + { + QString key = QString("rule_%1").arg(i+1); + cfg->writeEntry(key, urlRestrictions[i]); + } + KioskRun::self()->setConfigImmutable(file, group, true); +} + +KioskRunProgressDialog::KioskRunProgressDialog(QWidget *parent, const char *name, + const QString &caption, const QString &text) + : KProgressDialog(parent, name, caption, text, true) +{ + connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotProgress())); + progressBar()->setTotalSteps(20); + m_timeStep = 700; + m_timer.start(m_timeStep); + setAutoClose(false); +} + +void +KioskRunProgressDialog::slotProgress() +{ + int p = progressBar()->progress(); + if (p == 18) + { + progressBar()->reset(); + progressBar()->setProgress(1); + m_timeStep = m_timeStep * 2; + m_timer.start(m_timeStep); + } + else + { + progressBar()->setProgress(p+1); + } +} + +void +KioskRunProgressDialog::slotFinished() +{ + progressBar()->setProgress(20); + m_timer.stop(); + QTimer::singleShot(1000, this, SLOT(close())); +} + + +#include "kioskrun.moc" + -- cgit v1.2.1