[OpenDocString] kdeconnect-kde (cpp)
shareplugin.cpp
SharePlugin::SharePlugin(QObject *parent, const QVariantList &args)
    : KdeConnectPlugin(parent, args)
    , m_compositeJob()
{
}
Constructs a plugin object and its sub-jobs.
QUrl SharePlugin::destinationDir() const
{
    const QString defaultDownloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
    QUrl dir = QUrl::fromLocalFile(config()->getString(QStringLiteral("incoming_path"), defaultDownloadPath));

    if (dir.path().contains(QLatin1String("%1"))) {
        dir.setPath(dir.path().arg(device()->name()));
    }

    KJob *job = KIO::mkpath(dir);
    bool ret = job->exec();
    if (!ret) {
        qWarning() << "couldn't create" << dir;
    }

    return dir;
}
Returns a QUrl object that represents the destination directory for the current device. It first gets the incoming_path value from the configuration file, and if it contains the "arg" parameter, it creates it and returns it.
QUrl SharePlugin::getFileDestination(const QString filename) const
{
    const QUrl dir = destinationDir().adjusted(QUrl::StripTrailingSlash);
    QUrl destination(dir);
    destination.setPath(dir.path() + QStringLiteral("/") + filename, QUrl::DecodedMode);
    if (destination.isLocalFile() && QFile::exists(destination.toLocalFile())) {
        destination.setPath(dir.path() + QStringLiteral("/") + KFileUtils::suggestName(dir, filename), QUrl::DecodedMode);
    }
    return destination;
}
Returns a QUrl object that represents the destination of the file by appending the filename to it. It also checks if the destination exists. If it doesn't it creates one.
static QString cleanFilename(const QString &filename)
{
    int idx = filename.lastIndexOf(QLatin1Char('/'));
    return idx >= 0 ? filename.mid(idx + 1) : filename;
}
Removes the slash character from the end of a filename.
void SharePlugin::setDateModified(const QUrl &destination, const qint64 timestamp)
{
    QFile receivedFile(destination.toLocalFile());
    if (!receivedFile.exists() || !receivedFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
        return;
    }
    receivedFile.setFileTime(QDateTime::fromMSecsSinceEpoch(timestamp), QFileDevice::FileTime(QFileDevice::FileModificationTime));
}
Sets the date modified time on the given destination file by setting its modification time to the given timestamp.
void SharePlugin::setDateCreated(const QUrl &destination, const qint64 timestamp)
{
    QFile receivedFile(destination.toLocalFile());
    if (!receivedFile.exists() || !receivedFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
        return;
    }
    receivedFile.setFileTime(QDateTime::fromMSecsSinceEpoch(timestamp), QFileDevice::FileTime(QFileDevice::FileBirthTime));
}
Sets the date created timestamp on the received file by reading it from the given destination url, and setting it to the given timestamp.
bool SharePlugin::receivePacket(const NetworkPacket &np)
{
    /*
        //TODO: Write a test like this
        if (np.type() == PACKET_TYPE_PING) {

            qCDebug(KDECONNECT_PLUGIN_SHARE) << "sending file" << (QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.bashrc");

            NetworkPacket out(PACKET_TYPE_SHARE_REQUEST);
            out.set("filename", mDestinationDir + "itworks.txt");
            AutoClosingQFile* file = new AutoClosingQFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.bashrc"); //Test file to
       transfer

            out.setPayload(file, file->size());

            device()->sendPacket(out);

            return true;

        }
    */

    qCDebug(KDECONNECT_PLUGIN_SHARE) << "File transfer";

    if (np.hasPayload() || np.has(QStringLiteral("filename"))) {
        //         qCDebug(KDECONNECT_PLUGIN_SHARE) << "receiving file" << filename << "in" << dir << "into" << destination;
        const QString filename = cleanFilename(np.get(QStringLiteral("filename"), QString::number(QDateTime::currentMSecsSinceEpoch())));
        QUrl destination = getFileDestination(filename);

        if (np.hasPayload()) {
            qint64 dateCreated = np.get(QStringLiteral("creationTime"), QDateTime::currentMSecsSinceEpoch());
            qint64 dateModified = np.get(QStringLiteral("lastModified"), QDateTime::currentMSecsSinceEpoch());
            const bool open = np.get(QStringLiteral("open"), false);

            if (!m_compositeJob) {
                m_compositeJob = new CompositeFileTransferJob(device()->id());
                m_compositeJob->setProperty("destUrl", destinationDir().toString());
                m_compositeJob->setProperty("immediateProgressReporting", true);
                Daemon::instance()->jobTracker()->registerJob(m_compositeJob);
            }

            FileTransferJob *job = np.createPayloadTransferJob(destination);
            job->setOriginName(device()->name() + QStringLiteral(": ") + filename);
            connect(job, &KJob::result, this, [this, dateCreated, dateModified, open](KJob *job) -> void {
                finished(job, dateCreated, dateModified, open);
            });
            m_compositeJob->addSubjob(job);

            if (!m_compositeJob->isRunning()) {
                m_compositeJob->start();
            }
        } else {
            QFile file(destination.toLocalFile());
            file.open(QIODevice::WriteOnly);
            file.close();
        }
    } else if (np.has(QStringLiteral("text"))) {
        QString text = np.get(QStringLiteral("text"));

        auto mimeData = new QMimeData;
        mimeData->setText(text);
        KSystemClipboard::instance()->setMimeData(mimeData, QClipboard::Clipboard);

        QUrl url;
        QStringList lines = text.split(QStringLiteral("\n"), Qt::SkipEmptyParts);
        if (lines.count()) {
            url.setUrl(lines[lines.count() - 1].trimmed());
        }

        KNotification *notif = new KNotification(QStringLiteral("textShareReceived"));
        notif->setComponentName(QStringLiteral("kdeconnect"));
        notif->setText(text);
        notif->setTitle(i18nc("@info Some piece of text was received from a connected device", "Shared text from %1 copied to clipboard", device()->name()));
        QStringList actions;
        actions << i18nc("@action:button Edit text with default text editor", "Open in Text Editor");
        if (url.isValid() && (url.scheme() == QStringLiteral("http") || url.scheme() == QStringLiteral("https"))) {
            qDebug() << url;
            actions << i18nc("@action:button Open URL with default app", "Open Link");
        }
        notif->setActions(actions);

        connect(notif, &KNotification::action1Activated, this, [this, text]() {
            KService::Ptr service = KApplicationTrader::preferredService(QStringLiteral("text/plain"));
            const QString defaultApp = service ? service->desktopEntryName() : QString();

            if (defaultApp == QLatin1String("org.kde.kate") || defaultApp == QLatin1String("org.kde.kwrite")) {
                QProcess *proc = new QProcess();
                connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
                proc->start(defaultApp.section(QStringLiteral("."), 2, 2), QStringList(QStringLiteral("--stdin")));
                proc->write(text.toUtf8());
                proc->closeWriteChannel();
            } else {
                QTemporaryFile tmpFile;
                tmpFile.setFileTemplate(QStringLiteral("kdeconnect-XXXXXX.txt"));
                tmpFile.setAutoRemove(false);
                tmpFile.open();
                tmpFile.write(text.toUtf8());
                tmpFile.close();

                const QString fileName = tmpFile.fileName();
                QDesktopServices::openUrl(QUrl::fromLocalFile(fileName));
                Q_EMIT shareReceived(fileName);
            }
        });

        connect(notif, &KNotification::action2Activated, this, [this, url]() {
            QDesktopServices::openUrl(url);
            Q_EMIT shareReceived(url.toString());
        });

        notif->sendEvent();

    } else if (np.has(QStringLiteral("url"))) {
        QUrl url = QUrl::fromEncoded(np.get(QStringLiteral("url")));
        QDesktopServices::openUrl(url);
        Q_EMIT shareReceived(url.toString());
    } else {
        qCDebug(KDECONNECT_PLUGIN_SHARE) << "Error: Nothing attached!";
    }

    return true;
}
This method builds a network package object from an existing network packet. It creates a File object, sets the destination filename, and starts a composite file transfer job, and opens a new file or URL if it doesn't exist. It also creates a FileTransferJob object, sets the dateCreated, dateModified, and open flag to true. If the plugin is running, it creates a FileTransferJob object, sets the result of the operation as well, and sets the notification title and mime data. If the file is opened and opened, it is opened in the default application. It also connects the notification to the default app and sends an signal. If the plugin is not enabled, an error is emitted.
void SharePlugin::finished(KJob *job, const qint64 dateModified, const qint64 dateCreated, const bool open)
{
    FileTransferJob *ftjob = qobject_cast(job);
    if (ftjob && !job->error()) {
        Q_EMIT shareReceived(ftjob->destination().toString());
        setDateCreated(ftjob->destination(), dateCreated);
        setDateModified(ftjob->destination(), dateModified);
        qCDebug(KDECONNECT_PLUGIN_SHARE) << "File transfer finished." << ftjob->destination();
        if (open) {
            QDesktopServices::openUrl(ftjob->destination());
        }
    } else {
        qCDebug(KDECONNECT_PLUGIN_SHARE) << "File transfer failed." << (ftjob ? ftjob->destination() : QUrl());
    }
}
This sends a file transfer job to the share service and sets the date modified and open state of the job.
void SharePlugin::openDestinationFolder()
{
    QDesktopServices::openUrl(destinationDir());
}
Opens the destination folder using the QDesktopServices library.
void SharePlugin::shareUrl(const QUrl &url, bool open)
{
    NetworkPacket packet(PACKET_TYPE_SHARE_REQUEST);
    if (url.isLocalFile()) {
        QSharedPointer ioFile(new QFile(url.toLocalFile()));

        if (!ioFile->exists()) {
            Daemon::instance()->reportError(i18n("Could not share file"), i18n("%1 does not exist", url.toLocalFile()));
            return;
        } else {
            QFileInfo info(*ioFile);
            packet.setPayload(ioFile, ioFile->size());
            packet.set(QStringLiteral("filename"), QUrl(url).fileName());
            packet.set(QStringLiteral("creationTime"), info.birthTime().toMSecsSinceEpoch());
            packet.set(QStringLiteral("lastModified"), info.lastModified().toMSecsSinceEpoch());
            packet.set(QStringLiteral("open"), open);
        }
    } else {
        packet.set(QStringLiteral("url"), url.toString());
    }
    sendPacket(packet);
}
This sends a network packet to share the given url. It takes the ioFile of the url, if it exists, and sets the filename creation time and last modified time to the values of the QFile object.
void SharePlugin::shareUrls(const QStringList &urls)
{
    for (const QString &url : urls) {
        shareUrl(QUrl(url), false);
    }
}
This share the urls of the given strings, ignoring the quality of the URL.
void SharePlugin::shareText(const QString &text)
{
    NetworkPacket packet(PACKET_TYPE_SHARE_REQUEST);
    packet.set(QStringLiteral("text"), text);
    sendPacket(packet);
}
This sends a network packet that shares the given text.
QString SharePlugin::dbusPath() const
{
    return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/share");
}
Returns the dbus path as a QString.