示例#1
0
bool MimeTypeChecker::isWantedCollection(const Collection &collection, const QString &wantedMimeType)
{
    if (wantedMimeType.isEmpty() || !collection.isValid()) {
        return false;
    }

    const QStringList contentMimeTypes = collection.contentMimeTypes();
    if (contentMimeTypes.isEmpty()) {
        return false;
    }

    foreach (const QString &mimeType, contentMimeTypes) {
        if (mimeType.isEmpty()) {
            continue;
        }

        if (mimeType == wantedMimeType) {
            return true;
        }

        QMimeDatabase db;
        const QMimeType mt = db.mimeTypeForName(mimeType);
        if (!mt.isValid()) {
            continue;
        }

        if (mt.inherits(wantedMimeType)) {
            return true;
        }
    }

    return false;
}
void tst_QMimeDatabase::inheritsPerformance()
{
    // Check performance of inherits().
    // This benchmark (which started in 2009 in kmimetypetest.cpp) uses 40 mimetypes.
    QStringList mimeTypes;
    mimeTypes << QLatin1String("image/jpeg") << QLatin1String("image/png") << QLatin1String("image/tiff") << QLatin1String("text/plain") << QLatin1String("text/html");
    mimeTypes += mimeTypes;
    mimeTypes += mimeTypes;
    mimeTypes += mimeTypes;
    QCOMPARE(mimeTypes.count(), 40);
    QMimeDatabase db;
    QMimeType mime = db.mimeTypeForName(QString::fromLatin1("text/x-chdr"));
    QVERIFY(mime.isValid());
    QBENCHMARK {
        QString match;
        foreach (const QString &mt, mimeTypes) {
            if (mime.inherits(mt)) {
                match = mt;
                // of course there would normally be a "break" here, but we're testing worse-case
                // performance here
            }
        }
        QCOMPARE(match, QString::fromLatin1("text/plain"));
    }
    // Numbers from 2011, in release mode:
    // KDE 4.7 numbers: 0.21 msec / 494,000 ticks / 568,345 instr. loads per iteration
    // QMimeBinaryProvider (with Qt 5): 0.16 msec / NA / 416,049 instr. reads per iteration
    // QMimeXmlProvider (with Qt 5): 0.062 msec / NA / 172,889 instr. reads per iteration
    //   (but the startup time is way higher)
    // And memory usage is flat at 200K with QMimeBinaryProvider, while it peaks at 6 MB when
    // parsing XML, and then keeps being around 4.5 MB for all the in-memory hashes.
}
示例#3
0
QNetworkReply* Parse::uploadFile(QUrl url, QString name)
{
    QString filePath = url.toLocalFile();
    if (!isReady() || !QFile::exists(filePath)) return NULL;

    if (name.isEmpty()) name = url.fileName();
    setEndPoint( "files/"+name);

    QMimeDatabase db;
    QMimeType mime = db.mimeTypeForFile(filePath);

    QFile file(filePath);
    if (mime.inherits("text/plain")){
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return NULL;
    }
    else{
        if (!file.open(QIODevice::ReadOnly ))
            return NULL;
    }

    initHeaders();
    setHeader(QNetworkRequest::ContentTypeHeader, mime.name().toUtf8());

    m_conn = connect(this, &BaaS::replyFinished, [=]( QJsonDocument json){
        disconnect(m_conn);
        if ( getHttpCode() == 201 ){
            currentObject = json.object();
            emit fileUploaded( currentObject);
        }

    } );

    return request( BaaS::POST, file.readAll() );
}
// create table of file lists qualified for search
QHash<SearchWorker::WorkSet, QStringList> SearchWorker::createFileTable(QDir dir, QDir::Filter hidden, QHash<SearchWorker::WorkSet, bool> enabler)
{
    QHash<SearchWorker::WorkSet, QStringList> wtab;
    QStringList filetypefilters;

    if (!enabler[SearchWorker::EnableMimeType]) {
        if (enabler[SearchWorker::EnableTxt]) {
            filetypefilters.clear();
            filetypefilters << "*.txt";
            wtab[SearchWorker::EnableTxt] = dir.entryList(filetypefilters, QDir::Files | hidden);
        }
        if (enabler[SearchWorker::EnableHtml]) {
            filetypefilters.clear();
            filetypefilters << "*.html" << "*.htm";
            wtab[SearchWorker::EnableHtml] = dir.entryList(filetypefilters, QDir::Files | hidden);
        }
        if (enabler[SearchWorker::EnableSrc]) {
            filetypefilters.clear();
            filetypefilters << "*.cpp" << "*.c" << "*.h" << "*.py" << "*.sh" << "*.qml" << "*.js";
            wtab[SearchWorker::EnableSrc] = dir.entryList(filetypefilters, QDir::Files | hidden);
        }
        if (enabler[SearchWorker::EnableApps]) {
            filetypefilters.clear();
            filetypefilters << "*.desktop";
            wtab[SearchWorker::EnableApps] = dir.entryList(filetypefilters, QDir::Files | hidden);
        }
        if (enabler[SearchWorker::EnableSqlite]) {
            filetypefilters.clear();
            filetypefilters << "*.sqlite" << "*.sqlite3" << "*.db";
            wtab[SearchWorker::EnableSqlite] = dir.entryList(filetypefilters, QDir::Files | hidden);
        }
    }

    if (enabler[SearchWorker::EnableMimeType]) {
        filetypefilters.clear();
        QStringList names = dir.entryList(filetypefilters, QDir::Files | hidden);
        QMimeDatabase db;
        for (int i = 0 ; i < names.count() ; ++i) {
            QString fullpath = dir.absoluteFilePath(names.at(i));
            QMimeType mime = db.mimeTypeForFile(fullpath);
            if ( mime.inherits("application/x-sqlite3") ) {
                if (enabler[SearchWorker::EnableSqlite]) wtab[SearchWorker::EnableSqlite].append(names.at(i)); }
            else if ( mime.inherits("application/x-desktop") ) {
                if (enabler[SearchWorker::EnableApps]) wtab[SearchWorker::EnableApps].append(names.at(i)); }
            else if ( mime.inherits("text/html") ) {
                if (enabler[SearchWorker::EnableHtml]) wtab[SearchWorker::EnableHtml].append(names.at(i)); }
            else if ( mime.inherits("text/x-csrc") || mime.inherits("application/x-shellscript")
                   || mime.inherits("text/x-python") || mime.inherits("text/x-qml") ) {
                if (enabler[SearchWorker::EnableSrc]) wtab[SearchWorker::EnableSrc].append(names.at(i)); }
            else if ( mime.inherits("text/plain") ) {
                if (enabler[SearchWorker::EnableTxt]) wtab[SearchWorker::EnableTxt].append(names.at(i)); }
        }
    }
    return wtab;
}
示例#5
0
bool DropHandler::handleURL(const QList<QUrl>& urls_) {
  bool hasUnknown = false;
  QList<QUrl> tc, pdf, bib, ris, ciw;
  foreach(const QUrl& url, urls_) {
    QMimeType ptr;
    // findByURL doesn't work for http, so actually query
    // the url itself
    if(url.scheme() != QLatin1String("http")) {
      QMimeDatabase db;
      ptr = db.mimeTypeForUrl(url);
    } else {
      KIO::MimetypeJob* job = KIO::mimetype(url, KIO::HideProgressInfo);
      KJobWidgets::setWindow(job, GUI::Proxy::widget());
      job->exec();
      QMimeDatabase db;
      ptr = db.mimeTypeForName(job->mimetype());
    }
    if(ptr.inherits(QLatin1String("application/x-tellico"))) {
      tc << url;
    } else if(ptr.inherits(QLatin1String("application/pdf"))) {
      pdf << url;
    } else if(ptr.inherits(QLatin1String("text/x-bibtex")) ||
              ptr.inherits(QLatin1String("application/x-bibtex")) ||
              ptr.inherits(QLatin1String("application/bibtex"))) {
      bib << url;
    } else if(ptr.inherits(QLatin1String("application/x-research-info-systems"))) {
      ris << url;
    } else if(url.fileName().endsWith(QLatin1String(".bib"))) {
      bib << url;
    } else if(url.fileName().endsWith(QLatin1String(".ris"))) {
      ris << url;
    } else if(url.fileName().endsWith(QLatin1String(".ciw"))) {
      ciw << url;
    } else if(ptr.inherits(QLatin1String("text/plain")) && Import::BibtexImporter::maybeBibtex(url)) {
      bib << url;
    } else if(ptr.inherits(QLatin1String("text/plain")) && Import::RISImporter::maybeRIS(url)) {
      ris << url;
    } else if(ptr.inherits(QLatin1String("text/plain")) && Import::CIWImporter::maybeCIW(url)) {
      ciw << url;
    } else {
      myDebug() << "unrecognized type: " << ptr.name() << " (" << url << ")";
      hasUnknown = true;
    }
  }
示例#6
0
QString uwsgiProcess::findApplication(const QDir &projectDir)
{
    QMimeDatabase m_db;

    QDirIterator it(projectDir.absolutePath(), QDir::Files, QDirIterator::Subdirectories);
    while (it.hasNext()) {
        QString file = it.next();
        QMimeType mime = m_db.mimeTypeForFile(file);
        if (mime.inherits(QStringLiteral("application/x-sharedlib"))) {
            return file;
        }
    }
    return QString();
}
示例#7
0
void FolderFilesList::checkNextItem(const QFileInfo &item)
{
    if (m_cancelSearch) {
        return;
    }
    if (item.isFile()) {
        if (!m_binary) {
            QMimeType mimeType = QMimeDatabase().mimeTypeForFile(item);
            if (!mimeType.inherits(QStringLiteral("text/plain"))) {
                return;
            }
        }
        m_files << item.absoluteFilePath();
    }
    else {
        QDir currentDir(item.absoluteFilePath());

        if (!currentDir.isReadable()) {
            qDebug() << currentDir.absolutePath() << "Not readable";
            return;
        }

        QDir::Filters    filter  = QDir::Files | QDir::NoDotAndDotDot | QDir::Readable;
        if (m_hidden)    filter |= QDir::Hidden;
        if (m_recursive) filter |= QDir::AllDirs;
        if (!m_symlinks) filter |= QDir::NoSymLinks;

        // sort the items to have an deterministic order!
        const QFileInfoList currentItems = currentDir.entryInfoList(m_types, filter, QDir::Name | QDir::LocaleAware);

        bool skip;
        for (int i = 0; i<currentItems.size(); ++i) {
            skip = false;
            for (int j=0; j<m_excludeList.size(); j++) {
                if (m_excludeList[j].exactMatch(currentItems[i].fileName())) {
                    skip = true;
                    break;
                }
            }
            if (!skip) {
                checkNextItem(currentItems[i]);
            }
        }
    }
}
示例#8
0
文件: kfileitem.cpp 项目: KDE/kio
// KDE5 TODO: merge with comment()? Need to see what lxr says about the usage of both.
QString KFileItem::mimeComment() const
{
    if (!d) {
        return QString();
    }

    const QString displayType = d->m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_TYPE);
    if (!displayType.isEmpty()) {
        return displayType;
    }

    bool isLocalUrl;
    QUrl url = mostLocalUrl(&isLocalUrl);

    QMimeType mime = currentMimeType();
    // This cannot move to kio_file (with UDS_DISPLAY_TYPE) because it needs
    // the mimetype to be determined, which is done here, and possibly delayed...
    if (isLocalUrl && !d->isSlow() && mime.inherits(QStringLiteral("application/x-desktop"))) {
        KDesktopFile cfg(url.toLocalFile());
        QString comment = cfg.desktopGroup().readEntry("Comment");
        if (!comment.isEmpty()) {
            return comment;
        }
    }

    // Support for .directory file in directories
    if (isLocalUrl && isDir() && isDirectoryMounted(url)) {
        QUrl u(url);
        u.setPath(u.path() + QLatin1String("/.directory"));
        const KDesktopFile cfg(u.toLocalFile());
        const QString comment = cfg.readComment();
        if (!comment.isEmpty()) {
            return comment;
        }
    }

    const QString comment = mime.comment();
    //qDebug() << "finding comment for " << url.url() << " : " << d->m_mimeType->name();
    if (!comment.isEmpty()) {
        return comment;
    } else {
        return mime.name();
    }
}
示例#9
0
文件: window.cpp 项目: Jinxiaohai/QT
//! [5]
QStringList Window::findFiles(const QStringList &files, const QString &text) {
  QProgressDialog progressDialog(this);
  progressDialog.setCancelButtonText(tr("&Cancel"));
  progressDialog.setRange(0, files.size());
  progressDialog.setWindowTitle(tr("Find Files"));

  //! [5] //! [6]
  QMimeDatabase mimeDatabase;
  QStringList foundFiles;

  for (int i = 0; i < files.size(); ++i) {
    progressDialog.setValue(i);
    progressDialog.setLabelText(
        tr("Searching file number %1 of %n...", 0, files.size()).arg(i));
    QCoreApplication::processEvents();
    //! [6]

    if (progressDialog.wasCanceled()) break;

    //! [7]
    const QString fileName = files.at(i);
    const QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileName);
    if (mimeType.isValid() &&
        !mimeType.inherits(QStringLiteral("text/plain"))) {
      qWarning() << "Not searching binary file "
                 << QDir::toNativeSeparators(fileName);
      continue;
    }
    QFile file(fileName);
    if (file.open(QIODevice::ReadOnly)) {
      QString line;
      QTextStream in(&file);
      while (!in.atEnd()) {
        if (progressDialog.wasCanceled()) break;
        line = in.readLine();
        if (line.contains(text, Qt::CaseInsensitive)) {
          foundFiles << files[i];
          break;
        }
      }
    }
  }
  return foundFiles;
}
示例#10
0
void WebApplication::sendFile(const QString &path)
{
    const QDateTime lastModified {QFileInfo(path).lastModified()};

    // find translated file in cache
    auto it = m_translatedFiles.constFind(path);
    if ((it != m_translatedFiles.constEnd()) && (lastModified <= (*it).lastModified)) {
        const QString mimeName {QMimeDatabase().mimeTypeForFileNameAndData(path, (*it).data).name()};
        print((*it).data, mimeName);
        header(Http::HEADER_CACHE_CONTROL, getCachingInterval(mimeName));
        return;
    }

    QFile file {path};
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug("File %s was not found!", qUtf8Printable(path));
        throw NotFoundHTTPError();
    }

    if (file.size() > MAX_ALLOWED_FILESIZE) {
        qWarning("%s: exceeded the maximum allowed file size!", qUtf8Printable(path));
        throw InternalServerErrorHTTPError(tr("Exceeded the maximum allowed file size (%1)!")
                                           .arg(Utils::Misc::friendlyUnit(MAX_ALLOWED_FILESIZE)));
    }

    QByteArray data {file.readAll()};
    file.close();

    const QMimeType mimeType {QMimeDatabase().mimeTypeForFileNameAndData(path, data)};
    const bool isTranslatable {mimeType.inherits(QLatin1String("text/plain"))};

    // Translate the file
    if (isTranslatable) {
        QString dataStr {data};
        translateDocument(dataStr);
        data = dataStr.toUtf8();

        m_translatedFiles[path] = {data, lastModified}; // caching translated file
    }

    print(data, mimeType.name());
    header(Http::HEADER_CACHE_CONTROL, getCachingInterval(mimeType.name()));
}
示例#11
0
bool MimeTypeChecker::isWantedItem(const Item &item, const QString &wantedMimeType)
{
    if (wantedMimeType.isEmpty() || !item.isValid()) {
        return false;
    }

    const QString mimeType = item.mimeType();
    if (mimeType.isEmpty()) {
        return false;
    }

    if (mimeType == wantedMimeType) {
        return true;
    }

    QMimeDatabase db;
    const QMimeType mt = db.mimeTypeForName(mimeType);
    if (!mt.isValid()) {
        return false;
    }

    return mt.inherits(wantedMimeType);
}
示例#12
0
ActionReply SddmAuthHelper::installtheme(const QVariantMap &args)
{
    const QString filePath = args["filePath"].toString();
    if (filePath.isEmpty()) {
        return ActionReply::HelperErrorReply();
    }

    const QString themesBaseDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "sddm/themes", QStandardPaths::LocateDirectory);
    QDir dir(themesBaseDir);
    if (!dir.exists()) {
        return ActionReply::HelperErrorReply();
    }

    qDebug() << "Installing " << filePath << " into " << themesBaseDir;

    if (!QFile::exists(filePath)) {
        return ActionReply::HelperErrorReply();
    }

    QMimeDatabase db;
    QMimeType mimeType = db.mimeTypeForFile(filePath);
    qWarning() << "Postinstallation: uncompress the file";

    QScopedPointer<KArchive> archive;

    //there must be a better way to do this? If not, make a static bool KZip::supportsMimeType(const QMimeType &type); ?
    //or even a factory class in KArchive

    if (mimeType.inherits(QStringLiteral("application/zip"))) {
        archive.reset(new KZip(filePath));
    } else if (mimeType.inherits(QStringLiteral("application/tar"))
                || mimeType.inherits(QStringLiteral("application/x-gzip"))
                || mimeType.inherits(QStringLiteral("application/x-bzip"))
                || mimeType.inherits(QStringLiteral("application/x-lzma"))
                || mimeType.inherits(QStringLiteral("application/x-xz"))
                || mimeType.inherits(QStringLiteral("application/x-bzip-compressed-tar"))
                || mimeType.inherits(QStringLiteral("application/x-compressed-tar"))) {
        archive.reset(new KTar(filePath));
    } else {
        auto e = ActionReply::HelperErrorReply();
        e.setErrorDescription(i18n("Invalid theme package"));
        return e;    }

    if (!archive->open(QIODevice::ReadOnly)) {
        auto e = ActionReply::HelperErrorReply();
        e.setErrorDescription("Could not open file");
        return e;
    }

    auto directory = archive->directory();

    QStringList installedPaths;

    //some basic validation
    //the top level should only have folders, and those folders should contain a valid metadata.desktop file
    //if we get anything else, abort everything before copying
    for(const QString &name: directory->entries()) {
        auto entry = directory->entry(name);
        if (!entry->isDirectory()) {
            auto e = ActionReply::HelperErrorReply();
            e.setErrorDescription(i18n("Invalid theme package"));
            return e;
        }
        auto subDirectory = static_cast<const KArchiveDirectory*>(entry);
        auto metadataFile = subDirectory->file("metadata.desktop");
        if(!metadataFile || !metadataFile->data().contains("[SddmGreeterTheme]")) {
            auto e = ActionReply::HelperErrorReply();
            e.setErrorDescription(i18n("Invalid theme package"));
            return e;
        }
        installedPaths.append(themesBaseDir + '/' + name);
    }

    if (!directory->copyTo(themesBaseDir)) {
        auto e = ActionReply::HelperErrorReply();
        e.setErrorDescription(i18n("Could not decompress archive"));
        return e;
    }

    auto rc = ActionReply::SuccessReply();
    rc.addData(QStringLiteral("installedPaths"), installedPaths);
    return rc;
}
示例#13
0
文件: document.cpp 项目: KDE/okular
bool Document::open( const QString &fileName )
{
    close();

    QMimeDatabase db;
    const QMimeType mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchContent);

    /**
     * We have a zip archive
     */
    if ( mime.inherits(QStringLiteral("application/x-cbz") ) || mime.inherits( QStringLiteral("application/zip") ) ) {
        mArchive = new KZip( fileName );

        if ( !processArchive() ) {
            return false;
        }
    /**
     * We have a TAR archive
     */
    } else if ( mime.inherits( QStringLiteral("application/x-cbt") ) || mime.inherits( QStringLiteral("application/x-gzip") ) ||
                mime.inherits( QStringLiteral("application/x-tar") ) || mime.inherits( QStringLiteral("application/x-bzip") ) ) {
        mArchive = new KTar( fileName );

        if ( !processArchive() ) {
            return false;
        }
    } else if ( mime.inherits( QStringLiteral("application/x-cbr") ) || mime.inherits( QStringLiteral("application/x-rar") ) || mime.inherits( QStringLiteral("application/vnd.rar") ) ) {
        if ( !Unrar::isAvailable() ) {
            mLastErrorString = i18n( "Cannot open document, unrar was not found." );
            return false;
        }

        if ( !Unrar::isSuitableVersionAvailable() ) {
            mLastErrorString = i18n( "The version of unrar on your system is not suitable for opening comicbooks." );
            return false;
        }

        /**
         * We have a rar archive
         */
        mUnrar = new Unrar();

        if ( !mUnrar->open( fileName ) ) {
            delete mUnrar;
            mUnrar = 0;

            return false;
        }

        mEntries = mUnrar->list();
    } else if ( mime.inherits( QStringLiteral("inode/directory") ) ) {
        mDirectory = new Directory();

        if ( !mDirectory->open( fileName ) ) {
            delete mDirectory;
            mDirectory = 0;

            return false;
        }

        mEntries = mDirectory->list();
    } else {
        mLastErrorString = i18n( "Unknown ComicBook format." );
        return false;
    }

    return true;
}
示例#14
0
// Only called when a filename was given
bool KTar::createDevice(QIODevice::OpenMode mode)
{
    if (d->mimetype.isEmpty()) {
        // Find out mimetype manually

        QMimeDatabase db;
        QMimeType mime;
        if (mode != QIODevice::WriteOnly && QFile::exists(fileName())) {
            // Give priority to file contents: if someone renames a .tar.bz2 to .tar.gz,
            // we can still do the right thing here.
            QFile f(fileName());
            if (f.open(QIODevice::ReadOnly)) {
                mime = db.mimeTypeForData(&f);
            }
            if (!mime.isValid()) {
                // Unable to determine mimetype from contents, get it from file name
                mime = db.mimeTypeForFile(fileName(), QMimeDatabase::MatchExtension);
            }
        } else {
            mime = db.mimeTypeForFile(fileName(), QMimeDatabase::MatchExtension);
        }

        //qDebug() << mode << mime->name();

        if (mime.inherits(QString::fromLatin1("application/x-compressed-tar")) || mime.inherits(QString::fromLatin1(application_gzip))) {
            // gzipped tar file (with possibly invalid file name), ask for gzip filter
            d->mimetype = QString::fromLatin1(application_gzip);
        } else if (mime.inherits(QString::fromLatin1("application/x-bzip-compressed-tar")) || mime.inherits(QString::fromLatin1(application_bzip))) {
            // bzipped2 tar file (with possibly invalid file name), ask for bz2 filter
            d->mimetype = QString::fromLatin1(application_bzip);
        } else if (mime.inherits(QString::fromLatin1("application/x-lzma-compressed-tar")) || mime.inherits(QString::fromLatin1(application_lzma))) {
            // lzma compressed tar file (with possibly invalid file name), ask for xz filter
            d->mimetype = QString::fromLatin1(application_lzma);
        } else if (mime.inherits(QString::fromLatin1("application/x-xz-compressed-tar")) || mime.inherits(QString::fromLatin1(application_xz))) {
            // xz compressed tar file (with possibly invalid name), ask for xz filter
            d->mimetype = QString::fromLatin1(application_xz);
        }
    }

    if (d->mimetype == QLatin1String("application/x-tar")) {
        return KArchive::createDevice(mode);
    } else if (mode == QIODevice::WriteOnly) {
        if (!KArchive::createDevice(mode))
            return false;
        if (!d->mimetype.isEmpty()) {
            // Create a compression filter on top of the QSaveFile device that KArchive created.
            //qDebug() << "creating KFilterDev for" << d->mimetype;
            KCompressionDevice::CompressionType type = KFilterDev::compressionTypeForMimeType(d->mimetype);
            KCompressionDevice* compressionDevice = new KCompressionDevice(device(), true, type);
            setDevice(compressionDevice);
        }
        return true;
    } else {
        // The compression filters are very slow with random access.
        // So instead of applying the filter to the device,
        // the file is completely extracted instead,
        // and we work on the extracted tar file.
        // This improves the extraction speed by the tar ioslave dramatically,
        // if the archive file contains many files.
        // This is because the tar ioslave extracts one file after the other and normally
        // has to walk through the decompression filter each time.
        // Which is in fact nearly as slow as a complete decompression for each file.

        Q_ASSERT(!d->tmpFile);
        d->tmpFile = new QTemporaryFile();
        d->tmpFile->setFileTemplate(QLatin1String("ktar-XXXXXX.tar"));
        d->tmpFile->open();
        //qDebug() << "creating tempfile:" << d->tmpFile->fileName();

        setDevice(d->tmpFile);
        return true;
    }
}
示例#15
0
bool PackageJobThread::installPackage(const QString &src, const QString &dest, OperationType operation)
{
    QDir root(dest);
    if (!root.exists()) {
        QDir().mkpath(dest);
        if (!root.exists()) {
            d->errorMessage = i18n("Could not create package root directory: %1", dest);
            d->errorCode = Package::JobError::RootCreationError;
            //qWarning() << "Could not create package root directory: " << dest;
            return false;
        }
    }

    QFileInfo fileInfo(src);
    if (!fileInfo.exists()) {
        d->errorMessage = i18n("No such file: %1", src);
        d->errorCode = Package::JobError::PackageFileNotFoundError;
        return false;
    }

    QString path;
    QTemporaryDir tempdir;
    bool archivedPackage = false;

    if (fileInfo.isDir()) {
        // we have a directory, so let's just install what is in there
        path = src;
        // make sure we end in a slash!
        if (!path.endsWith('/')) {
            path.append('/');
        }
    } else {
        KArchive *archive = 0;
        QMimeDatabase db;
        QMimeType mimetype = db.mimeTypeForFile(src);
        if (mimetype.inherits(QStringLiteral("application/zip"))) {
            archive = new KZip(src);
        } else if (mimetype.inherits(QStringLiteral("application/x-compressed-tar")) ||
                   mimetype.inherits(QStringLiteral("application/x-tar")) ||
                   mimetype.inherits(QStringLiteral("application/x-bzip-compressed-tar")) ||
                   mimetype.inherits(QStringLiteral("application/x-xz")) ||
                   mimetype.inherits(QStringLiteral("application/x-lzma"))) {
            archive = new KTar(src);
        } else {
            //qWarning() << "Could not open package file, unsupported archive format:" << src << mimetype.name();
            d->errorMessage = i18n("Could not open package file, unsupported archive format: %1 %2", src, mimetype.name());
            d->errorCode = Package::JobError::UnsupportedArchiveFormatError;
            return false;
        }

        if (!archive->open(QIODevice::ReadOnly)) {
            //qWarning() << "Could not open package file:" << src;
            delete archive;
            d->errorMessage = i18n("Could not open package file: %1", src);
            d->errorCode = Package::JobError::PackageOpenError;
            return false;
        }

        archivedPackage = true;
        path = tempdir.path() + '/';

        d->installPath = path;

        const KArchiveDirectory *source = archive->directory();
        source->copyTo(path);

        QStringList entries = source->entries();
        if (entries.count() == 1) {
            const KArchiveEntry *entry = source->entry(entries[0]);
            if (entry->isDirectory()) {
                path.append(entry->name()).append("/");
            }
        }

        delete archive;
    }

    QDir packageDir(path);
    QFileInfoList entries = packageDir.entryInfoList(*metaDataFiles);
    KPluginMetaData meta;
    if (!entries.isEmpty()) {
        const QString metadataFilePath = entries.first().filePath();
        if (metadataFilePath.endsWith(QLatin1String(".desktop")))
            meta = KPluginMetaData(metadataFilePath);
        else {
            QFile f(metadataFilePath);
            if(!f.open(QIODevice::ReadOnly)){
                qWarning() << "Couldn't open metadata file" << src << path;
                d->errorMessage = i18n("Could not open metadata file: %1", src);
                d->errorCode = Package::JobError::MetadataFileMissingError;
                return false;
            }
            QJsonObject metadataObject = QJsonDocument::fromJson(f.readAll()).object();
            meta = KPluginMetaData(metadataObject, QString(), metadataFilePath);
        }
    }

    if (!meta.isValid()) {
        qDebug() << "No metadata file in package" << src << path;
        d->errorMessage = i18n("No metadata file in package: %1", src);
        d->errorCode = Package::JobError::MetadataFileMissingError;
        return false;
    }


    QString pluginName = meta.pluginId();
    qDebug() << "pluginname: " << meta.pluginId();
    if (pluginName.isEmpty()) {
        //qWarning() << "Package plugin name not specified";
        d->errorMessage = i18n("Package plugin name not specified: %1", src);
        d->errorCode = Package::JobError::PluginNameMissingError;
        return false;
    }

    // Ensure that package names are safe so package uninstall can't inject
    // bad characters into the paths used for removal.
    QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period.
    if (!validatePluginName.exactMatch(pluginName)) {
        //qDebug() << "Package plugin name " << pluginName << "contains invalid characters";
        d->errorMessage = i18n("Package plugin name %1 contains invalid characters", pluginName);
        d->errorCode = Package::JobError::PluginNameInvalidError;
        return false;
    }

    QString targetName = dest;
    if (targetName[targetName.size() - 1] != '/') {
        targetName.append('/');
    }
    targetName.append(pluginName);

    if (QFile::exists(targetName)) {
        if (operation == Update) {
            KPluginMetaData oldMeta(targetName + QLatin1String("/metadata.desktop"));

            if (oldMeta.serviceTypes() != meta.serviceTypes()) {
                d->errorMessage = i18n("The new package has a different type from the old version already installed.", meta.version(), meta.pluginId(), oldMeta.version());
                d->errorCode = Package::JobError::UpdatePackageTypeMismatchError;
            } else if (isVersionNewer(oldMeta.version(), meta.version())) {
                const bool ok = uninstallPackage(targetName);
                if (!ok) {
                    d->errorMessage = i18n("Impossible to remove the old installation of %1 located at %2. error: %3", pluginName, targetName, d->errorMessage);
                    d->errorCode = Package::JobError::OldVersionRemovalError;
                }
            } else {
                d->errorMessage = i18n("Not installing version %1 of %2. Version %3 already installed.", meta.version(), meta.pluginId(), oldMeta.version());
                d->errorCode = Package::JobError::NewerVersionAlreadyInstalledError;
            }
        } else {
            d->errorMessage = i18n("%1 already exists", targetName);
            d->errorCode = Package::JobError::PackageAlreadyInstalledError;
        }

        if (d->errorCode != KJob::NoError) {
            d->installPath = targetName;
            return false;
        }
    }

    //install dependencies
    const QStringList dependencies = KPluginMetaData::readStringList(meta.rawData(), QStringLiteral("X-KPackage-Dependencies"));
    for(const QString &dep : dependencies) {
        QUrl depUrl(dep);
        if (!installDependency(depUrl)) {
            d->errorMessage = i18n("Could not install dependency: %1", dep);
            d->errorCode = Package::JobError::PackageCopyError;
            return false;
        }
    }

    if (archivedPackage) {
        // it's in a temp dir, so just move it over.
        const bool ok = copyFolder(path, targetName);
        removeFolder(path);
        if (!ok) {
            //qWarning() << "Could not move package to destination:" << targetName;
            d->errorMessage = i18n("Could not move package to destination: %1", targetName);
            d->errorCode = Package::JobError::PackageMoveError;
            return false;
        }
    } else {
        // it's a directory containing the stuff, so copy the contents rather
        // than move them
        const bool ok = copyFolder(path, targetName);
        if (!ok) {
            //qWarning() << "Could not copy package to destination:" << targetName;
            d->errorMessage = i18n("Could not copy package to destination: %1", targetName);
            d->errorCode = Package::JobError::PackageCopyError;
            return false;
        }
    }

    if (archivedPackage) {
        // no need to remove the temp dir (which has been successfully moved if it's an archive)
        tempdir.setAutoRemove(false);
    }


    indexDirectory(dest, QStringLiteral("kpluginindex.json"));


    d->installPath = targetName;

    //qWarning() << "Not updating kbuildsycoca4, since that will go away. Do it yourself for now if needed.";
    return true;
}