diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 7e2d29b..5d197d2 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,285 +1,283 @@ # SPDX-FileCopyrightText: 2023 g10 code GmbH # SPDX-Contributor: Carl Schwan # SPDX-License-Identifier: BSD-2-Clause add_library(gpgol-server-static STATIC) target_sources(gpgol-server-static PRIVATE websocketclient.cpp websocketclient.h webserver.h webserver.cpp # Identity identity/addressvalidationjob.cpp identity/addressvalidationjob.h identity/identitymanager.cpp identity/identitymanager.h identity/identitydialog.cpp identity/identitydialog.h # HTTP Controller controllers/emailcontroller.cpp controllers/emailcontroller.h # Draft draft/draft.cpp draft/draft.h draft/draftmanager.cpp draft/draftmanager.h # EWS integration ews/ewsattachment.cpp ews/ewsattachment.h ews/ewsattendee.cpp ews/ewsattendee.h ews/ewsclient_debug.cpp ews/ewsclient_debug.h ews/ewsid.cpp ews/ewsid.h ews/ewsitem.cpp ews/ewsitem.h ews/ewsitembase.cpp ews/ewsitembase.h ews/ewsitembase_p.h ews/ewsmailbox.cpp ews/ewsmailbox.h ews/ewsmailfactory.cpp ews/ewsmailfactory.h ews/ewsoccurrence.cpp ews/ewsoccurrence.h ews/ewspropertyfield.cpp ews/ewspropertyfield.h ews/ewsrecurrence.cpp ews/ewsrecurrence.h ews/ewsserverversion.cpp ews/ewsserverversion.h ews/ewstypes.cpp ews/ewstypes.h ews/ewsxml.cpp ews/ewsxml.h # Editor editor/addresseelineedit.cpp editor/addresseelineedit.h editor/addresseelineeditmanager.cpp editor/addresseelineeditmanager.h editor/composer.cpp editor/composer.h editor/composerviewbase.cpp editor/composerviewbase.h editor/composerwindow.cpp editor/composerwindow.h editor/composerwindowfactory.cpp editor/composerwindowfactory.h editor/cryptostateindicatorwidget.cpp editor/cryptostateindicatorwidget.h editor/kmcomposerglobalaction.cpp editor/kmcomposerglobalaction.h editor/nearexpirywarning.cpp editor/nearexpirywarning.h editor/mailtemplates.cpp editor/mailtemplates.h editor/recipient.cpp editor/recipient.h editor/recipientline.cpp editor/recipientline.h editor/recipientseditor.cpp editor/recipientseditor.h editor/util.h editor/util.cpp editor/kmailcompletion.cpp editor/kmailcompletion.h editor/richtextcomposerng.cpp editor/richtextcomposerng.h editor/richtextcomposersignatures.cpp editor/richtextcomposersignatures.h editor/nodehelper.cpp editor/nodehelper.h editor/signaturecontroller.cpp editor/signaturecontroller.h editor/spellcheckerconfigdialog.cpp editor/spellcheckerconfigdialog.h # Editor job editor/job/abstractencryptjob.h editor/job/autocryptheadersjob.h editor/job/contentjobbase.h editor/job/contentjobbase_p.h editor/job/dndfromarkjob.cpp editor/job/dndfromarkjob.h editor/job/encryptjob.h editor/job/inserttextfilejob.h editor/job/itipjob.h editor/job/jobbase.h editor/job/jobbase_p.h editor/job/maintextjob.h editor/job/multipartjob.h editor/job/protectedheadersjob.h editor/job/signencryptjob.h editor/job/signjob.h editor/job/singlepartjob.h editor/job/skeletonmessagejob.h editor/job/transparentjob.h editor/job/autocryptheadersjob.cpp editor/job/contentjobbase.cpp editor/job/encryptjob.cpp editor/job/inserttextfilejob.cpp editor/job/itipjob.cpp editor/job/jobbase.cpp editor/job/maintextjob.cpp editor/job/multipartjob.cpp editor/job/protectedheadersjob.cpp editor/job/saveasfilejob.cpp editor/job/saveasfilejob.h editor/job/signencryptjob.cpp editor/job/signjob.cpp editor/job/singlepartjob.cpp editor/job/skeletonmessagejob.cpp editor/job/transparentjob.cpp ## Editor Part editor/part/globalpart.h editor/part/infopart.h editor/part/itippart.h editor/part/messagepart.h editor/part/textpart.h editor/part/globalpart.cpp editor/part/infopart.cpp editor/part/itippart.cpp editor/part/messagepart.cpp editor/part/textpart.cpp ## Attachment editor/attachment/attachmentjob.cpp editor/attachment/attachmentjob.h editor/attachment/attachmentclipboardjob.cpp editor/attachment/attachmentclipboardjob.h editor/attachment/attachmentcompressjob.cpp editor/attachment/attachmentcompressjob.h editor/attachment/attachmentcontroller.cpp editor/attachment/attachmentcontroller.h editor/attachment/attachmentcontrollerbase.cpp editor/attachment/attachmentcontrollerbase.h editor/attachment/attachmentfromfolderjob.cpp editor/attachment/attachmentfromfolderjob.h editor/attachment/attachmentfrommimecontentjob.cpp editor/attachment/attachmentfrommimecontentjob.h editor/attachment/attachmentfromurlbasejob.cpp editor/attachment/attachmentfromurlbasejob.h editor/attachment/attachmentfromurljob.cpp editor/attachment/attachmentfromurljob.h editor/attachment/attachmentfromurlutils.cpp editor/attachment/attachmentfromurlutils.h editor/attachment/attachmentfrompublickeyjob.cpp editor/attachment/attachmentfrompublickeyjob.h editor/attachment/attachmentloadjob.cpp editor/attachment/attachmentloadjob.h editor/attachment/attachmentmodel.cpp editor/attachment/attachmentmodel.h editor/attachment/attachmentpart.cpp editor/attachment/attachmentpart.h editor/attachment/attachmentpropertiesdialog.cpp editor/attachment/attachmentpropertiesdialog.h editor/attachment/attachmentupdatejob.cpp editor/attachment/attachmentupdatejob.h editor/attachment/attachmentview.cpp editor/attachment/attachmentview.h - editor/attachment/editorwatcher.cpp - editor/attachment/editorwatcher.h ) qt_add_resources(gpgol-server-static PREFIX "/" FILES assets/certificate.crt ) ki18n_wrap_ui(gpgol-server-static editor/attachment/ui/attachmentpropertiesdialog.ui editor/attachment/ui/attachmentpropertiesdialog_readonly.ui ) ecm_qt_declare_logging_category(gpgol-server-static_SRCS HEADER websocket_debug.h IDENTIFIER WEBSOCKET_LOG CATEGORY_NAME org.gpgol.server.websocket DESCRIPTION "Websocket connection in the server" EXPORT GPGOL ) ecm_qt_declare_logging_category(gpgol-server-static_SRCS HEADER ewsresource_debug.h IDENTIFIER EWSRES_LOG CATEGORY_NAME org.gpgol.ews DESCRIPTION "Ews mail client" EXPORT GPGOL ) ecm_qt_declare_logging_category(gpgol-server-static_SRCS HEADER ewscli_debug.h IDENTIFIER EWSCLI_LOG CATEGORY_NAME org.gpgol.ews.client DESCRIPTION "ews client (gpgol-server)" EXPORT GPGOL ) ecm_qt_declare_logging_category(gpgol-server-static_SRCS HEADER editor_debug.h IDENTIFIER EDITOR_LOG CATEGORY_NAME org.gpgol.editor DESCRIPTION "mail composer" EXPORT GPGOL ) set(WARN_TOOMANY_RECIPIENTS_DEFAULT true) set(ALLOW_SEMICOLON_AS_ADDRESS_SEPARATOR_DEFAULT true) configure_file(editor/settings/messagecomposer.kcfg.in ${CMAKE_CURRENT_BINARY_DIR}/messagecomposer.kcfg) kconfig_add_kcfg_files(gpgol-server-static editor/settings/messagecomposersettings.kcfgc) install(FILES composerui.rc DESTINATION ${KDE_INSTALL_KXMLGUIDIR}/gpgol-server) target_sources(gpgol-server-static PUBLIC ${gpgol-server-static_SRCS}) target_link_libraries(gpgol-server-static PUBLIC common Qt6::HttpServer Qt6::Widgets Qt6::PrintSupport KF6::CalendarCore KF6::ConfigCore KF6::ConfigGui KF6::Contacts KF6::Completion KF6::CoreAddons KF6::ColorScheme KF6::Codecs KF6::GuiAddons KF6::SonnetUi KF6::WidgetsAddons KF6::XmlGui KF6::KIOFileWidgets KF6::Archive KF6::TextAutoCorrectionCore KPim6::MimeTreeParserWidgets KPim6::Libkleo KPim6::Libkdepim KPim6::LdapWidgets KPim6::PimTextEdit KPim6::IdentityManagementCore KPim6::IdentityManagementWidgets ) add_executable(gpgol-server main.cpp) target_link_libraries(gpgol-server PRIVATE gpgol-server-static) if (BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/server/editor/attachment/attachmentcontroller.cpp b/server/editor/attachment/attachmentcontroller.cpp index 3089c08..31616e6 100644 --- a/server/editor/attachment/attachmentcontroller.cpp +++ b/server/editor/attachment/attachmentcontroller.cpp @@ -1,130 +1,128 @@ /* * This file is part of KMail. * SPDX-FileCopyrightText: 2009 Constantin Berzan * * Parts based on KMail code by: * Various authors. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "attachmentcontroller.h" #include "attachmentview.h" #include "attachmentmodel.h" #include "attachmentpart.h" #include "attachmentview.h" #include "editor/composerwindow.h" #include "editor_debug.h" #include #include #include #include #include #include using namespace MessageCore; AttachmentController::AttachmentController(MessageComposer::AttachmentModel *model, AttachmentView *view, ComposerWindow *composer) : AttachmentControllerBase(model, composer, composer->actionCollection()) , mComposer(composer) , mView(view) { connect(composer, &ComposerWindow::identityChanged, this, &AttachmentController::identityChanged); connect(view, &AttachmentView::contextMenuRequested, this, &AttachmentControllerBase::showContextMenu); connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AttachmentController::selectionChanged); connect(view, &QAbstractItemView::doubleClicked, this, &AttachmentController::doubleClicked); connect(this, &AttachmentController::refreshSelection, this, &AttachmentController::selectionChanged); connect(this, &AttachmentController::showAttachment, this, &AttachmentController::onShowAttachment); connect(this, &AttachmentController::selectedAllAttachment, this, &AttachmentController::slotSelectAllAttachment); connect(this, &AttachmentController::actionsCreated, this, &AttachmentController::slotActionsCreated); } AttachmentController::~AttachmentController() = default; void AttachmentController::slotSelectAllAttachment() { mView->selectAll(); } void AttachmentController::identityChanged() { const KIdentityManagementCore::Identity &identity = mComposer->identity(); // "Attach public key" is only possible if OpenPGP support is available: enableAttachPublicKey(QGpgME::openpgp()); // "Attach my public key" is only possible if OpenPGP support is // available and the user specified his key for the current identity: enableAttachMyPublicKey(QGpgME::openpgp() && !identity.pgpEncryptionKey().isEmpty()); } void AttachmentController::attachMyPublicKey() { const KIdentityManagementCore::Identity &identity = mComposer->identity(); qCDebug(EDITOR_LOG) << identity.identityName(); auto keyCache = Kleo::KeyCache::instance(); auto key = keyCache->findByFingerprint(identity.pgpEncryptionKey().data()); exportPublicKey(key); } void AttachmentController::slotActionsCreated() { // Disable public key actions if appropriate. identityChanged(); // Disable actions like 'Remove', since nothing is currently selected. selectionChanged(); } void AttachmentController::selectionChanged() { const QModelIndexList selectedRows = mView->selectionModel()->selectedRows(); AttachmentPart::List selectedParts; selectedParts.reserve(selectedRows.count()); for (const QModelIndex &index : selectedRows) { auto part = mView->model()->data(index, MessageComposer::AttachmentModel::AttachmentPartRole).value(); selectedParts.append(part); } setSelectedParts(selectedParts); } void AttachmentController::onShowAttachment(KMime::Content *content, const QByteArray &charset) { const QString charsetStr = QString::fromLatin1(charset); if (content->bodyAsMessage()) { KMime::Message::Ptr message(new KMime::Message); message->setContent(content->bodyAsMessage()->encodedContent()); message->parse(); auto dialog = new MimeTreeParser::Widgets::MessageViewerDialog({ message }); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } } void AttachmentController::doubleClicked(const QModelIndex &itemClicked) { if (!itemClicked.isValid()) { qCDebug(EDITOR_LOG) << "Received an invalid item clicked index"; return; } // The itemClicked index will contain the column information. But we want to retrieve // the AttachmentPart, so we must recreate the QModelIndex without the column information const QModelIndex &properItemClickedIndex = mView->model()->index(itemClicked.row(), 0); auto part = mView->model()->data(properItemClickedIndex, MessageComposer::AttachmentModel::AttachmentPartRole).value(); // We can't edit encapsulated messages, but we can view them. if (part->isMessageOrMessageCollection()) { viewAttachment(part); - } else { - editAttachment(part); } } #include "moc_attachmentcontroller.cpp" diff --git a/server/editor/attachment/attachmentcontrollerbase.cpp b/server/editor/attachment/attachmentcontrollerbase.cpp index 359142d..9c5d588 100644 --- a/server/editor/attachment/attachmentcontrollerbase.cpp +++ b/server/editor/attachment/attachmentcontrollerbase.cpp @@ -1,963 +1,860 @@ /* * This file is part of KMail. * SPDX-FileCopyrightText: 2009 Constantin Berzan * * Parts based on KMail code by: * Various authors. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "attachmentcontrollerbase.h" #include "../attachment/attachmentjob.h" #include "../attachment/attachmentmodel.h" #include "../attachment/attachmentupdatejob.h" #include "../attachment/attachmentfrompublickeyjob.h" #include "../attachment/attachmentfromurlbasejob.h" #include "../attachment/attachmentcompressjob.h" #include "../attachment/attachmentclipboardjob.h" #include "../attachment/attachmentfromurlutils.h" #include "../attachment/attachmentpropertiesdialog.h" #include "../composer.h" #include "../part/globalpart.h" -#include "messagecomposersettings.h" #include "editor_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace MessageComposer; using namespace MessageCore; class MessageComposer::AttachmentControllerBase::AttachmentControllerBasePrivate { public: AttachmentControllerBasePrivate(AttachmentControllerBase *qq); ~AttachmentControllerBasePrivate(); void attachmentRemoved(const AttachmentPart::Ptr &part); // slot void compressJobResult(KJob *job); // slot void loadJobResult(KJob *job); // slot void openSelectedAttachments(); // slot void viewSelectedAttachments(); // slot - void editSelectedAttachment(); // slot - void editSelectedAttachmentWith(); // slot void removeSelectedAttachments(); // slot void saveSelectedAttachmentAs(); // slot void selectedAttachmentProperties(); // slot - void editDone(MessageComposer::EditorWatcher *watcher); // slot void attachPublicKeyJobResult(KJob *job); // slot void slotAttachmentContentCreated(KJob *job); // slot void addAttachmentPart(AttachmentPart::Ptr part); void attachClipBoardElement(KJob *job); void selectedAllAttachment(); void createOpenWithMenu(QMenu *topMenu, const AttachmentPart::Ptr &part); void reloadAttachment(); void updateJobResult(KJob *); AttachmentPart::List selectedParts; AttachmentControllerBase *const q; MessageComposer::AttachmentModel *model = nullptr; QWidget *wParent = nullptr; - QHash editorPart; - QHash editorTempFile; KActionCollection *mActionCollection = nullptr; QAction *attachPublicKeyAction = nullptr; QAction *attachMyPublicKeyAction = nullptr; QAction *openContextAction = nullptr; QAction *viewContextAction = nullptr; - QAction *editContextAction = nullptr; - QAction *editWithContextAction = nullptr; QAction *removeAction = nullptr; QAction *removeContextAction = nullptr; QAction *saveAsAction = nullptr; QAction *saveAsContextAction = nullptr; QAction *propertiesAction = nullptr; QAction *propertiesContextAction = nullptr; QAction *addAttachmentFileAction = nullptr; QAction *addAttachmentDirectoryAction = nullptr; QAction *addContextAction = nullptr; QAction *selectAllAction = nullptr; KActionMenu *attachmentMenu = nullptr; QAction *reloadAttachmentAction = nullptr; QAction *attachClipBoardAction = nullptr; // If part p is compressed, uncompressedParts[p] is the uncompressed part. QHash uncompressedParts; bool encryptEnabled = false; bool signEnabled = false; }; AttachmentControllerBase::AttachmentControllerBasePrivate::AttachmentControllerBasePrivate(AttachmentControllerBase *qq) : q(qq) { } AttachmentControllerBase::AttachmentControllerBasePrivate::~AttachmentControllerBasePrivate() = default; void AttachmentControllerBase::setSelectedParts(const AttachmentPart::List &selectedParts) { d->selectedParts = selectedParts; const int selectedCount = selectedParts.count(); - const bool enableEditAction = (selectedCount == 1) && (!selectedParts.first()->isMessageOrMessageCollection()); d->openContextAction->setEnabled(selectedCount > 0); d->viewContextAction->setEnabled(selectedCount > 0); - d->editContextAction->setEnabled(enableEditAction); - d->editWithContextAction->setEnabled(enableEditAction); d->removeAction->setEnabled(selectedCount > 0); d->removeContextAction->setEnabled(selectedCount > 0); d->saveAsAction->setEnabled(selectedCount == 1); d->saveAsContextAction->setEnabled(selectedCount == 1); d->propertiesAction->setEnabled(selectedCount == 1); d->propertiesContextAction->setEnabled(selectedCount == 1); } void AttachmentControllerBase::AttachmentControllerBasePrivate::attachmentRemoved(const AttachmentPart::Ptr &part) { uncompressedParts.remove(part); } void AttachmentControllerBase::AttachmentControllerBasePrivate::compressJobResult(KJob *job) { if (job->error()) { KMessageBox::error(wParent, job->errorString(), i18nc("@title:window", "Failed to compress attachment")); return; } auto ajob = qobject_cast(job); Q_ASSERT(ajob); AttachmentPart::Ptr originalPart = ajob->originalPart(); AttachmentPart::Ptr compressedPart = ajob->compressedPart(); if (ajob->isCompressedPartLarger()) { const int result = KMessageBox::questionTwoActions(wParent, i18n("The compressed attachment is larger than the original. " "Do you want to keep the original one?"), QString(/*caption*/), KGuiItem(i18nc("Do not compress", "Keep")), KGuiItem(i18n("Compress"))); if (result == KMessageBox::ButtonCode::PrimaryAction) { // The user has chosen to keep the uncompressed file. return; } } qCDebug(EDITOR_LOG) << "Replacing uncompressed part in model."; uncompressedParts[compressedPart] = originalPart; bool ok = model->replaceAttachment(originalPart, compressedPart); if (!ok) { // The attachment was removed from the model while we were compressing. qCDebug(EDITOR_LOG) << "Compressed a zombie."; } } void AttachmentControllerBase::AttachmentControllerBasePrivate::loadJobResult(KJob *job) { if (job->error()) { KMessageBox::error(wParent, job->errorString(), i18n("Failed to attach file")); return; } auto ajob = qobject_cast(job); Q_ASSERT(ajob); AttachmentPart::Ptr part = ajob->attachmentPart(); q->addAttachment(part); } void AttachmentControllerBase::AttachmentControllerBasePrivate::openSelectedAttachments() { Q_ASSERT(selectedParts.count() >= 1); for (const AttachmentPart::Ptr &part : std::as_const(selectedParts)) { q->openAttachment(part); } } void AttachmentControllerBase::AttachmentControllerBasePrivate::viewSelectedAttachments() { Q_ASSERT(selectedParts.count() >= 1); for (const AttachmentPart::Ptr &part : std::as_const(selectedParts)) { q->viewAttachment(part); } } -void AttachmentControllerBase::AttachmentControllerBasePrivate::editSelectedAttachment() -{ - Q_ASSERT(selectedParts.count() == 1); - q->editAttachment(selectedParts.constFirst(), MessageComposer::EditorWatcher::NoOpenWithDialog); -} - -void AttachmentControllerBase::AttachmentControllerBasePrivate::editSelectedAttachmentWith() -{ - Q_ASSERT(selectedParts.count() == 1); - q->editAttachment(selectedParts.constFirst(), MessageComposer::EditorWatcher::OpenWithDialog); -} - void AttachmentControllerBase::AttachmentControllerBasePrivate::removeSelectedAttachments() { Q_ASSERT(selectedParts.count() >= 1); // We must store list, otherwise when we remove it changes selectedParts (as selection changed) => it will crash. const AttachmentPart::List toRemove = selectedParts; for (const AttachmentPart::Ptr &part : toRemove) { if (!model->removeAttachment(part)) { qCWarning(EDITOR_LOG) << "Impossible to remove attachment" << part->fileName(); } } } void AttachmentControllerBase::AttachmentControllerBasePrivate::saveSelectedAttachmentAs() { Q_ASSERT(selectedParts.count() == 1); q->saveAttachmentAs(selectedParts.constFirst()); } void AttachmentControllerBase::AttachmentControllerBasePrivate::selectedAttachmentProperties() { Q_ASSERT(selectedParts.count() == 1); q->attachmentProperties(selectedParts.constFirst()); } void AttachmentControllerBase::AttachmentControllerBasePrivate::reloadAttachment() { Q_ASSERT(selectedParts.count() == 1); auto ajob = new AttachmentUpdateJob(selectedParts.constFirst(), q); connect(ajob, &AttachmentUpdateJob::result, q, [this](KJob *job) { updateJobResult(job); }); ajob->start(); } void AttachmentControllerBase::AttachmentControllerBasePrivate::updateJobResult(KJob *job) { if (job->error()) { KMessageBox::error(wParent, job->errorString(), i18n("Failed to reload attachment")); return; } auto ajob = qobject_cast(job); Q_ASSERT(ajob); AttachmentPart::Ptr originalPart = ajob->originalPart(); AttachmentPart::Ptr updatedPart = ajob->updatedPart(); attachmentRemoved(originalPart); bool ok = model->replaceAttachment(originalPart, updatedPart); if (!ok) { // The attachment was removed from the model while we were compressing. qCDebug(EDITOR_LOG) << "Updated a zombie."; } } -void AttachmentControllerBase::AttachmentControllerBasePrivate::editDone(MessageComposer::EditorWatcher *watcher) -{ - AttachmentPart::Ptr part = editorPart.take(watcher); - Q_ASSERT(part); - QTemporaryFile *tempFile = editorTempFile.take(watcher); - Q_ASSERT(tempFile); - if (watcher->fileChanged()) { - qCDebug(EDITOR_LOG) << "File has changed."; - const QString name = watcher->url().path(); - QFile file(name); - if (file.open(QIODevice::ReadOnly)) { - const QByteArray data = file.readAll(); - part->setData(data); - model->updateAttachment(part); - } - } - - delete tempFile; - // The watcher deletes itself. -} - void AttachmentControllerBase::AttachmentControllerBasePrivate::createOpenWithMenu(QMenu *topMenu, const AttachmentPart::Ptr &part) { const QString contentTypeStr = QString::fromLatin1(part->mimeType()); const KService::List offers = KFileItemActions::associatedApplications(QStringList() << contentTypeStr); if (!offers.isEmpty()) { QMenu *menu = topMenu; auto actionGroup = new QActionGroup(menu); connect(actionGroup, &QActionGroup::triggered, q, &AttachmentControllerBase::slotOpenWithAction); if (offers.count() > 1) { // submenu 'open with' menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu); menu->menuAction()->setObjectName(QLatin1StringView("openWith_submenu")); // for the unittest topMenu->addMenu(menu); } // qCDebug(EDITOR_LOG) << offers.count() << "offers" << topMenu << menu; KService::List::ConstIterator it = offers.constBegin(); KService::List::ConstIterator end = offers.constEnd(); for (; it != end; ++it) { /* TODO Carl QAction *act = MessageViewer::Util::createAppAction(*it, // no submenu -> prefix single offer menu == topMenu, actionGroup, menu); menu->addAction(act); */ } QString openWithActionName; if (menu != topMenu) { // submenu menu->addSeparator(); openWithActionName = i18nc("@action:inmenu Open With", "&Other..."); } else { openWithActionName = i18nc("@title:menu", "&Open With..."); } auto openWithAct = new QAction(menu); openWithAct->setText(openWithActionName); QObject::connect(openWithAct, &QAction::triggered, q, &AttachmentControllerBase::slotOpenWithDialog); menu->addAction(openWithAct); } else { // no app offers -> Open With... auto act = new QAction(topMenu); act->setText(i18nc("@title:menu", "&Open With...")); QObject::connect(act, &QAction::triggered, q, &AttachmentControllerBase::slotOpenWithDialog); topMenu->addAction(act); } } void AttachmentControllerBase::exportPublicKey(const GpgME::Key &key) { if (key.isNull() || !QGpgME::openpgp()) { qCWarning(EDITOR_LOG) << "Tried to export key with empty fingerprint, or no OpenPGP."; return; } auto ajob = new MessageComposer::AttachmentFromPublicKeyJob(key, this); connect(ajob, &AttachmentFromPublicKeyJob::result, this, [this](KJob *job) { d->attachPublicKeyJobResult(job); }); ajob->start(); } void AttachmentControllerBase::AttachmentControllerBasePrivate::attachPublicKeyJobResult(KJob *job) { // The only reason we can't use loadJobResult() and need a separate method // is that we want to show the proper caption ("public key" instead of "file")... if (job->error()) { KMessageBox::error(wParent, job->errorString(), i18n("Failed to attach public key")); return; } Q_ASSERT(dynamic_cast(job)); auto ajob = static_cast(job); AttachmentPart::Ptr part = ajob->attachmentPart(); q->addAttachment(part); } void AttachmentControllerBase::AttachmentControllerBasePrivate::attachClipBoardElement(KJob *job) { if (job->error()) { qCDebug(EDITOR_LOG) << " Error during when get try to attach text from clipboard"; KMessageBox::error(wParent, job->errorString(), i18n("Failed to attach text from clipboard")); return; } auto ajob = static_cast(job); AttachmentPart::Ptr part = ajob->attachmentPart(); q->addAttachment(part); } static QTemporaryFile *dumpAttachmentToTempFile(const AttachmentPart::Ptr &part) // local { auto file = new QTemporaryFile; if (!file->open()) { qCCritical(EDITOR_LOG) << "Could not open tempfile" << file->fileName(); delete file; return nullptr; } if (file->write(part->data()) == -1) { qCCritical(EDITOR_LOG) << "Could not dump attachment to tempfile."; delete file; return nullptr; } file->flush(); return file; } AttachmentControllerBase::AttachmentControllerBase(MessageComposer::AttachmentModel *model, QWidget *wParent, KActionCollection *actionCollection) : QObject(wParent) , d(new AttachmentControllerBasePrivate(this)) { d->model = model; connect(model, &MessageComposer::AttachmentModel::attachUrlsRequested, this, &AttachmentControllerBase::addAttachments); connect(model, &MessageComposer::AttachmentModel::attachmentRemoved, this, [this](const MessageCore::AttachmentPart::Ptr &attr) { d->attachmentRemoved(attr); }); connect(model, &AttachmentModel::attachmentCompressRequested, this, &AttachmentControllerBase::compressAttachment); connect(model, &MessageComposer::AttachmentModel::encryptEnabled, this, &AttachmentControllerBase::setEncryptEnabled); connect(model, &MessageComposer::AttachmentModel::signEnabled, this, &AttachmentControllerBase::setSignEnabled); d->wParent = wParent; d->mActionCollection = actionCollection; } AttachmentControllerBase::~AttachmentControllerBase() = default; void AttachmentControllerBase::createActions() { // Create the actions. d->attachPublicKeyAction = new QAction(i18n("Attach &Public Key..."), this); connect(d->attachPublicKeyAction, &QAction::triggered, this, &AttachmentControllerBase::showAttachPublicKeyDialog); d->attachMyPublicKeyAction = new QAction(i18n("Attach &My Public Key"), this); connect(d->attachMyPublicKeyAction, &QAction::triggered, this, &AttachmentControllerBase::attachMyPublicKey); d->attachmentMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Attach"), this); connect(d->attachmentMenu, &QAction::triggered, this, &AttachmentControllerBase::showAddAttachmentFileDialog); d->attachmentMenu->setPopupMode(QToolButton::DelayedPopup); d->addAttachmentFileAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("&Attach File..."), this); d->addAttachmentFileAction->setIconText(i18n("Attach")); d->addContextAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Add Attachment..."), this); connect(d->addAttachmentFileAction, &QAction::triggered, this, &AttachmentControllerBase::showAddAttachmentFileDialog); connect(d->addContextAction, &QAction::triggered, this, &AttachmentControllerBase::showAddAttachmentFileDialog); d->addAttachmentDirectoryAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("&Attach Directory..."), this); d->addAttachmentDirectoryAction->setIconText(i18n("Attach")); connect(d->addAttachmentDirectoryAction, &QAction::triggered, this, &AttachmentControllerBase::showAddAttachmentCompressedDirectoryDialog); d->attachClipBoardAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("&Attach Text From Clipboard..."), this); d->attachClipBoardAction->setIconText(i18n("Attach Text From Clipboard")); connect(d->attachClipBoardAction, &QAction::triggered, this, &AttachmentControllerBase::showAttachClipBoard); d->attachmentMenu->addAction(d->addAttachmentFileAction); d->attachmentMenu->addAction(d->addAttachmentDirectoryAction); d->attachmentMenu->addSeparator(); d->attachmentMenu->addAction(d->attachClipBoardAction); d->removeAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("&Remove Attachment"), this); d->removeContextAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Remove"), this); // FIXME need two texts. is there a better way? connect(d->removeAction, &QAction::triggered, this, [this]() { d->removeSelectedAttachments(); }); connect(d->removeContextAction, &QAction::triggered, this, [this]() { d->removeSelectedAttachments(); }); d->openContextAction = new QAction(i18nc("to open", "Open"), this); connect(d->openContextAction, &QAction::triggered, this, [this]() { d->openSelectedAttachments(); }); d->viewContextAction = new QAction(i18nc("to view", "View"), this); connect(d->viewContextAction, &QAction::triggered, this, [this]() { d->viewSelectedAttachments(); }); - d->editContextAction = new QAction(i18nc("to edit", "Edit"), this); - connect(d->editContextAction, &QAction::triggered, this, [this]() { - d->editSelectedAttachment(); - }); - - d->editWithContextAction = new QAction(i18n("Edit With..."), this); - connect(d->editWithContextAction, &QAction::triggered, this, [this]() { - d->editSelectedAttachmentWith(); - }); - d->saveAsAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("&Save Attachment As..."), this); d->saveAsContextAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As..."), this); connect(d->saveAsAction, &QAction::triggered, this, [this]() { d->saveSelectedAttachmentAs(); }); connect(d->saveAsContextAction, &QAction::triggered, this, [this]() { d->saveSelectedAttachmentAs(); }); d->propertiesAction = new QAction(i18n("Attachment Pr&operties..."), this); d->propertiesContextAction = new QAction(i18n("Properties"), this); connect(d->propertiesAction, &QAction::triggered, this, [this]() { d->selectedAttachmentProperties(); }); connect(d->propertiesContextAction, &QAction::triggered, this, [this]() { d->selectedAttachmentProperties(); }); d->selectAllAction = new QAction(i18n("Select All"), this); connect(d->selectAllAction, &QAction::triggered, this, &AttachmentControllerBase::selectedAllAttachment); d->reloadAttachmentAction = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reload"), this); connect(d->reloadAttachmentAction, &QAction::triggered, this, [this]() { d->reloadAttachment(); }); // Insert the actions into the composer window's menu. KActionCollection *collection = d->mActionCollection; collection->addAction(QStringLiteral("attach_public_key"), d->attachPublicKeyAction); collection->addAction(QStringLiteral("attach_my_public_key"), d->attachMyPublicKeyAction); collection->addAction(QStringLiteral("attach"), d->addAttachmentFileAction); collection->setDefaultShortcut(d->addAttachmentFileAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_A)); collection->addAction(QStringLiteral("attach_directory"), d->addAttachmentDirectoryAction); collection->addAction(QStringLiteral("remove"), d->removeAction); collection->addAction(QStringLiteral("attach_save"), d->saveAsAction); collection->addAction(QStringLiteral("attach_properties"), d->propertiesAction); collection->addAction(QStringLiteral("select_all_attachment"), d->selectAllAction); collection->addAction(QStringLiteral("attach_menu"), d->attachmentMenu); setSelectedParts(AttachmentPart::List()); Q_EMIT actionsCreated(); } void AttachmentControllerBase::setEncryptEnabled(bool enabled) { d->encryptEnabled = enabled; } void AttachmentControllerBase::setSignEnabled(bool enabled) { d->signEnabled = enabled; } void AttachmentControllerBase::compressAttachment(const AttachmentPart::Ptr &part, bool compress) { if (compress) { qCDebug(EDITOR_LOG) << "Compressing part."; auto ajob = new AttachmentCompressJob(part, this); connect(ajob, &AttachmentCompressJob::result, this, [this](KJob *job) { d->compressJobResult(job); }); ajob->start(); } else { qCDebug(EDITOR_LOG) << "Uncompressing part."; // Replace the compressed part with the original uncompressed part, and delete // the compressed part. AttachmentPart::Ptr originalPart = d->uncompressedParts.take(part); Q_ASSERT(originalPart); // Found in uncompressedParts. bool ok = d->model->replaceAttachment(part, originalPart); Q_ASSERT(ok); Q_UNUSED(ok) } } void AttachmentControllerBase::showContextMenu() { Q_EMIT refreshSelection(); const int numberOfParts(d->selectedParts.count()); QMenu menu; - const bool enableEditAction = (numberOfParts == 1) && (!d->selectedParts.first()->isMessageOrMessageCollection()); - if (numberOfParts > 0) { if (numberOfParts == 1) { const QString mimetype = QString::fromLatin1(d->selectedParts.first()->mimeType()); QMimeDatabase mimeDb; auto mime = mimeDb.mimeTypeForName(mimetype); QStringList parentMimeType; if (mime.isValid()) { parentMimeType = mime.allAncestors(); } if ((mimetype == QLatin1String("text/plain")) || (mimetype == QLatin1String("image/png")) || (mimetype == QLatin1String("image/jpeg")) || parentMimeType.contains(QLatin1String("text/plain")) || parentMimeType.contains(QLatin1String("image/png")) || parentMimeType.contains(QLatin1String("image/jpeg"))) { menu.addAction(d->viewContextAction); } d->createOpenWithMenu(&menu, d->selectedParts.constFirst()); } menu.addAction(d->openContextAction); } - if (enableEditAction) { - menu.addAction(d->editWithContextAction); - menu.addAction(d->editContextAction); - } menu.addSeparator(); if (numberOfParts == 1) { if (!d->selectedParts.first()->url().isEmpty()) { menu.addAction(d->reloadAttachmentAction); } menu.addAction(d->saveAsContextAction); menu.addSeparator(); menu.addAction(d->propertiesContextAction); menu.addSeparator(); } if (numberOfParts > 0) { menu.addAction(d->removeContextAction); menu.addSeparator(); } const int nbAttachment = d->model->rowCount(); if (nbAttachment != numberOfParts) { menu.addAction(d->selectAllAction); menu.addSeparator(); } if (numberOfParts == 0) { menu.addAction(d->addContextAction); } menu.exec(QCursor::pos()); } void AttachmentControllerBase::slotOpenWithDialog() { openWith(); } void AttachmentControllerBase::slotOpenWithAction(QAction *act) { auto app = act->data().value(); Q_ASSERT(d->selectedParts.count() == 1); openWith(app); } void AttachmentControllerBase::openWith(const KService::Ptr &offer) { QTemporaryFile *tempFile = dumpAttachmentToTempFile(d->selectedParts.constFirst()); if (!tempFile) { KMessageBox::error(d->wParent, i18n("KMail was unable to write the attachment to a temporary file."), i18nc("@title:window", "Unable to open attachment")); return; } QUrl url = QUrl::fromLocalFile(tempFile->fileName()); tempFile->setPermissions(QFile::ReadUser); // If offer is null, this will show the "open with" dialog auto job = new KIO::ApplicationLauncherJob(offer); job->setUrls({url}); job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, d->wParent)); job->start(); connect(job, &KJob::result, this, [tempFile, job]() { if (job->error()) { delete tempFile; } }); // Delete the file only when the composer is closed // (and this object is destroyed). tempFile->setParent(this); // Manages lifetime. } void AttachmentControllerBase::openAttachment(const AttachmentPart::Ptr &part) { QTemporaryFile *tempFile = dumpAttachmentToTempFile(part); if (!tempFile) { KMessageBox::error(d->wParent, i18n("KMail was unable to write the attachment to a temporary file."), i18nc("@title:window", "Unable to open attachment")); return; } tempFile->setPermissions(QFile::ReadUser); auto job = new KIO::OpenUrlJob(QUrl::fromLocalFile(tempFile->fileName()), QString::fromLatin1(part->mimeType())); job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, d->wParent)); job->setDeleteTemporaryFile(true); connect(job, &KIO::OpenUrlJob::result, this, [this, tempFile](KJob *job) { if (job->error() == KIO::ERR_USER_CANCELED) { KMessageBox::error(d->wParent, i18n("KMail was unable to open the attachment."), job->errorString()); delete tempFile; } else { // The file was opened. Delete it only when the composer is closed // (and this object is destroyed). tempFile->setParent(this); // Manages lifetime. } }); job->start(); } void AttachmentControllerBase::viewAttachment(const AttachmentPart::Ptr &part) { auto composer = new MessageComposer::Composer; composer->globalPart()->setFallbackCharsetEnabled(true); auto attachmentJob = new MessageComposer::AttachmentJob(part, composer); connect(attachmentJob, &AttachmentJob::result, this, [this](KJob *job) { d->slotAttachmentContentCreated(job); }); attachmentJob->start(); } void AttachmentControllerBase::AttachmentControllerBasePrivate::slotAttachmentContentCreated(KJob *job) { if (!job->error()) { const MessageComposer::AttachmentJob *const attachmentJob = qobject_cast(job); Q_ASSERT(attachmentJob); if (attachmentJob) { Q_EMIT q->showAttachment(attachmentJob->content(), QByteArray()); } } else { // TODO: show warning to the user qCWarning(EDITOR_LOG) << "Error creating KMime::Content for attachment:" << job->errorText(); } } -void AttachmentControllerBase::editAttachment(AttachmentPart::Ptr part, MessageComposer::EditorWatcher::OpenWithOption openWithOption) -{ - QTemporaryFile *tempFile = dumpAttachmentToTempFile(part); - if (!tempFile) { - KMessageBox::error(d->wParent, - i18n("KMail was unable to write the attachment to a temporary file."), - i18nc("@title:window", "Unable to edit attachment")); - return; - } - - auto watcher = - new MessageComposer::EditorWatcher(QUrl::fromLocalFile(tempFile->fileName()), QString::fromLatin1(part->mimeType()), openWithOption, this, d->wParent); - connect(watcher, &MessageComposer::EditorWatcher::editDone, this, [this](MessageComposer::EditorWatcher *watcher) { - d->editDone(watcher); - }); - - switch (watcher->start()) { - case MessageComposer::EditorWatcher::NoError: - // The attachment is being edited. - // We will clean things up in editDone(). - d->editorPart[watcher] = part; - d->editorTempFile[watcher] = tempFile; - - // Delete the temp file if the composer is closed (and this object is destroyed). - tempFile->setParent(this); // Manages lifetime. - break; - case MessageComposer::EditorWatcher::CannotStart: - qCWarning(EDITOR_LOG) << "Could not start EditorWatcher."; - [[fallthrough]]; - case MessageComposer::EditorWatcher::Unknown: - case MessageComposer::EditorWatcher::Canceled: - case MessageComposer::EditorWatcher::NoServiceFound: - delete watcher; - delete tempFile; - break; - } -} - -void AttachmentControllerBase::editAttachmentWith(const AttachmentPart::Ptr &part) -{ - editAttachment(part, MessageComposer::EditorWatcher::OpenWithDialog); -} - void AttachmentControllerBase::saveAttachmentAs(const AttachmentPart::Ptr &part) { QString pname = part->name(); if (pname.isEmpty()) { pname = i18n("unnamed"); } const QUrl url = QFileDialog::getSaveFileUrl(d->wParent, i18n("Save Attachment As"), QUrl::fromLocalFile(pname)); if (url.isEmpty()) { qCDebug(EDITOR_LOG) << "Save Attachment As dialog canceled."; return; } byteArrayToRemoteFile(part->data(), url); } void AttachmentControllerBase::byteArrayToRemoteFile(const QByteArray &aData, const QUrl &aURL, bool overwrite) { KIO::StoredTransferJob *job = KIO::storedPut(aData, aURL, -1, overwrite ? KIO::Overwrite : KIO::DefaultFlags); connect(job, &KIO::StoredTransferJob::result, this, &AttachmentControllerBase::slotPutResult); } void AttachmentControllerBase::slotPutResult(KJob *job) { auto _job = qobject_cast(job); if (job->error()) { if (job->error() == KIO::ERR_FILE_ALREADY_EXIST) { if (KMessageBox::warningContinueCancel(nullptr, i18n("File %1 exists.\nDo you want to replace it?", _job->url().toLocalFile()), i18nc("@title:window", "Save to File"), KGuiItem(i18n("&Replace"))) == KMessageBox::Continue) { byteArrayToRemoteFile(_job->data(), _job->url(), true); } } else { KJobUiDelegate *ui = static_cast(job)->uiDelegate(); ui->showErrorMessage(); } } } void AttachmentControllerBase::attachmentProperties(const AttachmentPart::Ptr &part) { QPointer dialog = new AttachmentPropertiesDialog(part, false, d->wParent); dialog->setEncryptEnabled(d->encryptEnabled); dialog->setSignEnabled(d->signEnabled); if (dialog->exec() && dialog) { d->model->updateAttachment(part); } delete dialog; } void AttachmentControllerBase::attachDirectory(const QUrl &url) { const int rc = KMessageBox::warningTwoActions(d->wParent, i18n("Do you really want to attach this directory \"%1\"?", url.toLocalFile()), i18nc("@title:window", "Attach directory"), KGuiItem(i18nc("@action:button", "Attach")), KStandardGuiItem::cancel()); if (rc == KMessageBox::ButtonCode::PrimaryAction) { addAttachment(url); } } void AttachmentControllerBase::showAttachClipBoard() { auto job = new MessageComposer::AttachmentClipBoardJob(this); connect(job, &AttachmentClipBoardJob::result, this, [this](KJob *job) { d->attachClipBoardElement(job); }); job->start(); } void AttachmentControllerBase::showAddAttachmentCompressedDirectoryDialog() { const QUrl url = QFileDialog::getExistingDirectoryUrl(d->wParent, i18nc("@title:window", "Attach Directory")); if (url.isValid()) { attachDirectory(url); } } static QString fixEncoding(const QString &encoding) { QString returnEncoding = encoding; // According to https://www.iana.org/assignments/character-sets, uppercase is // preferred in MIME headers const QString returnEncodingToUpper = returnEncoding.toUpper(); if (returnEncodingToUpper.contains(QLatin1String("ISO "))) { returnEncoding = returnEncodingToUpper; returnEncoding.replace(QLatin1String("ISO "), QStringLiteral("ISO-")); } return returnEncoding; } void AttachmentControllerBase::showAddAttachmentFileDialog() { const KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenUrlsAndEncoding(QString(), QUrl(), QString(), d->wParent, i18nc("@title:window", "Attach File")); if (!result.URLs.isEmpty()) { const QString encoding = fixEncoding(result.encoding); const int numberOfFiles(result.URLs.count()); for (int i = 0; i < numberOfFiles; ++i) { const QUrl url = result.URLs.at(i); QUrl urlWithEncoding = url; QUrlQuery query; query.addQueryItem(QStringLiteral("charset"), encoding); urlWithEncoding.setQuery(query); QMimeDatabase mimeDb; const auto mimeType = mimeDb.mimeTypeForUrl(urlWithEncoding); if (mimeType.name() == QLatin1String("inode/directory")) { const int rc = KMessageBox::warningTwoActions(d->wParent, i18n("Do you really want to attach this directory \"%1\"?", url.toLocalFile()), i18nc("@title:window", "Attach directory"), KGuiItem(i18nc("@action:button", "Attach")), KStandardGuiItem::cancel()); if (rc == KMessageBox::ButtonCode::PrimaryAction) { addAttachment(urlWithEncoding); } } else { addAttachment(urlWithEncoding); } } } } void AttachmentControllerBase::addAttachment(const AttachmentPart::Ptr &part) { part->setEncrypted(d->model->isEncryptSelected()); part->setSigned(d->model->isSignSelected()); d->model->addAttachment(part); Q_EMIT fileAttached(); } void AttachmentControllerBase::addAttachmentUrlSync(const QUrl &url) { MessageCore::AttachmentFromUrlBaseJob *ajob = MessageCore::AttachmentFromUrlUtils::createAttachmentJob(url, this); if (ajob->exec()) { AttachmentPart::Ptr part = ajob->attachmentPart(); addAttachment(part); } else { if (ajob->error()) { KMessageBox::error(d->wParent, ajob->errorString(), i18nc("@title:window", "Failed to attach file")); } } } void AttachmentControllerBase::addAttachment(const QUrl &url) { MessageCore::AttachmentFromUrlBaseJob *ajob = MessageCore::AttachmentFromUrlUtils::createAttachmentJob(url, this); connect(ajob, &AttachmentFromUrlBaseJob::result, this, [this](KJob *job) { d->loadJobResult(job); }); ajob->start(); } void AttachmentControllerBase::addAttachments(const QList &urls) { for (const QUrl &url : urls) { addAttachment(url); } } void AttachmentControllerBase::showAttachPublicKeyDialog() { using Kleo::KeySelectionDialog; QPointer dialog = new KeySelectionDialog(i18n("Attach Public OpenPGP Key"), i18n("Select the public key which should be attached."), std::vector(), KeySelectionDialog::PublicKeys | KeySelectionDialog::OpenPGPKeys, false /* no multi selection */, false /* no remember choice box */, d->wParent); if (dialog->exec() == QDialog::Accepted) { exportPublicKey(dialog->selectedKey()); } delete dialog; } void AttachmentControllerBase::attachMyPublicKey() { } void AttachmentControllerBase::enableAttachPublicKey(bool enable) { d->attachPublicKeyAction->setEnabled(enable); } void AttachmentControllerBase::enableAttachMyPublicKey(bool enable) { d->attachMyPublicKeyAction->setEnabled(enable); } void AttachmentControllerBase::clear() { const auto parts = d->model->attachments(); for (const auto &attachmentPart : parts) { if (!d->model->removeAttachment(attachmentPart)) { qCWarning(EDITOR_LOG) << "Impossible to remove attachment" << attachmentPart->fileName(); } } } #include "moc_attachmentcontrollerbase.cpp" diff --git a/server/editor/attachment/attachmentcontrollerbase.h b/server/editor/attachment/attachmentcontrollerbase.h index d1e82e0..ebb971e 100644 --- a/server/editor/attachment/attachmentcontrollerbase.h +++ b/server/editor/attachment/attachmentcontrollerbase.h @@ -1,95 +1,90 @@ /* * This file is part of KMail. * SPDX-FileCopyrightText: 2009 Constantin Berzan * * Parts based on KMail code by: * Various authors. * * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once -#include "editorwatcher.h" - #include #include #include #include #include "attachmentpart.h" class KActionCollection; class QAction; class KJob; namespace MessageComposer { class AttachmentModel; /** * @brief The AttachmentControllerBase class */ class AttachmentControllerBase : public QObject { Q_OBJECT public: AttachmentControllerBase(MessageComposer::AttachmentModel *model, QWidget *wParent, KActionCollection *actionCollection); ~AttachmentControllerBase() override; void createActions(); // TODO dnd stuff... void setSelectedParts(const MessageCore::AttachmentPart::List &selectedParts); public Q_SLOTS: /// model sets these void setEncryptEnabled(bool enabled); void setSignEnabled(bool enabled); /// compression is async... void compressAttachment(const MessageCore::AttachmentPart::Ptr &part, bool compress); void showContextMenu(); void openAttachment(const MessageCore::AttachmentPart::Ptr &part); void viewAttachment(const MessageCore::AttachmentPart::Ptr &part); - void editAttachment(MessageCore::AttachmentPart::Ptr part, - MessageComposer::EditorWatcher::OpenWithOption option = MessageComposer::EditorWatcher::NoOpenWithDialog); - void editAttachmentWith(const MessageCore::AttachmentPart::Ptr &part); void saveAttachmentAs(const MessageCore::AttachmentPart::Ptr &part); void attachmentProperties(const MessageCore::AttachmentPart::Ptr &part); void showAddAttachmentFileDialog(); void showAddAttachmentCompressedDirectoryDialog(); /// sets sign, encrypt, shows properties dialog if so configured void addAttachment(const MessageCore::AttachmentPart::Ptr &part); void addAttachment(const QUrl &url); void addAttachmentUrlSync(const QUrl &url); void addAttachments(const QList &urls); void showAttachPublicKeyDialog(); void showAttachClipBoard(); virtual void attachMyPublicKey(); void clear(); Q_SIGNALS: void actionsCreated(); void refreshSelection(); void showAttachment(KMime::Content *content, const QByteArray &charset); void selectedAllAttachment(); void fileAttached(); protected: void exportPublicKey(const GpgME::Key &key); void enableAttachPublicKey(bool enable); void enableAttachMyPublicKey(bool enable); void byteArrayToRemoteFile(const QByteArray &aData, const QUrl &aURL, bool overwrite = false); void openWith(const KService::Ptr &offer = KService::Ptr()); private: void attachDirectory(const QUrl &url); void slotPutResult(KJob *job); void slotOpenWithDialog(); void slotOpenWithAction(QAction *act); private: class AttachmentControllerBasePrivate; std::unique_ptr const d; }; } //