/************************************************************************** ** ** This file is part of Qt SDK** ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* ** ** Contact: Nokia Corporation qt-info@nokia.com** ** ** No Commercial Usage ** ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception version ** 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you are unsure which license is appropriate for your use, please contact ** (qt-info@nokia.com). ** **************************************************************************/ #include "repositorygen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib7z_facade.h" #include using namespace QInstaller; QT_BEGIN_NAMESPACE static bool operator==(const PackageInfo& lhs, const PackageInfo& rhs) { return lhs.name == rhs.name && lhs.version == rhs.version; } QT_END_NAMESPACE static QVector collectAvailablePackages(const QString& packagesDirectory) { verbose() << "Collecting information about available packages..." << std::endl; QVector< PackageInfo > dict; const QFileInfoList entries = QDir(packagesDirectory) .entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (QFileInfoList::const_iterator it = entries.begin(); it != entries.end(); ++it) { verbose() << " found subdirectory \"" << it->fileName() << "\""; QFile file(QString::fromLatin1("%1/meta/package.xml").arg(it->filePath())); if (!file.exists()) { verbose() << ", but it contains no package information (meta/package.xml missing)" << std::endl; throw QInstaller::Error(QObject::tr("Component %1 does not contain a package " "description.").arg(it->fileName())); } file.open(QIODevice::ReadOnly); QDomDocument doc; QString errorMessage; int errorLine = 0; int errorColumn = 0; if (!doc.setContent(&file, &errorMessage, &errorLine, &errorColumn)) { verbose() << ", but it's package description is invalid. Error at " << errorLine << ", " << errorColumn << ": " << errorMessage << std::endl; throw QInstaller::Error(QObject::tr("Component package description for %1 is invalid. " "Error at %2, %3 : %4").arg(it->fileName(), QString::number(errorLine), QString::number(errorColumn), errorMessage)); } const QString name = doc.firstChildElement(QString::fromLatin1("Package")) .firstChildElement(QLatin1String("Name")).text(); if (name != it->fileName()) { throw QInstaller::Error(QObject::tr("Component folder name must match component name: " "\"%1\" in %2/").arg(name, it->fileName())); } PackageInfo info; info.name = name; info.version = doc.firstChildElement(QString::fromLatin1("Package")). firstChildElement(QString::fromLatin1("Version")).text(); info.dependencies = doc.firstChildElement(QString::fromLatin1("Package")). firstChildElement(QString::fromLatin1("Dependencies")).text().split(QLatin1String(","), QString::SkipEmptyParts); info.directory = it->filePath(); dict.push_back(info); verbose() << ", it provides the package " <& available) { const QString id = name.contains(QChar::fromLatin1('-')) ? name.section(QChar::fromLatin1('-'), 0, 0) : name; QString version = name.contains(QChar::fromLatin1('-')) ? name.section(QChar::fromLatin1('-'), 1, -1) : QString(); QRegExp compEx(QLatin1String("([<=>]+)(.*)")); const QString comparator = compEx.exactMatch(version) ? compEx.cap(1) : QString::fromLatin1("="); version = compEx.exactMatch(version) ? compEx.cap(2) : version; const bool allowEqual = comparator.contains(QLatin1Char('=')); const bool allowLess = comparator.contains(QLatin1Char('<')); const bool allowMore = comparator.contains(QLatin1Char('>')); for (QVector< PackageInfo >::const_iterator it = available.begin(); it != available.end(); ++it) { if (it->name != id) continue; if (allowEqual && (version.isEmpty() || it->version == version)) return *it; else if (allowLess && KDUpdater::compareVersion(version, it->version) > 0) return *it; else if (allowMore && KDUpdater::compareVersion(version, it->version) < 0) return *it; } return PackageInfo(); } /** * Returns true, when the \a package's identifier starts with, but not equals \a prefix. */ static bool packageHasPrefix(const PackageInfo& package, const QString& prefix) { return package.name.startsWith(prefix) && package.name.mid(prefix.length(), 1) == QString::fromLatin1("."); } /** * Returns true, whel all \a packages start with \a prefix */ static bool allPackagesHavePrefix(const QVector< PackageInfo >& packages, const QString& prefix) { for (QVector< PackageInfo >::const_iterator it = packages.begin(); it != packages.end(); ++it) { if (!packageHasPrefix(*it, prefix)) return false; } return true; } /** * Returns all packages out of \a all starting with \a prefix. */ static QVector< PackageInfo > packagesWithPrefix(const QVector< PackageInfo >& all, const QString& prefix) { QVector< PackageInfo > result; for (QVector< PackageInfo >::const_iterator it = all.begin(); it != all.end(); ++it) { if (packageHasPrefix(*it, prefix)) result.push_back(*it); } return result; } static QVector< PackageInfo > calculateNeededPackages(const QStringList& components, const QVector< PackageInfo >& available, bool addDependencies = true) { QVector< PackageInfo > result; for (QStringList::const_iterator it = components.begin(); it != components.end(); ++it) { static bool recursion = false; static QStringList hitComponents; if (!recursion) hitComponents.clear(); if (hitComponents.contains(*it)) throw Error(QObject::tr("Circular dependencies detected").arg(*it)); hitComponents.push_back(*it); recursion = true; verbose() << "Trying to find a package for name " << *it << "... "; const PackageInfo info = findMatchingPackage(*it, available); if (info.name.isEmpty()) { verbose() << "Not found :-o" << std::endl; verbose() << " Couldn't find package for component " << *it << " bailing out..." << std::endl; throw Error(QObject::tr("Couldn't find package for component %1").arg(*it)); } verbose() << "Found." << std::endl; if (!result.contains(info)) { result.push_back(info); if (addDependencies) { QVector< PackageInfo > dependencies; if (!info.dependencies.isEmpty()) { verbose() << " It depends on:" << std::endl; for (QStringList::const_iterator dep = info.dependencies.begin(); dep != info.dependencies.end(); ++dep) verbose() << " " << *dep << std::endl; dependencies += calculateNeededPackages(info.dependencies, available); } // append all child items, as this package was requested explicitely dependencies += packagesWithPrefix(available, info.name); for (QVector< PackageInfo >::const_iterator dep = dependencies.begin(); dep != dependencies.end(); ++dep) { if (result.contains(*dep)) continue; result += *dep; const QVector< PackageInfo > depdeps = calculateNeededPackages(QStringList() << dep->name, available); for (QVector< PackageInfo >::const_iterator dep2 = depdeps.begin(); dep2 != depdeps.end(); ++dep2) if (!result.contains(*dep2)) result += *dep2; } } } recursion = false; } return result; } namespace { struct ArchiveFile { ArchiveFile() : uncompressedSize(0) {} quint64 uncompressedSize; QByteArray sha1sum; QString fileName; }; } void QInstaller::compressDirectory(const QString& path, const QString& archivePath) { if (!QFileInfo(path).exists()) throw QInstaller::Error(QObject::tr("Folder %1 does not exist").arg(path)); if (!QFileInfo(path).isDir()) throw QInstaller::Error(QObject::tr("%1 is not a folder").arg(path)); QFile archive(archivePath); openForWrite(&archive, archivePath); Lib7z::createArchive(&archive, path); } void QInstaller::compressMetaDirectories(const QString& configDir, const QString& repoDir) { const QString configfile = QFileInfo(configDir, QLatin1String("config.xml")).absoluteFilePath(); const QInstaller::InstallerSettings settings = QInstaller::InstallerSettings::fromFileAndPrefix(configfile, configDir); KDUpdaterCrypto crypto; crypto.setPrivateKey(settings.privateKey()); ConsolePasswordProvider passwordProvider; crypto.setPrivatePasswordProvider(&passwordProvider); QDir dir(repoDir); const QStringList sub = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QString& i, sub) { QDir sd(dir); sd.cd(i); const QString absPath = sd.absolutePath(); const QString fn = QLatin1String("meta.7z"); const QString tmpTarget = repoDir + QLatin1String("/") +fn; compressDirectory(absPath, tmpTarget); QFile tmp(tmpTarget); const QString finalTarget = absPath + QLatin1String("/") + fn; if (!tmp.rename(finalTarget)) { throw QInstaller::Error(QObject::tr("Could not move %1 to %2").arg(tmpTarget, finalTarget)); } // if we have a private key, sign the meta.7z file if (!settings.privateKey().isEmpty()) { verbose() << "Adding a RSA signature to " << finalTarget << std::endl; const QByteArray signature = crypto.sign(finalTarget); QFile sigFile(finalTarget + QLatin1String(".sig")); if (!sigFile.open(QIODevice::WriteOnly)) { throw QInstaller::Error(QObject::tr("Could not open %1 for writing") .arg(finalTarget)); } sigFile.write(signature); } } } void QInstaller::generateMetaDataDirectory(const QString& metapath_, const QString& dataDir, const QVector< PackageInfo >& packages, const QString& appName, const QString& appVersion) { QString metapath = metapath_; if (QFileInfo(metapath).isRelative()) metapath = QDir::cleanPath(QDir::current().absoluteFilePath(metapath)); verbose() << "Generating meta data..." << std::endl; if (!QFile::exists(metapath)) QInstaller::mkpath(metapath); QDomDocument doc; QDomElement root; // use existing Updates.xml, if any QFile existingUpdatesXml(QFileInfo(dataDir, QLatin1String("Updates.xml")).absoluteFilePath()); if (!existingUpdatesXml.open(QIODevice::ReadOnly) || !doc.setContent(&existingUpdatesXml)) { root = doc.createElement("Updates"); root.appendChild(doc.createElement("ApplicationName")).appendChild( doc.createTextNode(appName)); root.appendChild(doc.createElement("ApplicationVersion")).appendChild( doc.createTextNode(appVersion)); root.appendChild(doc.createElement("Checksum")).appendChild( doc.createTextNode(QLatin1String("true"))); } else { root = doc.documentElement(); } for (QVector< PackageInfo >::const_iterator it = packages.begin(); it != packages.end(); ++it) { const QString packageXmlPath = QString::fromLatin1("%1/meta/package.xml").arg(it->directory); verbose() << " Generating meta data for package " << it->name << " using " << packageXmlPath << std::endl;; // remove existing entry for thes component from existing Updates.xml const QDomNodeList packageNodes = root.childNodes(); for (int i = 0; i < packageNodes.count(); ++i) { const QDomNode node = packageNodes.at(i); if (node.nodeName() != QLatin1String("PackageUpdate")) continue; if (node.firstChildElement(QLatin1String("Name")).text() != it->name) continue; root.removeChild(node); --i; } QDomDocument packageXml; QFile file(packageXmlPath); openForRead(&file, packageXmlPath); QString errMsg; int col = 0; int line = 0; if (!packageXml.setContent(&file, &errMsg, &line, &col)) { throw Error(QObject::tr("Could not parse %1: %2:%3: %4 (%5)").arg(packageXmlPath, QString::number(line), QString::number(col), errMsg, it->name)); } const QDomNode package = packageXml.firstChildElement("Package"); QDomElement update = doc.createElement("PackageUpdate"); const QDomNodeList childNodes = package.childNodes(); for (int i = 0; i < childNodes.count(); ++i) { const QDomNode node = childNodes.at(i); // just skip the comments... if (node.isComment()) continue; const QString key = node.nodeName(); if (key == QString::fromLatin1("UserInterfaces")) continue; if (key == QString::fromLatin1("Translations")) continue; if (key == QString::fromLatin1("Licenses")) continue; const QString value = node.toElement().text(); update.appendChild(doc.createElement(key)).appendChild(doc.createTextNode(value)); } // get the size of the data quint64 componentSize = 0; quint64 compressedComponentSize = 0; const QString cmpDataDir = QString::fromLatin1("%1/%2").arg(dataDir, it->name); const QFileInfoList entries = !QDir(cmpDataDir + QLatin1String("/data")).exists() ? QDir(cmpDataDir).entryInfoList(QDir::Files | QDir::NoDotAndDotDot) : QDir(cmpDataDir + QLatin1String("/data")).entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); QVector archiveFiles; Q_FOREACH (const QFileInfo& fi, entries) { if (fi.isHidden()) continue; try { if (fi.isDir()) { QDirIterator recursDirIt(fi.filePath(), QDirIterator::Subdirectories); while (recursDirIt.hasNext()) { componentSize += QFile(recursDirIt.next()).size(); compressedComponentSize += QFile(recursDirIt.next()).size(); } } else if (Lib7z::isSupportedArchive(fi.filePath())) { // if it's an archive already, list it's files and sum the uncompressed sizes QFile archive(fi.filePath()); compressedComponentSize += archive.size(); archive.open(QIODevice::ReadOnly); const QVector< Lib7z::File > files = Lib7z::listArchive(&archive); for (QVector< Lib7z::File >::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) { componentSize += fileIt->uncompressedSize; } } else { // otherwise just add it's size componentSize += fi.size(); compressedComponentSize += fi.size(); } } catch(...) { // ignore, that's just about the sizes - and size doesn't matter, you know? } } // add fake update files const QStringList platforms = QStringList() << "Windows" << "MacOSX" << "Linux"; Q_FOREACH (const QString& platform, platforms) { QDomElement file = doc.createElement("UpdateFile"); file.setAttribute("OS", platform); file.setAttribute("UncompressedSize", componentSize); file.setAttribute("CompressedSize", compressedComponentSize); file.appendChild(doc.createTextNode(QLatin1String("(null)"))); update.appendChild(file); } root.appendChild(update); if (!QDir(metapath).mkpath(it->name)) throw Error(QObject::tr("Could not create directory %1").arg(it->name)); // copy scripts const QString script = package.firstChildElement("Script").text(); if (!script.isEmpty()) { verbose() << " Copying associated script " << script << " into the meta package..."; if (!QFile::copy(QString::fromLatin1("%1/meta/%2").arg(it->directory, script), QString::fromLatin1("%1/%2/%3").arg(metapath, it->name, script))) { verbose() << "failed!" << std::endl; throw Error(QObject::tr("Could not copy the scriot %1 to its target location (%2)") .arg(script, it->name)); } else { verbose() << std::endl; } } // copy user interfaces const QDomNodeList uiNodes = package.firstChildElement("UserInterfaces").childNodes(); QStringList userinterfaces; for (int i = 0; i < uiNodes.count(); ++i) { const QDomNode node = uiNodes.at(i); if (node.nodeName() != QString::fromLatin1("UserInterface")) continue; const QDir dir(QString::fromLatin1("%1/meta").arg(it->directory)); const QStringList uis = dir.entryList(QStringList(node.toElement().text()), QDir::Files); if (uis.isEmpty()) { throw Error(QObject::tr("Couldn't find any user interface matching %1 while copying " "user interfaces of %2").arg(node.toElement().text(), it->name)); } for (QStringList::const_iterator ui = uis.begin(); ui != uis.end(); ++ui) { verbose() << " Copying associated user interface " << *ui << " into the meta " "package..."; userinterfaces.push_back(*ui); if (!QFile::copy(QString::fromLatin1("%1/meta/%2").arg(it->directory, *ui), QString::fromLatin1("%1/%2/%3").arg(metapath, it->name, *ui))) { verbose() << "failed!" << std::endl; throw Error(QObject::tr("Could not copy the UI file %1 to its target location " "(%2)").arg(*ui, it->name)); } else { verbose() << std::endl; } } } if (!userinterfaces.isEmpty()) { update.appendChild(doc.createElement(QString::fromLatin1("UserInterfaces"))) .appendChild(doc.createTextNode(userinterfaces.join(QChar::fromLatin1(',')))); } // copy translations const QDomNodeList qmNodes = package.firstChildElement("Translations").childNodes(); QStringList translations; for (int i = 0; i < qmNodes.count(); ++i) { const QDomNode node = qmNodes.at(i); if (node.nodeName() != QString::fromLatin1("Translation")) continue; const QDir dir(QString::fromLatin1("%1/meta").arg(it->directory)); const QStringList qms = dir.entryList(QStringList(node.toElement().text()), QDir::Files); if (qms.isEmpty()) { throw Error(QObject::tr("Could not find any user interface matching %1 while " "copying user interfaces of %2").arg(node.toElement().text(), it->name)); } for (QStringList::const_iterator qm = qms.begin(); qm != qms.end(); ++qm) { verbose() << " Copying associated translation " << *qm << " into the meta " "package..."; translations.push_back(*qm); if (!QFile::copy(QString::fromLatin1("%1/meta/%2").arg(it->directory, *qm), QString::fromLatin1("%1/%2/%3").arg(metapath, it->name, *qm))) { verbose() << "failed!" << std::endl; throw Error(QObject::tr("Could not copy the translation %1 to its target " "location (%2)").arg(*qm, it->name)); } else { verbose() << std::endl; } } } if (!translations.isEmpty()) { update.appendChild(doc.createElement(QString::fromLatin1("Translations"))) .appendChild(doc.createTextNode(translations.join(QChar::fromLatin1(',')))); } // copy license files const QDomNodeList licenseNodes = package.firstChildElement("Licenses").childNodes(); for (int i = 0; i < licenseNodes.count(); ++i) { const QDomNode licenseNode = licenseNodes.at(i); if (licenseNode.nodeName() == QLatin1String("License")) { const QString &licenseFile = licenseNode.toElement().attributeNode(QLatin1String("file")).value(); const QString &sourceFile = QString::fromLatin1("%1/meta/%2").arg(it->directory).arg(licenseFile); if (!QFile::exists(sourceFile)) { throw Error(QObject::tr("Could not find any license matching %1 while " "copying license files of %2").arg(licenseFile, it->name)); } verbose() << " Copying associated license file " << licenseFile << " into " "the meta package..."; if (!QFile::copy(sourceFile, QString::fromLatin1("%1/%2/%3") .arg(metapath, it->name, licenseFile))) { verbose() << "failed!" << std::endl; throw Error(QObject::tr("Could not copy the license file %1 to its " "target location (%2)").arg(licenseFile, it->name)); } else { verbose() << std::endl; } } } if (licenseNodes.count() > 0) update.appendChild(package.firstChildElement("Licenses").cloneNode()); } doc.appendChild(root); const QString updatesXmlFile = QFileInfo(metapath, "Updates.xml").absoluteFilePath(); QFile updatesXml(updatesXmlFile); openForWrite(&updatesXml, updatesXmlFile); blockingWrite(&updatesXml, doc.toByteArray()); } QVector QInstaller::createListOfPackages(const QStringList& components, const QString& packagesDirectory, bool addDependencies) { const QVector< PackageInfo > available = collectAvailablePackages(packagesDirectory); verbose() << "Calculating dependencies for selected packages..." << std::endl; QVector needed = calculateNeededPackages(components, available, addDependencies); verbose() << "The following packages will be placed in the installer:" << std::endl; Q_FOREACH (const PackageInfo& i, needed) { verbose() << " " << i.name; if (!i.version.isEmpty()) verbose() << "-" << i.version; verbose() << std::endl; } // now just append the virtual parents (not including all their descendants!) // like... if com.nokia.sdk.qt.qtcore was passed, even com.nokia.sdk.qt will show up in the tree if (addDependencies) { for (int i = 0; i < needed.count(); ++i) { const PackageInfo& package = needed[ i ]; const QString name = package.name; const QString version = package.version; QString id = name.section(QChar::fromLatin1('.'), 0, -2); while (!id.isEmpty()) { PackageInfo info; if (!version.isEmpty()) info = findMatchingPackage(QString::fromLatin1("%1-%2").arg(id, version), available); if (info.name.isEmpty()) info = findMatchingPackage(id, available); if (!info.name.isEmpty() && !allPackagesHavePrefix(needed, id) && !needed.contains(info)) { verbose() << "Adding " << info.name << " as it is the virtual parent item of " << name << std::endl; needed.push_back(info); } id = id.section(QChar::fromLatin1('.'), 0, -2); } } } return needed; } QMap QInstaller::buildPathToVersionMap(const QVector& info) { QMap map; Q_FOREACH (PackageInfo inf, info) { map[inf.name] = inf.version; } return map; } static void writeSHA1ToNodeWithName(QDomDocument& doc, QDomNodeList& list, const QByteArray& sha1sum, const QString& nodename) { verbose() << "searching sha1sum node for " << nodename << std::endl; for (int i = 0; i < list.size(); ++i) { QDomNode curNode = list.at(i); QDomNode nameTag = curNode.firstChildElement(QLatin1String("Name")); if (!nameTag.isNull() && nameTag.toElement().text() == nodename) { QDomNode sha1Node = doc.createElement(QLatin1String("SHA1")); sha1Node.appendChild(doc.createTextNode(QString::fromLatin1(sha1sum.toHex().constData()))); curNode.appendChild(sha1Node); } } } void QInstaller::compressMetaDirectories(const QString& configDir, const QString& repoDir, const QString& baseDir, const QMap& versionMapping) { const QString configfile = QFileInfo(configDir, QLatin1String("config.xml")).absoluteFilePath(); const QInstaller::InstallerSettings settings = QInstaller::InstallerSettings::fromFileAndPrefix(configfile, configDir); KDUpdaterCrypto crypto; crypto.setPrivateKey(settings.privateKey()); ConsolePasswordProvider passwordProvider; crypto.setPrivatePasswordProvider(&passwordProvider); QDomDocument doc; QDomElement root; // use existing Updates.xml, if any QFile existingUpdatesXml(QFileInfo(QDir(repoDir), QLatin1String("Updates.xml")).absoluteFilePath()); if (!existingUpdatesXml.open(QIODevice::ReadOnly) || !doc.setContent(&existingUpdatesXml)) { verbose() << "Could not find Updates.xml" << std::endl; } else { root = doc.documentElement(); } existingUpdatesXml.close(); QDir dir(repoDir); const QStringList sub = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); QDomNodeList elements = doc.elementsByTagName(QLatin1String("PackageUpdate")); Q_FOREACH (const QString& i, sub) { QDir sd(dir); sd.cd(i); const QString path = QString(i).remove(baseDir); const QString versionPrefix = versionMapping[path]; if (path.isNull()) continue; const QString absPath = sd.absolutePath(); const QString fn = QLatin1String(versionPrefix.toLatin1() + "meta.7z"); const QString tmpTarget = repoDir + QLatin1String("/") +fn; compressDirectory(absPath, tmpTarget); QFile tmp(tmpTarget); tmp.open(QFile::ReadOnly); QByteArray fileToCheck = tmp.readAll(); QByteArray sha1Sum = QCryptographicHash::hash(fileToCheck, QCryptographicHash::Sha1); writeSHA1ToNodeWithName(doc, elements, sha1Sum, path); const QString finalTarget = absPath + QLatin1String("/") + fn; if (!tmp.rename(finalTarget)) throw QInstaller::Error(QObject::tr("Could not move %1 to %2").arg(tmpTarget, finalTarget)); // if we have a private key, sign the meta.7z file if (!settings.privateKey().isEmpty()) { verbose() << "Adding a RSA signature to " << finalTarget << std::endl; const QByteArray signature = crypto.sign(finalTarget); QFile sigFile(finalTarget + QLatin1String(".sig")); if (!sigFile.open(QIODevice::WriteOnly)) throw QInstaller::Error(QObject::tr("Could not open %1 for writing").arg(finalTarget)); sigFile.write(signature); } } openForWrite(&existingUpdatesXml, existingUpdatesXml.fileName()); blockingWrite(&existingUpdatesXml, doc.toByteArray()); existingUpdatesXml.close(); } void QInstaller::copyComponentData(const QString& packageDir, const QString& configDir, const QString& repoDir, const QVector& infos) { const QString configfile = QFileInfo(configDir, QLatin1String("config.xml")).absoluteFilePath(); const QInstaller::InstallerSettings settings = QInstaller::InstallerSettings::fromFileAndPrefix(configfile, configDir); KDUpdaterCrypto crypto; crypto.setPrivateKey(settings.privateKey()); ConsolePasswordProvider passwordProvider; crypto.setPrivatePasswordProvider(&passwordProvider); Q_FOREACH (const PackageInfo& info, infos) { const QString i = info.name; verbose() << "Copying component data for " << i << std::endl; const QString dataDirPath = QString::fromLatin1("%1/%2/data").arg(packageDir, i); const QDir dataDir(dataDirPath); if (!QDir().mkpath(QString::fromLatin1("%1/%2").arg(repoDir, i))) { throw QInstaller::Error(QObject::tr("Could not create repository folder for " "component %1").arg(i)); } const QStringList files = dataDir.entryList(QDir::Files); Q_FOREACH (const QString& file, files) { QFile tmp(dataDir.absoluteFilePath(file)); openForRead(&tmp, tmp.fileName()); const QString target = QString::fromLatin1("%1/%2/%4%3").arg(repoDir, i, file, info.version); verbose() << QString::fromLatin1("Copying archive from %1 to %2").arg(tmp.fileName(), target) << std::endl; if (!tmp.copy(target)) { throw QInstaller::Error(QObject::tr("Could not copy %1 to %2: %3") .arg(tmp.fileName(), target, tmp.errorString())); } QFile archiveFile(target); QString archiveHashFileName = archiveFile.fileName(); archiveHashFileName += QLatin1String(".sha1"); verbose() << "Hash is stored in "<< archiveHashFileName << std::endl; QFile archiveHashFile(archiveHashFileName); try { openForRead(&archiveFile, archiveFile.fileName()); openForWrite(&archiveHashFile, archiveHashFile.fileName()); const QByteArray archiveData = archiveFile.readAll(); archiveFile.close(); const QByteArray hashOfArchiveData = QCryptographicHash::hash(archiveData, QCryptographicHash::Sha1).toHex(); archiveHashFile.write(hashOfArchiveData); archiveHashFile.close(); } catch(const Error& /*e*/) { //verbose() << e.message() << std::endl; archiveHashFile.close(); archiveFile.close(); throw; } } const QStringList dirs = dataDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QString& dir, dirs) { verbose() << "Compressing data directory " << dir << std::endl; const QString archiveName = QString::fromLatin1("%1/%2/%4%3.7z").arg(repoDir, i, dir, info.version); compressDirectory(dataDir.absoluteFilePath(dir), archiveName); verbose() << "Creating hash of archive "<< archiveName << std::endl; QFile archiveFile(archiveName); QString archiveHashFileName = archiveFile.fileName(); archiveHashFileName += QLatin1String(".sha1"); verbose() << "Hash is stored in "<< archiveHashFileName << std::endl; QFile archiveHashFile(archiveHashFileName); try { openForRead(&archiveFile, archiveFile.fileName()); openForWrite(&archiveHashFile, archiveHashFile.fileName()); const QByteArray archiveData = archiveFile.readAll(); archiveFile.close(); const QByteArray hashOfArchiveData = QCryptographicHash::hash(archiveData, QCryptographicHash::Sha1).toHex(); archiveHashFile.write(hashOfArchiveData); archiveHashFile.close(); } catch(const Error& /*e*/) { //std::cerr << e.message() << std::endl; archiveHashFile.close(); archiveFile.close(); throw; } } // if we have a private key, sign all target files - including those we compressed ourself if (!settings.privateKey().isEmpty()) { const QDir compDataDir(QString::fromLatin1("%1/%2").arg(repoDir, i)); const QStringList targetFiles = compDataDir.entryList(QDir::Files); for (QStringList::const_iterator it = targetFiles.begin(); it != targetFiles.end(); ++it) { verbose() << "Adding a RSA signature to " << *it << std::endl; const QByteArray signature = crypto.sign(compDataDir.absoluteFilePath(*it)); QFile sigFile(compDataDir.absoluteFilePath(*it) + QLatin1String(".sig")); if (!sigFile.open(QIODevice::WriteOnly)) { throw QInstaller::Error(QObject::tr("Could not open %1 for writing: %2") .arg(sigFile.fileName(), sigFile.errorString())); } sigFile.write(signature); } } } }