/*************************************************************************** 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, Qt::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, TQT_SIGNAL(result(TDEIO::Job*)), TQT_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, TQT_SIGNAL(installFinished()), this, TQT_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 = ::tqqt_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(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 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"