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.
tellico/src/newstuff/manager.cpp

452 lines
15 KiB

/***************************************************************************
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 <tdeglobal.h>
#include <tdeio/netaccess.h>
#include <tdeconfig.h>
#include <tdetempfile.h>
#include <tdeio/job.h>
#include <tdefileitem.h>
#include <tdeversion.h>
#include <tdenewstuff/entry.h>
#include <kstandarddirs.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqptrstack.h>
#include <tqvaluestack.h>
#include <tqwidget.h>
#include <sys/types.h>
#include <sys/stat.h>
using Tellico::NewStuff::Manager;
Manager::Manager(TQObject* parent_) : TQObject(parent_), m_tempFile(0) {
m_infoList.setAutoDelete(true);
}
Manager::~Manager() {
delete m_tempFile;
m_tempFile = 0;
}
bool Manager::installTemplate(const KURL& url_, const TQString& entryName_) {
FileHandler::FileRef* ref = FileHandler::fileRef(url_);
TQString xslFile;
TQStringList 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(TQString::fromLatin1("entry-templates/")));
allFiles = archiveFiles(archiveDir);
// remember files installed for template
xslFile = findXSL(archiveDir);
} else { // assume it's an xsl file
TQString name = entryName_;
if(name.isEmpty()) {
name = url_.fileName();
}
if(!name.endsWith(TQString::fromLatin1(".xsl"))) {
name += TQString::fromLatin1(".xsl");
}
KURL dest;
dest.setPath(Tellico::saveLocation(TQString::fromLatin1("entry-templates/")) + name);
success = true;
if(TQFile::exists(dest.path())) {
myDebug() << "Manager::installTemplate() - " << dest.path() << " exists!" << endl;
success = false;
} else if(TDEIO::NetAccess::file_copy(url_, dest)) {
xslFile = dest.fileName();
allFiles += xslFile;
}
}
if(xslFile.isEmpty()) {
success = false;
} else {
// remove ".xsl"
xslFile.truncate(xslFile.length()-4);
TDEConfigGroup config(TDEGlobal::config(), "TDENewStuffFiles");
config.writeEntry(xslFile, allFiles);
m_urlNameMap.insert(url_, xslFile);
}
checkCommonFile();
delete ref;
return success;
}
bool Manager::removeTemplate(const TQString& name_) {
TDEConfigGroup fileGroup(TDEGlobal::config(), "TDENewStuffFiles");
TQStringList 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;
TQString path = Tellico::saveLocation(TQString::fromLatin1("entry-templates/"));
for(TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
if((*it).endsWith(TQChar('/'))) {
// ok to not delete all directories
TQDir().rmdir(path + *it);
} else {
success = success && TQFile(path + *it).remove();
if(!success) {
myDebug() << "Manager::removeTemplate() - failed to remove " << (path+*it) << endl;
}
}
}
// remove config entries even if unsuccessful
fileGroup.deleteEntry(name_);
TDEConfigGroup statusGroup(TDEGlobal::config(), "TDENewStuffStatus");
TQString 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();
TQString exeFile = findEXE(archiveDir);
if(exeFile.isEmpty()) {
myDebug() << "Manager::installScript() - no exe file found" << endl;
return false;
}
TQFileInfo exeInfo(exeFile);
DataSourceInfo* info = new DataSourceInfo();
TQString copyTarget = Tellico::saveLocation(TQString::fromLatin1("data-sources/"));
TQString 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(TQFile::exists(copyTarget)) {
KURL u;
u.setPath(scriptFolder);
myLog() << "Manager::installScript() - deleting " << scriptFolder << endl;
TDEIO::NetAccess::del(u, Kernel::self()->widget());
info->isUpdate = true;
}
scriptFolder = copyTarget;
} else {
scriptFolder = copyTarget + firstEntry->name() + '/';
if(TQFile::exists(copyTarget + exeFile)) {
info->isUpdate = true;
}
}
// overwrites stuff there
archiveDir->copyTo(copyTarget);
info->specFile = scriptFolder + exeInfo.baseName(true) + ".spec";
if(!TQFile::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(TQFile::encodeName(dest.path()), item.permissions() | S_IXUSR);
{
TDEConfig 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);
}
{
TDEConfigGroup config(TDEGlobal::config(), "TDENewStuffFiles");
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 TQString& name_) {
TDEConfigGroup fileGroup(TDEGlobal::config(), "TDENewStuffFiles");
TQStringList files = fileGroup.readListEntry(name_);
bool success = true;
TQString path = Tellico::saveLocation(TQString::fromLatin1("data-sources/"));
for(TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
if((*it).endsWith(TQChar('/'))) {
// ok to not delete all directories
TQDir().rmdir(path + *it);
} else {
success = success && TQFile(path + *it).remove();
if(!success) {
myDebug() << "Manager::removeScript() - failed to remove " << (path+*it) << endl;
}
}
}
// remove config entries even if unsuccessful
fileGroup.deleteEntry(name_);
TDEConfigGroup statusGroup(TDEGlobal::config(), "TDENewStuffStatus");
TQString entryName = statusGroup.readEntry(name_);
statusGroup.deleteEntry(name_);
statusGroup.deleteEntry(entryName);
return success;
}
Tellico::NewStuff::InstallStatus Manager::installStatus(KNS::Entry* entry_) {
TDEConfigGroup config(TDEGlobal::config(), "TDENewStuffStatus");
TQString datestring = config.readEntry(entry_->name());
if(datestring.isEmpty()) {
return NotInstalled;
}
TQDate date = TQDate::fromString(datestring, TQt::ISODate);
if(!date.isValid()) {
return NotInstalled;
}
if(date < entry_->releaseDate()) {
return OldVersion;
}
// also check that executable files exists
TDEConfigGroup fileGroup(TDEGlobal::config(), "TDENewStuffFiles");
TQStringList files = fileGroup.readListEntry(entry_->name());
TQString path = Tellico::saveLocation(TQString::fromLatin1("data-sources/"));
for(TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
if(!TQFile::exists(path + *it)) {
return NotInstalled;
}
}
return Current;
}
TQStringList Manager::archiveFiles(const KArchiveDirectory* dir_, const TQString& path_) {
TQStringList list;
const TQStringList dirEntries = dir_->entries();
for(TQStringList::ConstIterator it = dirEntries.begin(); it != dirEntries.end(); ++it) {
const TQString& 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
TQString Manager::findXSL(const KArchiveDirectory* dir_) {
const TQStringList entries = dir_->entries();
for(TQStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
const TQString& entry = *it;
if(entry.endsWith(TQString::fromLatin1(".xsl"))) {
return entry;
}
}
return TQString();
}
TQString Manager::findEXE(const KArchiveDirectory* dir_) {
TQPtrStack<KArchiveDirectory> dirStack;
TQValueStack<TQString> dirNameStack;
dirStack.push(dir_);
dirNameStack.push(TQString());
do {
const TQString dirName = dirNameStack.pop();
const KArchiveDirectory* curDir = dirStack.pop();
const TQStringList entries = curDir->entries();
for(TQStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
const TQString& 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 TQString();
}
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());
TDEIO::FileCopyJob* job = TDEIO::file_copy(entry_->payload(), destination, -1, true);
connect(job, TQ_SIGNAL(result(TDEIO::Job*)), TQ_SLOT(slotDownloadJobResult(TDEIO::Job*)));
m_jobMap.insert(job, EntryPair(entry_, type_));
}
void Manager::slotDownloadJobResult(TDEIO::Job* job_) {
TDEIO::FileCopyJob* job = static_cast<TDEIO::FileCopyJob*>(job_);
if(job->error()) {
GUI::CursorSaver cs(TQt::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) {
if (installTemplate(job->destURL(), entry->name())) {
emit signalInstalled(entry);
}
else {
emit signalInstalled(0);
}
} 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, TQ_SIGNAL(installFinished()), this, TQ_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 = ::tqt_cast<const NewScript*>(sender());
if(newScript && newScript->successfulInstall()) {
const TQString name = m_urlNameMap[newScript->url()];
KNS::Entry* entry = m_scriptEntryMap[newScript];
TDEConfigGroup config(TDEGlobal::config(), "TDENewStuffStatus");
// 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(), TQString(entry->releaseDate().toString(TQt::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
TQString userDataDir = Tellico::saveLocation(TQString());
TQString userCommonFile = userDataDir + '/' + TQString::fromLatin1("tellico-common.xsl");
if(TQFile::exists(userCommonFile)) {
// check timestamps
// pics/tellico.png is not likely to be in a user directory
TQString installDir = TDEGlobal::dirs()->findResourceDir("appdata", TQString::fromLatin1("pics/tellico.png"));
TQString installCommonFile = installDir + '/' + TQString::fromLatin1("tellico-common.xsl");
#ifndef NDEBUG
if(userCommonFile == installCommonFile) {
kdWarning() << "Manager::checkCommonFile() - install location is same as user location" << endl;
}
#endif
TQFileInfo installInfo(installCommonFile);
TQFileInfo 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;
TQFile::remove(userCommonFile);
} else {
return true;
}
}
KURL src, dest;
src.setPath(TDEGlobal::dirs()->findResource("appdata", TQString::fromLatin1("tellico-common.xsl")));
dest.setPath(userCommonFile);
return TDEIO::NetAccess::file_copy(src, dest);
}
#include "manager.moc"