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