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.
447 lines
15 KiB
447 lines
15 KiB
15 years ago
|
/***************************************************************************
|
||
|
copyright : (C) 2006 by Robby Stephenson
|
||
|
email : robby@periapsis.org
|
||
|
***************************************************************************/
|
||
|
|
||
|
/***************************************************************************
|
||
|
* *
|
||
|
* This program is free software; you can redistribute it and/or modify *
|
||
|
* it under the terms of version 2 of the GNU General Public License as *
|
||
|
* published by the Free Software Foundation; *
|
||
|
* *
|
||
|
***************************************************************************/
|
||
|
|
||
|
#include "manager.h"
|
||
|
#include "newscript.h"
|
||
|
#include "../filehandler.h"
|
||
|
#include "../tellico_debug.h"
|
||
|
#include "../tellico_utils.h"
|
||
|
#include "../tellico_kernel.h"
|
||
|
#include "../fetch/fetch.h"
|
||
|
|
||
|
#include <kurl.h>
|
||
|
#include <ktar.h>
|
||
|
#include <kglobal.h>
|
||
|
#include <kio/netaccess.h>
|
||
|
#include <kconfig.h>
|
||
|
#include <ktempfile.h>
|
||
|
#include <kio/job.h>
|
||
|
#include <kfileitem.h>
|
||
|
#include <kdeversion.h>
|
||
|
#include <knewstuff/entry.h>
|
||
|
#include <kstandarddirs.h>
|
||
|
|
||
|
#include <qfileinfo.h>
|
||
|
#include <qdir.h>
|
||
|
#include <qptrstack.h>
|
||
|
#include <qvaluestack.h>
|
||
|
#include <qwidget.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
using Tellico::NewStuff::Manager;
|
||
|
|
||
|
Manager::Manager(QObject* parent_) : QObject(parent_), m_tempFile(0) {
|
||
|
m_infoList.setAutoDelete(true);
|
||
|
}
|
||
|
|
||
|
Manager::~Manager() {
|
||
|
delete m_tempFile;
|
||
|
m_tempFile = 0;
|
||
|
}
|
||
|
|
||
|
bool Manager::installTemplate(const KURL& url_, const QString& entryName_) {
|
||
|
FileHandler::FileRef* ref = FileHandler::fileRef(url_);
|
||
|
|
||
|
QString xslFile;
|
||
|
QStringList allFiles;
|
||
|
|
||
|
bool success = true;
|
||
|
|
||
|
// is there a better way to figure out if the url points to a XSL file or a tar archive
|
||
|
// than just trying to open it?
|
||
|
KTar archive(ref->fileName());
|
||
|
if(archive.open(IO_ReadOnly)) {
|
||
|
const KArchiveDirectory* archiveDir = archive.directory();
|
||
|
archiveDir->copyTo(Tellico::saveLocation(QString::fromLatin1("entry-templates/")));
|
||
|
|
||
|
allFiles = archiveFiles(archiveDir);
|
||
|
// remember files installed for template
|
||
|
xslFile = findXSL(archiveDir);
|
||
|
} else { // assume it's an xsl file
|
||
|
QString name = entryName_;
|
||
|
if(name.isEmpty()) {
|
||
|
name = url_.fileName();
|
||
|
}
|
||
|
if(!name.endsWith(QString::fromLatin1(".xsl"))) {
|
||
|
name += QString::fromLatin1(".xsl");
|
||
|
}
|
||
|
KURL dest;
|
||
|
dest.setPath(Tellico::saveLocation(QString::fromLatin1("entry-templates/")) + name);
|
||
|
success = true;
|
||
|
if(QFile::exists(dest.path())) {
|
||
|
myDebug() << "Manager::installTemplate() - " << dest.path() << " exists!" << endl;
|
||
|
success = false;
|
||
|
} else if(KIO::NetAccess::file_copy(url_, dest)) {
|
||
|
xslFile = dest.fileName();
|
||
|
allFiles += xslFile;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(xslFile.isEmpty()) {
|
||
|
success = false;
|
||
|
} else {
|
||
|
// remove ".xsl"
|
||
|
xslFile.truncate(xslFile.length()-4);
|
||
|
KConfigGroup config(KGlobal::config(), "KNewStuffFiles");
|
||
|
config.writeEntry(xslFile, allFiles);
|
||
|
m_urlNameMap.insert(url_, xslFile);
|
||
|
}
|
||
|
|
||
|
checkCommonFile();
|
||
|
|
||
|
delete ref;
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
bool Manager::removeTemplate(const QString& name_) {
|
||
|
KConfigGroup fileGroup(KGlobal::config(), "KNewStuffFiles");
|
||
|
QStringList files = fileGroup.readListEntry(name_);
|
||
|
// at least, delete xsl file
|
||
|
if(files.isEmpty()) {
|
||
|
kdWarning() << "Manager::deleteTemplate() no file list found for " << name_ << endl;
|
||
|
files += name_;
|
||
|
}
|
||
|
|
||
|
bool success = true;
|
||
|
QString path = Tellico::saveLocation(QString::fromLatin1("entry-templates/"));
|
||
|
for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
|
||
|
if((*it).endsWith(QChar('/'))) {
|
||
|
// ok to not delete all directories
|
||
|
QDir().rmdir(path + *it);
|
||
|
} else {
|
||
|
success = success && QFile(path + *it).remove();
|
||
|
if(!success) {
|
||
|
myDebug() << "Manager::removeTemplate() - failed to remove " << (path+*it) << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// remove config entries even if unsuccessful
|
||
|
fileGroup.deleteEntry(name_);
|
||
|
KConfigGroup statusGroup(KGlobal::config(), "KNewStuffStatus");
|
||
|
QString entryName = statusGroup.readEntry(name_);
|
||
|
statusGroup.deleteEntry(name_);
|
||
|
statusGroup.deleteEntry(entryName);
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
bool Manager::installScript(const KURL& url_) {
|
||
|
FileHandler::FileRef* ref = FileHandler::fileRef(url_);
|
||
|
|
||
|
KTar archive(ref->fileName());
|
||
|
if(!archive.open(IO_ReadOnly)) {
|
||
|
myDebug() << "Manager::installScript() - can't open tar file" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const KArchiveDirectory* archiveDir = archive.directory();
|
||
|
|
||
|
QString exeFile = findEXE(archiveDir);
|
||
|
if(exeFile.isEmpty()) {
|
||
|
myDebug() << "Manager::installScript() - no exe file found" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
QFileInfo exeInfo(exeFile);
|
||
|
DataSourceInfo* info = new DataSourceInfo();
|
||
|
|
||
|
QString copyTarget = Tellico::saveLocation(QString::fromLatin1("data-sources/"));
|
||
|
QString scriptFolder;
|
||
|
|
||
|
// package could have a top-level directory or not
|
||
|
// it should have a directory...
|
||
|
const KArchiveEntry* firstEntry = archiveDir->entry(archiveDir->entries().first());
|
||
|
if(firstEntry->isFile()) {
|
||
|
copyTarget += exeInfo.baseName(true) + '/';
|
||
|
if(QFile::exists(copyTarget)) {
|
||
|
KURL u;
|
||
|
u.setPath(scriptFolder);
|
||
|
myLog() << "Manager::installScript() - deleting " << scriptFolder << endl;
|
||
|
KIO::NetAccess::del(u, Kernel::self()->widget());
|
||
|
info->isUpdate = true;
|
||
|
}
|
||
|
scriptFolder = copyTarget;
|
||
|
} else {
|
||
|
scriptFolder = copyTarget + firstEntry->name() + '/';
|
||
|
if(QFile::exists(copyTarget + exeFile)) {
|
||
|
info->isUpdate = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// overwrites stuff there
|
||
|
archiveDir->copyTo(copyTarget);
|
||
|
|
||
|
info->specFile = scriptFolder + exeInfo.baseName(true) + ".spec";
|
||
|
if(!QFile::exists(info->specFile)) {
|
||
|
myDebug() << "Manager::installScript() - no spec file for script! " << info->specFile << endl;
|
||
|
delete info;
|
||
|
return false;
|
||
|
}
|
||
|
info->sourceName = exeFile;
|
||
|
info->sourceExec = copyTarget + exeFile;
|
||
|
m_infoList.append(info);
|
||
|
|
||
|
KURL dest;
|
||
|
dest.setPath(info->sourceExec);
|
||
|
KFileItem item(KFileItem::Unknown, KFileItem::Unknown, dest, true);
|
||
|
::chmod(QFile::encodeName(dest.path()), item.permissions() | S_IXUSR);
|
||
|
|
||
|
{
|
||
|
KConfig spec(info->specFile, false, false);
|
||
|
// update name
|
||
|
info->sourceName = spec.readEntry("Name", info->sourceName);
|
||
|
spec.writePathEntry("ExecPath", info->sourceExec);
|
||
|
spec.writeEntry("NewStuffName", info->sourceName);
|
||
|
spec.writeEntry("DeleteOnRemove", true);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
KConfigGroup config(KGlobal::config(), "KNewStuffFiles");
|
||
|
config.writeEntry(info->sourceName, archiveFiles(archiveDir));
|
||
|
m_urlNameMap.insert(url_, info->sourceName);
|
||
|
}
|
||
|
|
||
|
|
||
|
// myDebug() << "Manager::installScript() - exeFile = " << exeFile << endl;
|
||
|
// myDebug() << "Manager::installScript() - sourceExec = " << info->sourceExec << endl;
|
||
|
// myDebug() << "Manager::installScript() - sourceName = " << info->sourceName << endl;
|
||
|
// myDebug() << "Manager::installScript() - specFile = " << info->specFile << endl;
|
||
|
|
||
|
delete ref;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool Manager::removeScript(const QString& name_) {
|
||
|
KConfigGroup fileGroup(KGlobal::config(), "KNewStuffFiles");
|
||
|
QStringList files = fileGroup.readListEntry(name_);
|
||
|
|
||
|
bool success = true;
|
||
|
QString path = Tellico::saveLocation(QString::fromLatin1("data-sources/"));
|
||
|
for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
|
||
|
if((*it).endsWith(QChar('/'))) {
|
||
|
// ok to not delete all directories
|
||
|
QDir().rmdir(path + *it);
|
||
|
} else {
|
||
|
success = success && QFile(path + *it).remove();
|
||
|
if(!success) {
|
||
|
myDebug() << "Manager::removeScript() - failed to remove " << (path+*it) << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// remove config entries even if unsuccessful
|
||
|
fileGroup.deleteEntry(name_);
|
||
|
KConfigGroup statusGroup(KGlobal::config(), "KNewStuffStatus");
|
||
|
QString entryName = statusGroup.readEntry(name_);
|
||
|
statusGroup.deleteEntry(name_);
|
||
|
statusGroup.deleteEntry(entryName);
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
Tellico::NewStuff::InstallStatus Manager::installStatus(KNS::Entry* entry_) {
|
||
|
KConfigGroup config(KGlobal::config(), "KNewStuffStatus");
|
||
|
QString datestring = config.readEntry(entry_->name());
|
||
|
if(datestring.isEmpty()) {
|
||
|
return NotInstalled;
|
||
|
}
|
||
|
|
||
|
QDate date = QDate::fromString(datestring, Qt::ISODate);
|
||
|
if(!date.isValid()) {
|
||
|
return NotInstalled;
|
||
|
}
|
||
|
if(date < entry_->releaseDate()) {
|
||
|
return OldVersion;
|
||
|
}
|
||
|
|
||
|
// also check that executable files exists
|
||
|
KConfigGroup fileGroup(KGlobal::config(), "KNewStuffFiles");
|
||
|
QStringList files = fileGroup.readListEntry(entry_->name());
|
||
|
QString path = Tellico::saveLocation(QString::fromLatin1("data-sources/"));
|
||
|
for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
|
||
|
if(!QFile::exists(path + *it)) {
|
||
|
return NotInstalled;
|
||
|
}
|
||
|
}
|
||
|
return Current;
|
||
|
}
|
||
|
|
||
|
QStringList Manager::archiveFiles(const KArchiveDirectory* dir_, const QString& path_) {
|
||
|
QStringList list;
|
||
|
|
||
|
const QStringList dirEntries = dir_->entries();
|
||
|
for(QStringList::ConstIterator it = dirEntries.begin(); it != dirEntries.end(); ++it) {
|
||
|
const QString& entry = *it;
|
||
|
const KArchiveEntry* curEntry = dir_->entry(entry);
|
||
|
if(curEntry->isFile()) {
|
||
|
list += path_ + entry;
|
||
|
} else if(curEntry->isDirectory()) {
|
||
|
list += archiveFiles(static_cast<const KArchiveDirectory*>(curEntry), path_ + entry + '/');
|
||
|
// add directory AFTER contents, since we delete from the top down later
|
||
|
list += path_ + entry + '/';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
// don't recurse, the .xsl must be in top directory
|
||
|
QString Manager::findXSL(const KArchiveDirectory* dir_) {
|
||
|
const QStringList entries = dir_->entries();
|
||
|
for(QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
|
||
|
const QString& entry = *it;
|
||
|
if(entry.endsWith(QString::fromLatin1(".xsl"))) {
|
||
|
return entry;
|
||
|
}
|
||
|
}
|
||
|
return QString();
|
||
|
}
|
||
|
|
||
|
QString Manager::findEXE(const KArchiveDirectory* dir_) {
|
||
|
QPtrStack<KArchiveDirectory> dirStack;
|
||
|
QValueStack<QString> dirNameStack;
|
||
|
|
||
|
dirStack.push(dir_);
|
||
|
dirNameStack.push(QString());
|
||
|
|
||
|
do {
|
||
|
const QString dirName = dirNameStack.pop();
|
||
|
const KArchiveDirectory* curDir = dirStack.pop();
|
||
|
const QStringList entries = curDir->entries();
|
||
|
for(QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
|
||
|
const QString& entry = *it;
|
||
|
const KArchiveEntry* archEntry = curDir->entry(entry);
|
||
|
|
||
|
if(archEntry->isFile() && (archEntry->permissions() & S_IEXEC)) {
|
||
|
return dirName + entry;
|
||
|
} else if(archEntry->isDirectory()) {
|
||
|
dirStack.push(static_cast<const KArchiveDirectory*>(archEntry));
|
||
|
dirNameStack.push(dirName + entry + '/');
|
||
|
}
|
||
|
}
|
||
|
} while(!dirStack.isEmpty());
|
||
|
|
||
|
return QString();
|
||
|
}
|
||
|
|
||
|
void Manager::install(DataType type_, KNS::Entry* entry_) {
|
||
|
if(m_tempFile) {
|
||
|
delete m_tempFile;
|
||
|
}
|
||
|
m_tempFile = new KTempFile();
|
||
|
m_tempFile->setAutoDelete(true);
|
||
|
|
||
|
KURL destination;
|
||
|
destination.setPath(m_tempFile->name());
|
||
|
KIO::FileCopyJob* job = KIO::file_copy(entry_->payload(), destination, -1, true);
|
||
|
connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotDownloadJobResult(KIO::Job*)));
|
||
|
m_jobMap.insert(job, EntryPair(entry_, type_));
|
||
|
}
|
||
|
|
||
|
void Manager::slotDownloadJobResult(KIO::Job* job_) {
|
||
|
KIO::FileCopyJob* job = static_cast<KIO::FileCopyJob*>(job_);
|
||
|
if(job->error()) {
|
||
|
GUI::CursorSaver cs(Qt::arrowCursor);
|
||
|
delete m_tempFile;
|
||
|
m_tempFile = 0;
|
||
|
job->showErrorDialog(Kernel::self()->widget());
|
||
|
emit signalInstalled(0); // still need to notify dialog that install failed
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
KNS::Entry* entry = m_jobMap[job_].first;
|
||
|
DataType type = m_jobMap[job_].second;
|
||
|
|
||
|
bool deleteTempFile = true;
|
||
|
if(type == EntryTemplate) {
|
||
|
installTemplate(job->destURL(), entry->name());
|
||
|
} else {
|
||
|
#if KDE_IS_VERSION(3,3,90)
|
||
|
// needed so the GPG signature can be checked
|
||
|
NewScript* newScript = new NewScript(this, Kernel::self()->widget());
|
||
|
connect(newScript, SIGNAL(installFinished()), this, SLOT(slotInstallFinished()));
|
||
|
// need to delete temp file if install was not a success
|
||
|
// if it was a success, it gets deleted later
|
||
|
deleteTempFile = !newScript->install(job->destURL().path());
|
||
|
m_scriptEntryMap.insert(newScript, entry);
|
||
|
#endif
|
||
|
// if failed, emit empty signal now
|
||
|
if(deleteTempFile) {
|
||
|
emit signalInstalled(0);
|
||
|
}
|
||
|
}
|
||
|
if(deleteTempFile) {
|
||
|
delete m_tempFile;
|
||
|
m_tempFile = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Manager::slotInstallFinished() {
|
||
|
const NewScript* newScript = ::qt_cast<const NewScript*>(sender());
|
||
|
if(newScript && newScript->successfulInstall()) {
|
||
|
const QString name = m_urlNameMap[newScript->url()];
|
||
|
KNS::Entry* entry = m_scriptEntryMap[newScript];
|
||
|
KConfigGroup config(KGlobal::config(), "KNewStuffStatus");
|
||
|
// have to keep a config entry that maps the name of the file to the name
|
||
|
// of the newstuff entry, so we can track which entries are installed
|
||
|
// name and entry-name() are probably the same for scripts, but not a problem
|
||
|
config.writeEntry(name, entry->name());
|
||
|
config.writeEntry(entry->name(), entry->releaseDate().toString(Qt::ISODate));
|
||
|
config.sync();
|
||
|
emit signalInstalled(entry);
|
||
|
} else {
|
||
|
emit signalInstalled(0);
|
||
|
kdWarning() << "Manager::slotInstallFinished() - Failed to install" << endl;
|
||
|
}
|
||
|
delete m_tempFile;
|
||
|
m_tempFile = 0;
|
||
|
}
|
||
|
|
||
|
bool Manager::checkCommonFile() {
|
||
|
// look for a file that gets installed to know the installation directory
|
||
|
// need to check timestamps
|
||
|
QString userDataDir = Tellico::saveLocation(QString::null);
|
||
|
QString userCommonFile = userDataDir + '/' + QString::fromLatin1("tellico-common.xsl");
|
||
|
if(QFile::exists(userCommonFile)) {
|
||
|
// check timestamps
|
||
|
// pics/tellico.png is not likely to be in a user directory
|
||
|
QString installDir = KGlobal::dirs()->findResourceDir("appdata", QString::fromLatin1("pics/tellico.png"));
|
||
|
QString installCommonFile = installDir + '/' + QString::fromLatin1("tellico-common.xsl");
|
||
|
#ifndef NDEBUG
|
||
|
if(userCommonFile == installCommonFile) {
|
||
|
kdWarning() << "Manager::checkCommonFile() - install location is same as user location" << endl;
|
||
|
}
|
||
|
#endif
|
||
|
QFileInfo installInfo(installCommonFile);
|
||
|
QFileInfo userInfo(userCommonFile);
|
||
|
if(installInfo.lastModified() > userInfo.lastModified()) {
|
||
|
// the installed file has been modified more recently than the user's
|
||
|
// remove user's tellico-common.xsl file so it gets copied again
|
||
|
myLog() << "Manager::checkCommonFile() - removing " << userCommonFile << endl;
|
||
|
myLog() << "Manager::checkCommonFile() - copying " << installCommonFile << endl;
|
||
|
QFile::remove(userCommonFile);
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
KURL src, dest;
|
||
|
src.setPath(KGlobal::dirs()->findResource("appdata", QString::fromLatin1("tellico-common.xsl")));
|
||
|
dest.setPath(userCommonFile);
|
||
|
return KIO::NetAccess::file_copy(src, dest);
|
||
|
}
|
||
|
|
||
|
#include "manager.moc"
|