You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kiosktool/kiosktool/kioskrun.cpp

1688 lines
44 KiB

/*
* kioskrun.cpp
*
* Copyright (C) 2004 Waldo Bastian <bastian@kde.org>
*
* 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 <assert.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <qdir.h>
#include <qfile.h>
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kconfig.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kprocess.h>
#include <ksavefile.h>
#include <ksimpleconfig.h>
#include <kstandarddirs.h>
#include <kurl.h>
#include <kuser.h>
#include "kiosksync.h"
#include <kio/netaccess.h>
#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<int> 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<int> 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<KConfig> 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<QString, QString> map = newCfg.entryMap(*it);
for(QMap<QString, QString>::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("<qt>The directory <b>%1</b> could not be created because of an unspecified problem.<p>")
.arg(dir);
else
msg = i18n("<qt>The directory <b>%1</b> could not be created because of the following problem:"
"<p>%2<p>")
.arg(dir, NETACCESS::lastErrorString());
msg += i18n("Without this directory your changes can not be saved.<p>"
"Do you want to retry creating the directory or abort the saving of changes?</qt>");
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("<qt>The directory <b>%1</b> 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("<qt>The directory <b>%1</b> could not be created because of an unspecified problem.<p>")
.arg(dest.prettyURL());
else
msg = i18n("<qt>The directory <b>%1</b> could not be created because of the following problem:"
"<p>%2<p>")
.arg(dest.prettyURL(), NETACCESS::lastErrorString());
msg += i18n("Without this directory your files can not be uploaded.<p>"
"Do you want to retry creating the directory or abort uploading?</qt>");
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("<qt>The file <b>%1</b> could not be installed because of an unspecified problem.")
.arg(destination);
else
msg = i18n("<qt>The file <b>%1</b> could not be installed because of the following problem:"
"<p>%2<p>")
.arg(destination, NETACCESS::lastErrorString());
msg += i18n("Do you want to retry the installation or abort the saving of changes?</qt>");
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("<qt>The file <b>%1</b> could not be uploaded to <b>%2</b> because of an unspecified problem.")
.arg(file, dest.prettyURL());
else
msg = i18n("<qt>The file <b>%1</b> could not be uploaded to <b>%2</b> because of the following problem:"
"<p>%3<p>")
.arg(file, dest.prettyURL(),NETACCESS::lastErrorString());
msg += i18n("Do you want to retry or abort the uploading?</qt>");
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("<qt>The file <b>%1</b> could not be accessed because of an unspecified problem.")
.arg(settingsUrl.path());
else
msg = i18n("<qt>The file <b>%1</b> could not be accessed because of the following problem:"
"<p>%2<p>")
.arg(settingsUrl.path(), error);
msg += i18n("Do you want to retry the operation or abort the saving of changes?</qt>");
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("<qt>The profile directory <b>%1</b> 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<QString, QString> cfg_users = mapCfg.entryMap("Users");
for ( QMap<QString, QString>::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"