diff --git a/client/Application.cpp b/client/Application.cpp index 66b4107d5..b0a2448ca 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -77,9 +77,7 @@ class MacMenuBar {}; #include #ifdef Q_OS_WIN32 -#include #include -#include #endif using namespace std::chrono; @@ -316,6 +314,7 @@ Application::Application( int &argc, char **argv ) , d(new Private) { setApplicationName(QStringLiteral("qdigidoc4")); + setApplicationDisplayName(tr("DigiDoc4 Client")); setApplicationVersion(QStringLiteral(VERSION_STR)); setDesktopFileName("ee.ria.qdigidoc4"); setOrganizationDomain(QStringLiteral("ria.ee")); @@ -686,36 +685,10 @@ void Application::loadTranslation( const QString &lang ) if(d->helpAction) d->helpAction->setText(tr("DigiDoc4 Client Help")); } -#ifndef Q_OS_MAC +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) void Application::mailTo( const QUrl &url ) { QUrlQuery q(url); -#if defined(Q_OS_WIN) - if(QLibrary lib("mapi32"); auto mapi = LPMAPISENDMAILW(lib.resolve("MAPISendMailW"))) - { - QString file = q.queryItemValue( "attachment", QUrl::FullyDecoded ); - QString filePath = QDir::toNativeSeparators( file ); - QString fileName = QFileInfo( file ).fileName(); - QString subject = q.queryItemValue( "subject", QUrl::FullyDecoded ); - MapiFileDescW doc {}; - doc.nPosition = -1; - doc.lpszPathName = PWSTR(filePath.utf16()); - doc.lpszFileName = PWSTR(fileName.utf16()); - MapiMessageW message {}; - message.lpszSubject = PWSTR(subject.utf16()); - message.lpszNoteText = PWSTR(L""); - message.nFileCount = 1; - message.lpFiles = lpMapiFileDescW(&doc); - switch( mapi( NULL, 0, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0 ) ) - { - case SUCCESS_SUCCESS: - case MAPI_E_USER_ABORT: - case MAPI_E_LOGIN_FAILURE: - return; - default: break; - } - } -#elif defined(Q_OS_UNIX) QByteArray thunderbird; QProcess p; QStringList env = QProcess::systemEnvironment(); @@ -764,7 +737,6 @@ void Application::mailTo( const QUrl &url ) } if( status ) return; -#endif QDesktopServices::openUrl( url ); } #endif diff --git a/client/Application_win.cpp b/client/Application_win.cpp new file mode 100644 index 000000000..55114293e --- /dev/null +++ b/client/Application_win.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Estonian Information System Authority +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "Application.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Qt::StringLiterals; + +namespace +{ +winrt::fire_and_forget showShareSheet(QUrl url, HWND window) try +{ + using namespace winrt::Windows::ApplicationModel::DataTransfer; + using namespace winrt::Windows::Storage; + + QUrlQuery query(url); + const QString path = QDir::toNativeSeparators( + query.queryItemValue(QStringLiteral("attachment"), QUrl::FullyDecoded)); + const QString subject = query.queryItemValue(QStringLiteral("subject"), QUrl::FullyDecoded); + auto file = co_await StorageFile::GetFileFromPathAsync(winrt::hstring(PWSTR(path.utf16()), uint32_t(path.size()))); + auto interop = winrt::get_activation_factory(); + constexpr winrt::guid dataTransferManagerId{ + 0xa5caee9b, 0x8708, 0x49d1, {0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c}}; + DataTransferManager manager{nullptr}; + winrt::check_hresult(interop->GetForWindow(window, dataTransferManagerId, winrt::put_abi(manager))); + manager.DataRequested([file = std::move(file), subject](DataTransferManager const &, DataRequestedEventArgs const &args) { + auto data = args.Request().Data(); + data.Properties().Title(winrt::hstring(PWSTR(subject.utf16()), uint32_t(subject.size()))); + data.Properties().Description(L"DigiDoc4 Client"); + data.SetStorageItems(winrt::single_threaded_vector({file})); + data.RequestedOperation(DataPackageOperation::Copy); + }); + winrt::check_hresult(interop->ShowShareUIForWindow(window)); +} +catch(...) +{ + if(auto *app = QCoreApplication::instance()) + QMetaObject::invokeMethod(app, + [url = std::move(url)] { QDesktopServices::openUrl(url); }, Qt::QueuedConnection); +} + +bool sendWithMapi(const QUrl &url) +{ + QSettings userClients(u"HKEY_CURRENT_USER\\Software\\Clients\\Mail"_s, QSettings::NativeFormat); + QSettings machineClients(u"HKEY_LOCAL_MACHINE\\Software\\Clients\\Mail"_s, QSettings::NativeFormat); + QString client = userClients.value(QStringLiteral("Default")).toString(); + if(client.isEmpty()) + client = machineClients.value(QStringLiteral("Default")).toString(); + if(client.isEmpty()) + return false; + auto hasProvider = [&client](QSettings &clients) { + clients.beginGroup(client); + return clients.contains(QStringLiteral("DLLPath")) + || clients.contains(QStringLiteral("MSIComponentID")); + }; + if(!hasProvider(userClients) && !hasProvider(machineClients)) + return false; + + QUrlQuery query(url); + QString file = query.queryItemValue(QStringLiteral("attachment"), QUrl::FullyDecoded); + QString filePath = QDir::toNativeSeparators(file); + QString fileName = QFileInfo(file).fileName(); + QString subject = query.queryItemValue(QStringLiteral("subject"), QUrl::FullyDecoded); + MapiFileDescW document { + .nPosition = 0, + .lpszPathName = PWSTR(filePath.utf16()), + .lpszFileName = PWSTR(fileName.utf16()), + }; + MapiMessageW message { + .lpszSubject = PWSTR(subject.utf16()), + .lpszNoteText = PWSTR(L""), + .nFileCount = 1, + .lpFiles = &document, + }; + if(QLibrary lib("mapi32"); auto mapi = LPMAPISENDMAILW(lib.resolve("MAPISendMailW"))) + { + switch(mapi({}, 0, &message, MAPI_LOGON_UI | MAPI_DIALOG, 0)) + { + case SUCCESS_SUCCESS: + case MAPI_E_USER_ABORT: + case MAPI_E_LOGIN_FAILURE: + return true; + default: return false; + } + } + return false; +} + +} + +void Application::mailTo(const QUrl &url) +{ + if(sendWithMapi(url)) + return; + if(auto *window = mainWindow()) + showShareSheet(url, reinterpret_cast(window->winId())); + else + QDesktopServices::openUrl(url); +} diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 475f57341..0f3ec4d37 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -81,6 +81,8 @@ add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE ) if(WIN32) set_source_files_properties(Diagnostics_unix.cpp PROPERTIES HEADER_FILE_ONLY TRUE) + target_sources(${PROJECT_NAME} PRIVATE Application_win.cpp) + target_link_libraries(${PROJECT_NAME} windowsapp) else() set_source_files_properties(Diagnostics_win.cpp PROPERTIES HEADER_FILE_ONLY TRUE) endif()