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