xref: /trunk/main/shell/source/win32/sysmail/sysmailclient.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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 {
212     return Reference<XMailMessage>( new WinSysMailMsg() );
213 }
214 
215 /**
216     Assemble a command line for SendDoc.exe out of the members
217     of the supplied XMailMessage.
218 
219     @param xMailMessage
220     [in] the mail message.
221 
222     @param aFlags
223     [in] different flags to be used with the system mail service.
224 
225     @param rCommandArgs
226     [in|out] a buffer for the command line arguments. The buffer
227     is assumed to be empty.
228 
229     @throws com::sun::star::lang::IllegalArgumentException
230     if an invalid file URL has been detected in the attachment list.
231 */
assembleCommandLine(const Reference<XMailMessage> & xMailMessage,sal_Int32 aFlag,StringList_t & rCommandArgs)232 void WinSysMailClient::assembleCommandLine(
233     const Reference<XMailMessage>& xMailMessage,
234     sal_Int32 aFlag,
235     StringList_t& rCommandArgs)
236 {
237     OSL_ENSURE(rCommandArgs.size() == 0, "Provided command argument buffer not empty");
238 
239     rtl::OUString to = xMailMessage->getRecipient();
240     if (to.getLength() > 0)
241     {
242         rCommandArgs.push_back(TO);
243         rCommandArgs.push_back(to);
244     }
245 
246     Sequence<rtl::OUString> ccRecipients = xMailMessage->getCcRecipient();
247     for (int i = 0; i < ccRecipients.getLength(); i++)
248     {
249         rCommandArgs.push_back(CC);
250         rCommandArgs.push_back(ccRecipients[i]);
251     }
252 
253     Sequence<rtl::OUString> bccRecipients = xMailMessage->getBccRecipient();
254     for (int i = 0; i < bccRecipients.getLength(); i++)
255     {
256         rCommandArgs.push_back(BCC);
257         rCommandArgs.push_back(bccRecipients[i]);
258     }
259 
260     rtl::OUString from = xMailMessage->getOriginator();
261     if (from.getLength() > 0)
262     {
263         rCommandArgs.push_back(FROM);
264         rCommandArgs.push_back(from);
265     }
266 
267     Sequence<rtl::OUString> attachments = xMailMessage->getAttachement();
268     for (int i = 0; i < attachments.getLength(); i++)
269     {
270         rtl::OUString sysPath;
271         osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(attachments[i], sysPath);
272         if (err != osl::FileBase::E_None)
273             throw IllegalArgumentException(
274                 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid attachment file URL")),
275                 static_cast<XMailClient*>(this),
276                 1);
277 
278         rCommandArgs.push_back(ATTACH);
279         rCommandArgs.push_back( quoteString( sysPath ) );
280     }
281 
282     rtl::OUString body = xMailMessage->getBody();
283     if (body.getLength()>0)
284     {
285         rCommandArgs.push_back(BODY);
286         rCommandArgs.push_back( quoteAndEscape( body ) );
287     }
288 
289     rtl::OUString subject = xMailMessage->getSubject();
290     if (subject.getLength() > 0)
291     {
292         rCommandArgs.push_back(SUBJECT);
293         rCommandArgs.push_back( quoteAndEscape( subject ) );
294     }
295 
296     if (!(aFlag & NO_USER_INTERFACE))
297         rCommandArgs.push_back(FLAG_MAPI_DIALOG);
298 
299     if (!(aFlag & NO_LOGON_DIALOG))
300         rCommandArgs.push_back(FLAG_MAPI_LOGON_UI);
301 }
302 
sendMailMessage(const Reference<XMailMessage> & xMailMessage,sal_Int32 aFlag)303 void SAL_CALL WinSysMailClient::sendMailMessage(
304     const Reference<XMailMessage>& xMailMessage,
305     sal_Int32 aFlag)
306 {
307     validateParameter(xMailMessage, aFlag);
308 
309     StringList_t senddocParams;
310     assembleCommandLine(xMailMessage, aFlag, senddocParams);
311 
312     if (!executeSenddoc(senddocParams))
313         throw Exception(
314             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Send email failed")),
315             static_cast<XMailClient*>(this));
316 }
317 
validateParameter(const Reference<XMailMessage> & xMailMessage,sal_Int32 aFlag)318 void WinSysMailClient::validateParameter(
319     const Reference<XMailMessage>& xMailMessage,
320     sal_Int32 aFlag )
321 {
322     if (!xMailMessage.is())
323         throw IllegalArgumentException(
324             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Empty mail message reference")),
325             static_cast<XMailClient*>(this),
326             1);
327 
328     // #93077#
329     OSL_ENSURE(!(aFlag & NO_LOGON_DIALOG), "Flag NO_LOGON_DIALOG has currently no effect");
330 
331     // check the flags, the allowed range is 0 - (2^n - 1)
332     if (aFlag < 0 || aFlag > 3)
333         throw IllegalArgumentException(
334             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid flag value")),
335             static_cast<XMailClient*>(this),
336             2);
337 
338     // check if a recipient is specified of the flags NO_USER_INTERFACE is specified
339     if ((aFlag & NO_USER_INTERFACE) && !xMailMessage->getRecipient().getLength())
340         throw IllegalArgumentException(
341             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No recipient specified")),
342             static_cast<XMailClient*>(this),
343             1);
344 }
345 
346 }
347