Signed-off-by: Jeremy White jwhite@codeweavers.com --- This addresses https://bugs.winehq.org/show_bug.cgi?id=11770 for systems with xdg-email. --- dlls/winemapi/Makefile.in | 3 +- dlls/winemapi/sendmail.c | 45 +++++--- dlls/winemapi/winemapi_private.h | 28 +++++ dlls/winemapi/xdg-email.c | 230 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 291 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 813ee42..d6f79b4 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 03a29ba..f7a63ca 100644 --- a/dlls/winemapi/sendmail.c +++ b/dlls/winemapi/sendmail.c @@ -36,6 +36,8 @@ #include "shlwapi.h" #include "wine/debug.h"
+#include "winemapi_private.h" + WINE_DEFAULT_DEBUG_CHANNEL(winemapi);
/* Escapes a string for use in mailto: URL */ @@ -70,23 +72,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; @@ -291,6 +282,32 @@ 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) +{ + 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 0000000..ac76ab3 --- /dev/null +++ b/dlls/winemapi/winemapi_private.h @@ -0,0 +1,28 @@ +/* + * + * 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 0000000..6008d6e --- /dev/null +++ b/dlls/winemapi/xdg-email.c @@ -0,0 +1,230 @@ +/* + * 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 <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winnls.h" +#include "mapi.h" +#include "wine/debug.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "winemapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winemapi); + +static void add_argument(char **argv, int *argc, const char *arg, const char *param) +{ + argv[(*argc)] = HeapAlloc(GetProcessHeap(), 0, strlen(arg) + 1); + strcpy(argv[(*argc)++], arg); + if (param) + { + argv[(*argc)] = HeapAlloc(GetProcessHeap(), 0, 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) +{ + WCHAR *fullname, *p; + char *unixpath; + int namelen = 1; + + if (path) + namelen += strlen(path) + 1; + if (file) + namelen += strlen(file); + + p = fullname = HeapAlloc(GetProcessHeap(), 0, namelen * sizeof(WCHAR)); + ZeroMemory(fullname, namelen * sizeof(WCHAR)); + if (path) + { + MultiByteToWideChar(CP_ACP, 0, path, -1, p, namelen); + p += namelen; + MultiByteToWideChar(CP_ACP, 0, "\", 1, p, 1); + p ++; + } + if (file) + MultiByteToWideChar(CP_ACP, 0, file, -1, p, namelen - (p - fullname)); + + unixpath = wine_get_unix_file_name(fullname); + if (!unixpath) + TRACE("Cannot find unix path of '%s'; not attaching.\n", debugstr_w(fullname)); + HeapFree(GetProcessHeap(), 0, fullname); + if (!unixpath) + return; + + add_argument(argv, argc, "--attach", unixpath); + HeapFree(GetProcessHeap(), 0, unixpath); +} + +/************************************************************************** + * 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 = HeapAlloc(GetProcessHeap(), 0, len + strlen("xdg-email") + 1 + 1); + memcpy(test, p, len); + strcpy(test + len, "/"); + strcat(test, "xdg-email"); + + rc = access(test, X_OK); + HeapFree(GetProcessHeap(), 0, 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 = HeapAlloc(GetProcessHeap(), 0, (max_args + 1) * sizeof(*argv)); + ZeroMemory(argv, (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: + HeapFree(GetProcessHeap(), 0, argv); + return ret; +} +
Hi Jeremy,
diff --git a/dlls/winemapi/Makefile.in b/dlls/winemapi/Makefile.in index 813ee42..d6f79b4 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
I think it would be better to put this in sendmail.c. There’s not enough code to warrant a new file and I don’t expect it to grow.
+/**************************************************************************
- 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)
+{
- if (XDGMailAvailable())
return XDGSendMail(session, uiparam, message, flags, reserved);
- return BrowserSendMail(session, uiparam, message, flags, reserved);
+}
It’s hard to reliably check for availability of any given command, and even if you can there’s still a race. You should just call _spawnvp and fall back to the existing path if it fails.
+static void add_argument(char **argv, int *argc, const char *arg, const char *param) +{
- argv[(*argc)] = HeapAlloc(GetProcessHeap(), 0, strlen(arg) + 1);
- strcpy(argv[(*argc)++], arg);
- if (param)
- {
argv[(*argc)] = HeapAlloc(GetProcessHeap(), 0, 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) +{
- WCHAR *fullname, *p;
- char *unixpath;
- int namelen = 1;
- if (path)
namelen += strlen(path) + 1;
- if (file)
namelen += strlen(file);
- p = fullname = HeapAlloc(GetProcessHeap(), 0, namelen * sizeof(WCHAR));
- ZeroMemory(fullname, namelen * sizeof(WCHAR));
You should check for allocation failure. memset is preferred over ZeroMemory.