[Spice-devel,v4] winemapi: Directly use xdg-email if available, enabling file attachments.

Submitted by Jeremy White on Dec. 2, 2016, 9:13 p.m.

Details

Message ID 1480713231-30919-1-git-send-email-jwhite@codeweavers.com
State New
Headers show
Series "winemapi: Directly use xdg-email if available, enabling file attachments." ( rev: 1 ) in Spice

Not browsing as part of any series.

Commit Message

Jeremy White Dec. 2, 2016, 9:13 p.m.
Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
This addresses https://bugs.winehq.org/show_bug.cgi?id=11770 for systems with xdg-email.
v4: Tweaks suggested by Andre.
v3: Replace use of memset with more precise behavior, use return of MultiByteToWideChar
instead of strlen, make a helper function visibly atomic with respect to memory.
v2: Remove memory leak, check allocations, do not test for an executable xdg-email prior
to invocation, keep the changes in sendmail.c, and increment a pointer correctly when
combining file paths.
---
 dlls/winemapi/sendmail.c | 240 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 217 insertions(+), 23 deletions(-)

Patch hide | download patch | download mbox

diff --git a/dlls/winemapi/sendmail.c b/dlls/winemapi/sendmail.c
index 03a29ba..57df451 100644
--- a/dlls/winemapi/sendmail.c
+++ b/dlls/winemapi/sendmail.c
@@ -69,25 +69,103 @@  static char *escape_string(char *in, char *empty_string)
     return escaped ? escaped : empty_string;
 }
 
