From 0ae9f7c32cf873611ce5ed7f341f5979690f2318 Mon Sep 17 00:00:00 2001
From: Jeremy White <jwhite@codeweavers.com>
Date: Thu, 29 Mar 2018 11:58:25 +1100
Subject: [PATCH] winemapi: Directly use xdg-email if available, enabling file
 attachments.

v2:
Use new heap_* function.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=11770

Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 dlls/winemapi/Makefile.in        |   3 +-
 dlls/winemapi/sendmail.c         |  48 ++++--
 dlls/winemapi/winemapi_private.h |  27 ++++
 dlls/winemapi/xdg-email.c        | 241 +++++++++++++++++++++++++++++++
 4 files changed, 304 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 84ff227e1cb..4fd83830aea 100644
--- a/dlls/winemapi/Makefile.in
+++ b/dlls/winemapi/Makefile.in
@@ -5,4 +5,5 @@ EXTRADLLFLAGS = -mno-cygwin
 
 C_SRCS = \
 	main.c \
-	sendmail.c
+	sendmail.c \
+	xdg-email.c
diff --git a/dlls/winemapi/sendmail.c b/dlls/winemapi/sendmail.c
index ca1b6c59df1..e288837a33d 100644
--- a/dlls/winemapi/sendmail.c
+++ b/dlls/winemapi/sendmail.c
@@ -34,6 +34,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 */
@@ -68,23 +70,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;
@@ -289,6 +280,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..08a9b49cc7f
--- /dev/null
+++ b/dlls/winemapi/xdg-email.c
@@ -0,0 +1,241 @@
+/*
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnls.h"
+#include "mapi.h"
+#include "process.h"
+#include "wine/heap.h"
+#include "wine/debug.h"
+
+#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.17.1

