From: Jeremy White Subject: [PATCH v4] winemapi: Directly use xdg-email if available, enabling file attachments. Message-Id: <1480713351-31085-1-git-send-email-jwhite@codeweavers.com> Date: Fri, 2 Dec 2016 15:15:51 -0600 Signed-off-by: Jeremy White --- This addresses https://bugs.winehq.org/show_bug.cgi?id=11770 for systems with xdg-email. v4: Minor 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(-) 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) { -- 2.1.4