From 57e066e3be063d122243daa149ef327c2594901a Mon Sep 17 00:00:00 2001 From: Jeremy White Date: Thu, 29 Mar 2018 11:58:25 +1100 Subject: [PATCH] winemapi: Directly use xdg-email if available, enabling file attachments. This addresses https://bugs.winehq.org/show_bug.cgi?id=11770 for systems with xdg-email. v2: Use new heap_* function. Signed-off-by: Jeremy White --- dlls/winemapi/Makefile.in | 3 +- dlls/winemapi/sendmail.c | 48 ++++-- dlls/winemapi/winemapi_private.h | 27 ++++ dlls/winemapi/xdg-email.c | 246 +++++++++++++++++++++++++++++++ 4 files changed, 309 insertions(+), 15 deletions(-) create mode 100644 dlls/winemapi/winemapi_private.h create mode 100644 dlls/winemapi/xdg-email.c diff --git a/dlls/winemapi/Makefile.in b/dlls/winemapi/Makefile.in index 813ee42b349..d6f79b403da 100644 --- a/dlls/winemapi/Makefile.in +++ b/dlls/winemapi/Makefile.in @@ -3,4 +3,5 @@ IMPORTS = shlwapi shell32 mapi32 C_SRCS = \ main.c \ - sendmail.c + sendmail.c \ + xdg-email.c diff --git a/dlls/winemapi/sendmail.c b/dlls/winemapi/sendmail.c index cfb27e55c95..d5625686e9e 100644 --- a/dlls/winemapi/sendmail.c +++ b/dlls/winemapi/sendmail.c @@ -37,6 +37,8 @@ #include "winternl.h" #include "wine/debug.h" +#include "winemapi_private.h" + WINE_DEFAULT_DEBUG_CHANNEL(winemapi); /* Escapes a string for use in mailto: URL */ @@ -71,23 +73,12 @@ static char *escape_string(char *in, char *empty_string) } /************************************************************************** - * MAPISendMail + * BrowserSendMail * - * Send a message using a native mail client. - * - * PARAMS - * session [I] Handle to a MAPI session. - * uiparam [I] Parent window handle. - * message [I] Pointer to a MAPIMessage structure. - * flags [I] Flags. - * reserved [I] Reserved, pass 0. - * - * RETURNS - * Success: SUCCESS_SUCCESS - * Failure: MAPI_E_FAILURE + * Send an email by forming a mailto uri and invoking a browser. * */ -ULONG WINAPI MAPISendMail(LHANDLE session, ULONG_PTR uiparam, +static ULONG BrowserSendMail(LHANDLE session, ULONG_PTR uiparam, lpMapiMessage message, FLAGS flags, ULONG reserved) { ULONG ret = MAPI_E_FAILURE; @@ -292,6 +283,35 @@ exit: return ret; } +/************************************************************************** + * MAPISendMail + * + * Send a message using a native mail client. + * + * PARAMS + * session [I] Handle to a MAPI session. + * uiparam [I] Parent window handle. + * message [I] Pointer to a MAPIMessage structure. + * flags [I] Flags. + * reserved [I] Reserved, pass 0. + * + * RETURNS + * Success: SUCCESS_SUCCESS + * Failure: MAPI_E_FAILURE + * + */ +ULONG WINAPI MAPISendMail(LHANDLE session, ULONG_PTR uiparam, + lpMapiMessage message, FLAGS flags, ULONG reserved) +{ + TRACE("(0x%08lx 0x%08lx %p 0x%08x 0x%08x)\n", session, uiparam, + message, flags, reserved); + + if (XDGMailAvailable()) + return XDGSendMail(session, uiparam, message, flags, reserved); + + return BrowserSendMail(session, uiparam, message, flags, reserved); +} + ULONG WINAPI MAPISendDocuments(ULONG_PTR uiparam, LPSTR delim, LPSTR paths, LPSTR filenames, ULONG reserved) { diff --git a/dlls/winemapi/winemapi_private.h b/dlls/winemapi/winemapi_private.h new file mode 100644 index 00000000000..a8bb8c5e075 --- /dev/null +++ b/dlls/winemapi/winemapi_private.h @@ -0,0 +1,27 @@ +/* + * + * Copyright 2016 Jeremy White + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINEMAPI_PRIVATE_INCLUDED__ +#define __WINEMAPI_PRIVATE_INCLUDED__ + +ULONG XDGSendMail(LHANDLE session, ULONG_PTR uiparam, + lpMapiMessage message, FLAGS flags, ULONG reserved); +BOOLEAN XDGMailAvailable(void); + +#endif /* __WINEMAPI_PRIVATE_INCLUDED__ */ diff --git a/dlls/winemapi/xdg-email.c b/dlls/winemapi/xdg-email.c new file mode 100644 index 00000000000..6b420663a26 --- /dev/null +++ b/dlls/winemapi/xdg-email.c @@ -0,0 +1,246 @@ +/* + * MAPISendMail implementation for xdg-email + * + * Copyright 2016 Jeremy White for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winnls.h" +#include "mapi.h" +#include "wine/heap.h" +#include "wine/debug.h" +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "winemapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winemapi); + +static inline WCHAR *heap_strdupAtoW(const char *str) +{ + LPWSTR ret = NULL; + + if(str) { + DWORD len; + + len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + ret = heap_alloc(len*sizeof(WCHAR)); + if(ret) + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + } + + return ret; +} + +static void add_argument(char **argv, int *argc, const char *arg, const char *param) +{ + argv[(*argc)] = heap_alloc(strlen(arg) + 1); + strcpy(argv[(*argc)++], arg); + if (param) + { + argv[(*argc)] = heap_alloc(strlen(param) + 1); + strcpy(argv[(*argc)++], param); + } +} + +static void add_target(char **argv, int *argc, ULONG class, const char *address) +{ + static const char smtp[] = "smtp:"; + + if (!strncasecmp(address, smtp, sizeof(smtp) - 1)) + address += sizeof(smtp) - 1; + + switch (class) + { + case MAPI_ORIG: + TRACE("From: %s\n (unused)", debugstr_a(address)); + break; + + case MAPI_TO: + TRACE("To: %s\n", debugstr_a(address)); + add_argument(argv, argc, address, NULL); + break; + + case MAPI_CC: + TRACE("CC: %s\n", debugstr_a(address)); + add_argument(argv, argc, "--cc", address); + break; + + case MAPI_BCC: + TRACE("BCC: %s\n", debugstr_a(address)); + add_argument(argv, argc, "--bcc", address); + break; + + default: + TRACE("Unknown recipient class: %d\n", class); + } +} + +static void add_file(char **argv, int *argc, const char *path, const char *file) +{ + char fullname[MAX_PATH] = {0}; + WCHAR *fullnameW; + char *unixpath; + + if (path) + { + strcpy(fullname, path); + strcat(fullname, "\\"); + } + if (file) + strcat(fullname, file); + + fullnameW = heap_strdupAtoW(fullname); + if(!fullnameW) + { + ERR("Out of memory\n"); + return; + } + + unixpath = wine_get_unix_file_name(fullnameW); + if (unixpath) + { + add_argument(argv, argc, "--attach", unixpath); + heap_free(unixpath); + } + else + ERR("Cannot find unix path of '%s'; not attaching.\n", debugstr_w(fullnameW)); + + heap_free(fullnameW); +} + +/************************************************************************** + * XDGIsAvailable + * + */ +BOOLEAN XDGMailAvailable(void) +{ +#ifdef HAVE_UNISTD_H + char *p, *q, *test; + int len; + int rc; + + for (p = getenv("PATH"); p; p = q) + { + while (*p == ':') + p++; + + if (!*p) + break; + + q = strchr(p, ':'); + len = q ? q - p : strlen(p); + + test = heap_alloc(len + strlen("xdg-email") + 2); /* '/' + NULL */ + if(!test) + break; + memcpy(test, p, len); + strcpy(test + len, "/"); + strcat(test, "xdg-email"); + + rc = access(test, X_OK); + heap_free(test); + + if (rc == 0) + return TRUE; + } +#endif + return FALSE; +} + +/************************************************************************** + * XDGSendMail + * + * Send a message using xdg-email mail client. + * + */ +ULONG XDGSendMail(LHANDLE session, ULONG_PTR uiparam, + lpMapiMessage message, FLAGS flags, ULONG reserved) +{ + int i; + int argc = 0; + int max_args; + char **argv = NULL; + ULONG ret = MAPI_E_FAILURE; + + TRACE("(0x%08lx 0x%08lx %p 0x%08x 0x%08x)\n", session, uiparam, message, flags, reserved); + + if (!message) + return MAPI_E_FAILURE; + + max_args = 1 + (2 + message->nRecipCount + message->nFileCount) * 2; + argv = heap_alloc_zero( (max_args + 1) * sizeof(*argv)); + + add_argument(argv, &argc, "xdg-email", NULL); + + if (message->lpOriginator) + TRACE("From: %s (unused)\n", debugstr_a(message->lpOriginator->lpszAddress)); + + for (i = 0; i < message->nRecipCount; i++) + { + if (!message->lpRecips) + { + WARN("Recipient %d missing\n", i); + goto exit; + } + + if (message->lpRecips[i].lpszAddress) + add_target(argv, &argc, message->lpRecips[i].ulRecipClass, + message->lpRecips[i].lpszAddress); + else + FIXME("Name resolution and entry identifiers not supported\n"); + } + + for (i = 0; i < message->nFileCount; i++) + { + TRACE("File Path: %s, name %s\n", debugstr_a(message->lpFiles[i].lpszPathName), + debugstr_a(message->lpFiles[i].lpszFileName)); + add_file(argv, &argc, message->lpFiles[i].lpszPathName, message->lpFiles[i].lpszFileName); + } + + if (message->lpszSubject) + { + TRACE("Subject: %s\n", debugstr_a(message->lpszSubject)); + add_argument(argv, &argc, "--subject", message->lpszSubject); + } + + if (message->lpszNoteText) + { + TRACE("Body: %s\n", debugstr_a(message->lpszNoteText)); + add_argument(argv, &argc, "--body", message->lpszNoteText); + } + + TRACE("Executing xdg-email; parameters:\n"); + for (i = 0; argv[i] && i <= max_args; i++) + TRACE(" %d: [%s]\n", i, argv[i]); + if (_spawnvp(_P_WAIT, "xdg-email", (const char **) argv) == 0) + ret = SUCCESS_SUCCESS; + +exit: + heap_free(argv); + return ret; +} -- 2.20.1