+static ULONG add_argument(char **argv, int *argc, const char *arg, const char *param)
+{
+    argv[(*argc)] = HeapAlloc(GetProcessHeap(), 0, strlen(arg) + 1);
+    if (!argv[(*argc)])
+        return MAPI_E_INSUFFICIENT_MEMORY;
+    strcpy(argv[(*argc)++], arg);
+
+    if (param)
+    {
+        argv[(*argc)] = HeapAlloc(GetProcessHeap(), 0, strlen(param) + 1);
+        if (!argv[(*argc)])
+        {
+            HeapFree(GetProcessHeap(), 0, argv[--(*argc)]);
+            return MAPI_E_INSUFFICIENT_MEMORY;
+        }
+        strcpy(argv[(*argc)++], param);
+    }
+
+    return SUCCESS_SUCCESS;
+}
+
+static ULONG 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));
+            return add_argument(argv, argc, address, NULL);
+
+        case MAPI_CC:
+            TRACE("CC: %s\n", debugstr_a(address));
+            return add_argument(argv, argc, "--cc", address);
+
+        case MAPI_BCC:
+            TRACE("BCC: %s\n", debugstr_a(address));
+            return add_argument(argv, argc, "--bcc", address);
+
+        default:
+            TRACE("Unknown recipient class: %d\n", class);
+    }
+
+    return SUCCESS_SUCCESS;
+}
+
+static ULONG add_file(char **argv, int *argc, const char *path, const char *file)
+{
+    WCHAR *fullname, *p;
+    char *unixpath;
+    int namelen = 1;
+    ULONG ret;
+
+    if (path)
+        namelen += strlen(path) + 1;
+    if (file)
+        namelen += strlen(file);
+
+    p = fullname = HeapAlloc(GetProcessHeap(), 0, namelen * sizeof(WCHAR));
+    if (!fullname)
+        return MAPI_E_INSUFFICIENT_MEMORY;
+    if (path)
+    {
+        p += MultiByteToWideChar(CP_ACP, 0, path, -1, p, namelen);
+        p += MultiByteToWideChar(CP_ACP, 0, "\\", 1, p, 1);
+    }
+    if (file)
+        p += MultiByteToWideChar(CP_ACP, 0, file, -1, p, namelen - (p - fullname));
+    *p = 0;
+
+    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 MAPI_E_FAILURE;
+
+    ret = add_argument(argv, argc, "--attach", unixpath);
+    HeapFree(GetProcessHeap(), 0, unixpath);
+
+    return ret;
+}
+
 /**************************************************************************
- *  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,
-    lpMapiMessage message, FLAGS flags, ULONG reserved)
+static ULONG BrowserSendMail(lpMapiMessage message)
 {
     ULONG ret = MAPI_E_FAILURE;
     unsigned int i, to_count = 0, cc_count = 0, bcc_count = 0;
@@ -103,12 +181,6 @@  ULONG WINAPI MAPISendMail(LHANDLE session, ULONG_PTR uiparam,
     HRESULT res;
     DWORD size;
 
-    TRACE("(0x%08lx 0x%08lx %p 0x%08x 0x%08x)\n", session, uiparam,
-           message, flags, reserved);
-
-    if (!message)
-        return MAPI_E_FAILURE;
-
     for (i = 0; i < message->nRecipCount; i++)
     {
         if (!message->lpRecips)
@@ -127,7 +199,7 @@  ULONG WINAPI MAPISendMail(LHANDLE session, ULONG_PTR uiparam,
             switch (message->lpRecips[i].ulRecipClass)
             {
                 case MAPI_ORIG:
-                    TRACE("From: %s\n", debugstr_a(address));
+                    TRACE("From: %s (unused)\n", debugstr_a(address));
                     break;
 
                 case MAPI_TO:
@@ -205,7 +277,7 @@  ULONG WINAPI MAPISendMail(LHANDLE session, ULONG_PTR uiparam,
     }
 
     if (message->lpOriginator)
-        TRACE("From: %s\n", debugstr_a(message->lpOriginator->lpszAddress));
+        TRACE("From: %s (unused)\n", debugstr_a(message->lpOriginator->lpszAddress));
 
     for (i = 0; i < message->nRecipCount; i++)
     {
@@ -291,6 +363,128 @@  exit:
     return ret;
 }
 
+/**************************************************************************
+ *  XDGSendMail
+ *
+ * Send a message using xdg-email mail client.
+ *
+ */
+static ULONG XDGSendMail(lpMapiMessage message)
+{
+    int i;
+    int argc = 0;
+    int max_args;
+    char **argv = NULL;
+    ULONG ret;
+
+    max_args = 1 + (2 + message->nRecipCount + message->nFileCount) * 2;
+    argv = HeapAlloc(GetProcessHeap(), 0, (max_args + 1) * sizeof(*argv));
+    if (!argv)
+        return MAPI_E_INSUFFICIENT_MEMORY;
+
+    ret = add_argument(argv, &argc, "xdg-email", NULL);
+    if (ret != SUCCESS_SUCCESS)
+        goto exit;
+
+    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);
+            ret = MAPI_E_FAILURE;
+            goto exit;
+        }
+
+        if (message->lpRecips[i].lpszAddress)
+        {
+            ret = add_target(argv, &argc, message->lpRecips[i].ulRecipClass,
+                        message->lpRecips[i].lpszAddress);
+            if (ret != SUCCESS_SUCCESS)
+                goto exit;
+        }
+        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));
+        ret = add_file(argv, &argc, message->lpFiles[i].lpszPathName, message->lpFiles[i].lpszFileName);
+        if (ret != SUCCESS_SUCCESS)
+            goto exit;
+    }
+
+    if (message->lpszSubject)
+    {
+        TRACE("Subject: %s\n", debugstr_a(message->lpszSubject));
+        ret = add_argument(argv, &argc, "--subject", message->lpszSubject);
+        if (ret != SUCCESS_SUCCESS)
+            goto exit;
+    }
+
+    if (message->lpszNoteText)
+    {
+        TRACE("Body: %s\n", debugstr_a(message->lpszNoteText));
+        ret = add_argument(argv, &argc, "--body", message->lpszNoteText);
+        if (ret != SUCCESS_SUCCESS)
+            goto exit;
+    }
+
+    argv[argc] = NULL;
+    TRACE("Executing xdg-email; parameters:\n");
+    for (i = 0; i < argc; i++)
+        TRACE(" %d: [%s]\n", i, argv[i]);
+    if (_spawnvp(_P_WAIT, "xdg-email", (const char **) argv) == 0)
+        ret = SUCCESS_SUCCESS;
+    else
+        ret = MAPI_E_FAILURE;
+
+exit:
+    for (i = 0; i < argc; i++)
+        HeapFree(GetProcessHeap(), 0, argv[i]);
+    HeapFree(GetProcessHeap(), 0, argv);
+
+    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)
+{
+    ULONG ret;
+
+    TRACE("(0x%08lx 0x%08lx %p 0x%08x 0x%08x)\n", session, uiparam, message, flags, reserved);
+
+    if (!message)
+        return MAPI_E_FAILURE;
+
+    ret = XDGSendMail(message);
+    if (ret != SUCCESS_SUCCESS)
+        ret = BrowserSendMail(message);
+
+    return ret;
+}
+
 ULONG WINAPI MAPISendDocuments(ULONG_PTR uiparam, LPSTR delim, LPSTR paths,
     LPSTR filenames, ULONG reserved)
 {

Comments

> This addresses https://bugs.winehq.org/show_bug.cgi?id=11770 for systems with xdg-email.

*Blush*.  I really need to configure repo by repo to targets...

Sorry for the confusion.

Cheers,

Jeremy