1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_shell.hxx"
24 
25 #include "sysmailclient.hxx"
26 #include "sysmailmsg.hxx"
27 
28 #include <com/sun/star/system/MailClientFlags.hpp>
29 #include <com/sun/star/system/XMailMessage.hpp>
30 
31 #include <osl/diagnose.h>
32 #include <osl/process.h>
33 #include <rtl/bootstrap.hxx>
34 #include <osl/file.hxx>
35 #include <rtl/ustrbuf.hxx>
36 
37 #define WIN32_LEAN_AND_MEAN
38 #if defined _MSC_VER
39 #pragma warning(push, 1)
40 #endif
41 #include <windows.h>
42 #include <mapi.h>
43 #if defined _MSC_VER
44 #pragma warning(pop)
45 #endif
46 
47 #include <process.h>
48 #include <vector>
49 
50 using css::uno::UNO_QUERY;
51 using css::uno::Reference;
52 using css::uno::Exception;
53 using css::uno::RuntimeException;
54 using css::uno::Sequence;
55 using css::lang::IllegalArgumentException;
56 
57 using css::system::XMailClient;
58 using css::system::XMailMessage;
59 using css::system::XMailMessage;
60 using css::system::MailClientFlags::NO_USER_INTERFACE;
61 using css::system::MailClientFlags::NO_LOGON_DIALOG;
62 
63 using rtl::OUString;
64 using rtl::OUStringBuffer;
65 
66 namespace shell
67 {
68 namespace /* private */
69 {
70     typedef std::vector<rtl::OUString> StringList_t;
71     typedef StringList_t::const_iterator StringListIterator_t;
72 
73     const rtl::OUString TO = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--to"));
74     const rtl::OUString CC = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--cc"));
75     const rtl::OUString BCC = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--bcc"));
76     const rtl::OUString FROM = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--from"));
77     const rtl::OUString SUBJECT = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--subject"));
78     const rtl::OUString BODY = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--body"));
79     const rtl::OUString ATTACH = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--attach"));
80     const rtl::OUString FLAG_MAPI_DIALOG = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--mapi-dialog"));
81     const rtl::OUString FLAG_MAPI_LOGON_UI = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("--mapi-logon-ui"));
82 
quoteString(const OUString & rStr)83     static OUString quoteString( const OUString& rStr )
84     {
85         rtl::OUStringBuffer quoted;
86         quoted.append(sal_Unicode('"'));
87         quoted.append(rStr);
88         quoted.append(sal_Unicode('"'));
89 
90         return quoted.makeStringAndClear();
91     }
92 
quoteAndEscape(const OUString & rStr)93     static OUString quoteAndEscape( const OUString &rStr )
94     {
95         OUStringBuffer aBuffer;
96         aBuffer.append(sal_Unicode('"'));
97 
98         sal_Int32 nIndex = rStr.indexOf(sal_Unicode('"'));
99         if ( nIndex == -1 )
100             aBuffer.append( rStr );
101         else
102         {
103             const sal_Unicode *pStart = rStr.getStr();
104             const sal_Unicode *pFrom = pStart;
105             const sal_Int32 nLen = rStr.getLength();
106             sal_Int32 nPrev = 0;;
107             do
108             {
109                 aBuffer.append( pFrom, nIndex - nPrev );
110                 aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "\\\"" ) );
111                 nIndex++;
112                 pFrom = pStart + nIndex;
113                 nPrev = nIndex;
114             }
115             while ( ( nIndex = rStr.indexOf( '"' , nIndex ) ) != -1  );
116 
117             aBuffer.append( pFrom, nLen - nPrev );
118         }
119 
120         aBuffer.append(sal_Unicode('"'));
121 
122         return aBuffer.makeStringAndClear();
123     }
124 
125     /** @internal
126         look if an alternative program is configured
127         which should be used as senddoc executable */
getAlternativeSenddocUrl()128     static rtl::OUString getAlternativeSenddocUrl()
129     {
130         rtl::OUString altSenddocUrl;
131         HKEY hkey;
132         LONG lret = RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\OpenOffice\\SendAsEMailClient", &hkey);
133         if (lret == ERROR_SUCCESS)
134         {
135             wchar_t buff[MAX_PATH];
136             LONG sz = sizeof(buff);
137             lret = RegQueryValueW(hkey, NULL, buff, &sz);
138             if (lret == ERROR_SUCCESS)
139             {
140                 osl::FileBase::getFileURLFromSystemPath(reinterpret_cast<const sal_Unicode*>(buff), altSenddocUrl);
141             }
142             RegCloseKey(hkey);
143         }
144         return altSenddocUrl;
145     }
146 
147     /**
148         Returns the absolute file Url of the senddoc executable.
149 
150         @returns
151         the absolute file Url of the senddoc executable. In case
152         of an error an empty string will be returned.
153     */
getSenddocUrl()154     static rtl::OUString getSenddocUrl()
155     {
156         rtl::OUString senddocUrl = getAlternativeSenddocUrl();
157 
158         if (senddocUrl.getLength() == 0)
159         {
160             senddocUrl = rtl::OUString(
161                 RTL_CONSTASCII_USTRINGPARAM(
162                     "$OOO_BASE_DIR/program/senddoc.exe"));
163             rtl::Bootstrap::expandMacros(senddocUrl); //TODO: detect failure
164         }
165         return senddocUrl;
166     }
167 
168     /**
169         Execute Senddoc.exe which a MAPI wrapper.
170         @param rCommandArgs
171                 [in] the arguments to be passed to Senddoc.exe
172         @returns
173                 <TRUE/> on success.
174     */
executeSenddoc(const StringList_t & rCommandArgs)175     static bool executeSenddoc(const StringList_t& rCommandArgs)
176     {
177         rtl::OUString senddocUrl = getSenddocUrl();
178         if (senddocUrl.getLength() == 0)
179             return false;
180 
181         oslProcess proc;
182         oslProcessError err = osl_Process_E_Unknown;
183 
184         /* for efficiency reasons we are using a 'bad' cast here
185         as a vector or rtl::OUStrings is nothing else than
186         an array of pointers to rtl_uString's */
187         err = osl_executeProcess(
188             senddocUrl.pData,
189             (rtl_uString**)&rCommandArgs[0],
190             rCommandArgs.size(),
191             osl_Process_WAIT | osl_Process_DETACHED,
192             NULL,
193             NULL,
194             NULL,
195             0,
196             &proc);
197 
198         if (err != osl_Process_E_None)
199             return false;
200 
201         oslProcessInfo procInfo;
202         procInfo.Size = sizeof(oslProcessInfo);
203         osl_getProcessInfo(proc, osl_Process_EXITCODE, &procInfo);
204         osl_freeProcessHandle(proc);
205         return (procInfo.Code == SUCCESS_SUCCESS);
206     }
207 } // namespace private
208 
209 
createMailMessage()210 Reference<XMailMessage> SAL_CALL WinSysMailClient::createMailMessage()
211     throw (RuntimeException)
212 {
213     return Reference<XMailMessage>( new WinSysMailMsg() );
214 }
215 
216 /**
217     Assemble a command line for SendDoc.exe out of the members
218     of the supplied XMailMessage.
219 
220     @param xMailMessage
221     [in] the mail message.
222 
223     @param aFlags
224     [in] different flags to be used with the system mail service.
225 
226     @param rCommandArgs
227     [in|out] a buffer for the command line arguments. The buffer
228     is assumed to be empty.
229 
230     @throws com::sun::star::lang::IllegalArgumentException
231     if an invalid file URL has been detected in the attachment list.
232 */
assembleCommandLine(const Reference<XMailMessage> & xMailMessage,sal_Int32 aFlag,StringList_t & rCommandArgs)233 void WinSysMailClient::assembleCommandLine(
234     const Reference<XMailMessage>& xMailMessage,
235     sal_Int32 aFlag,
236     StringList_t& rCommandArgs)
237 {
238     OSL_ENSURE(rCommandArgs.size() == 0, "Provided command argument buffer not empty");
239 
240     rtl::OUString to = xMailMessage->getRecipient();
241     if (to.getLength() > 0)
242     {
243         rCommandArgs.push_back(TO);
244         rCommandArgs.push_back(to);
245     }
246 
247     Sequence<rtl::OUString> ccRecipients = xMailMessage->getCcRecipient();
248     for (int i = 0; i < ccRecipients.getLength(); i++)
249     {
250         rCommandArgs.push_back(CC);
251         rCommandArgs.push_back(ccRecipients[i]);
252     }
253 
254     Sequence<rtl::OUString> bccRecipients = xMailMessage->getBccRecipient();
255     for (int i = 0; i < bccRecipients.getLength(); i++)
256     {
257         rCommandArgs.push_back(BCC);
258         rCommandArgs.push_back(bccRecipients[i]);
259     }
260 
261     rtl::OUString from = xMailMessage->getOriginator();
262     if (from.getLength() > 0)
263     {
264         rCommandArgs.push_back(FROM);
265         rCommandArgs.push_back(from);
266     }
267 
268     Sequence<rtl::OUString> attachments = xMailMessage->getAttachement();
269     for (int i = 0; i < attachments.getLength(); i++)
270     {
271         rtl::OUString sysPath;
272         osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(attachments[i], sysPath);
273         if (err != osl::FileBase::E_None)
274             throw IllegalArgumentException(
275                 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid attachment file URL")),
276                 static_cast<XMailClient*>(this),
277                 1);
278 
279         rCommandArgs.push_back(ATTACH);
280         rCommandArgs.push_back( quoteString( sysPath ) );
281     }
282 
283     rtl::OUString body = xMailMessage->getBody();
284     if (body.getLength()>0)
285     {
286         rCommandArgs.push_back(BODY);
287         rCommandArgs.push_back( quoteAndEscape( body ) );
288     }
289 
290     rtl::OUString subject = xMailMessage->getSubject();
291     if (subject.getLength() > 0)
292     {
293         rCommandArgs.push_back(SUBJECT);
294         rCommandArgs.push_back( quoteAndEscape( subject ) );
295     }
296 
297     if (!(aFlag & NO_USER_INTERFACE))
298         rCommandArgs.push_back(FLAG_MAPI_DIALOG);
299 
300     if (!(aFlag & NO_LOGON_DIALOG))
301         rCommandArgs.push_back(FLAG_MAPI_LOGON_UI);
302 }
303 
sendMailMessage(const Reference<XMailMessage> & xMailMessage,sal_Int32 aFlag)304 void SAL_CALL WinSysMailClient::sendMailMessage(
305     const Reference<XMailMessage>& xMailMessage,
306     sal_Int32 aFlag)
307     throw (IllegalArgumentException, Exception, RuntimeException)
308 {
309     validateParameter(xMailMessage, aFlag);
310 
311     StringList_t senddocParams;
312     assembleCommandLine(xMailMessage, aFlag, senddocParams);
313 
314     if (!executeSenddoc(senddocParams))
315         throw Exception(
316             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Send email failed")),
317             static_cast<XMailClient*>(this));
318 }
319 
validateParameter(const Reference<XMailMessage> & xMailMessage,sal_Int32 aFlag)320 void WinSysMailClient::validateParameter(
321     const Reference<XMailMessage>& xMailMessage,
322     sal_Int32 aFlag )
323 {
324     if (!xMailMessage.is())
325         throw IllegalArgumentException(
326             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Empty mail message reference")),
327             static_cast<XMailClient*>(this),
328             1);
329 
330     // #93077#
331     OSL_ENSURE(!(aFlag & NO_LOGON_DIALOG), "Flag NO_LOGON_DIALOG has currently no effect");
332 
333     // check the flags, the allowed range is 0 - (2^n - 1)
334     if (aFlag < 0 || aFlag > 3)
335         throw IllegalArgumentException(
336             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid flag value")),
337             static_cast<XMailClient*>(this),
338             2);
339 
340     // check if a recipient is specified of the flags NO_USER_INTERFACE is specified
341     if ((aFlag & NO_USER_INTERFACE) && !xMailMessage->getRecipient().getLength())
342         throw IllegalArgumentException(
343             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No recipient specified")),
344             static_cast<XMailClient*>(this),
345             1);
346 }
347 
348 }
349