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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_shell.hxx"
26 #include <osl/diagnose.h>
27 #include <osl/process.h>
28 #include <rtl/bootstrap.hxx>
29 #include "smplmailclient.hxx"
30 #include "smplmailmsg.hxx"
31 #include <com/sun/star/system/SimpleMailClientFlags.hpp>
32 #include <osl/file.hxx>
33 
34 #define WIN32_LEAN_AND_MEAN
35 #if defined _MSC_VER
36 #pragma warning(push, 1)
37 #endif
38 #include <windows.h>
39 #include <mapi.h>
40 #if defined _MSC_VER
41 #pragma warning(pop)
42 #endif
43 
44 #include <process.h>
45 #include <vector>
46 
47 using css::uno::Reference;
48 using css::uno::Exception;
49 using css::uno::RuntimeException;
50 using css::uno::Sequence;
51 using css::lang::IllegalArgumentException;
52 
53 using css::system::XSimpleMailClient;
54 using css::system::XSimpleMailMessage;
55 using css::system::SimpleMailClientFlags::NO_USER_INTERFACE;
56 using css::system::SimpleMailClientFlags::NO_LOGON_DIALOG;
57 
58 typedef std::vector<rtl::OUString> StringList_t;
59 typedef StringList_t::const_iterator StringListIterator_t;
60 
61 const rtl::OUString TO = rtl::OUString::createFromAscii("--to");
62 const rtl::OUString CC = rtl::OUString::createFromAscii("--cc");
63 const rtl::OUString BCC = rtl::OUString::createFromAscii("--bcc");
64 const rtl::OUString FROM = rtl::OUString::createFromAscii("--from");
65 const rtl::OUString SUBJECT = rtl::OUString::createFromAscii("--subject");
66 const rtl::OUString BODY = rtl::OUString::createFromAscii("--body");
67 const rtl::OUString ATTACH = rtl::OUString::createFromAscii("--attach");
68 const rtl::OUString FLAG_MAPI_DIALOG = rtl::OUString::createFromAscii("--mapi-dialog");
69 const rtl::OUString FLAG_MAPI_LOGON_UI = rtl::OUString::createFromAscii("--mapi-logon-ui");
70 
71 namespace /* private */
72 {
73     /** @internal
74         look if an alternative program is configured
75         which should be used as senddoc executable */
76     rtl::OUString getAlternativeSenddocUrl()
77     {
78         rtl::OUString altSenddocUrl;
79         HKEY hkey;
80         LONG lret = RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\OpenOffice.org\\SendAsEMailClient", &hkey);
81         if (lret == ERROR_SUCCESS)
82         {
83             wchar_t buff[MAX_PATH];
84             LONG sz = sizeof(buff);
85             lret = RegQueryValueW(hkey, NULL, buff, &sz);
86             if (lret == ERROR_SUCCESS)
87             {
88                 osl::FileBase::getFileURLFromSystemPath(reinterpret_cast<const sal_Unicode*>(buff), altSenddocUrl);
89             }
90             RegCloseKey(hkey);
91         }
92         return altSenddocUrl;
93     }
94 
95     /**
96         Returns the absolute file Url of the senddoc executable.
97 
98         @returns
99         the absolute file Url of the senddoc executable. In case
100         of an error an empty string will be returned.
101     */
102     rtl::OUString getSenddocUrl()
103     {
104         rtl::OUString senddocUrl = getAlternativeSenddocUrl();
105 
106         if (senddocUrl.getLength() == 0)
107         {
108             senddocUrl = rtl::OUString(
109                 RTL_CONSTASCII_USTRINGPARAM(
110                     "$OOO_BASE_DIR/program/senddoc.exe"));
111             rtl::Bootstrap::expandMacros(senddocUrl); //TODO: detect failure
112         }
113         return senddocUrl;
114     }
115 
116     /**
117         Execute Senddoc.exe which a MAPI wrapper.
118 
119         @param rCommandArgs
120         [in] the arguments to be passed to Senddoc.exe
121 
122         @returns
123         <TRUE/> on success.
124     */
125     bool executeSenddoc(const StringList_t& rCommandArgs)
126     {
127         rtl::OUString senddocUrl = getSenddocUrl();
128         if (senddocUrl.getLength() == 0)
129             return false;
130 
131         oslProcess proc;
132         oslProcessError err = osl_Process_E_Unknown;
133 
134         /* for efficiency reasons we are using a 'bad' cast here
135         as a vector or rtl::OUStrings is nothing else than
136         an array of pointers to rtl_uString's */
137         err = osl_executeProcess(
138             senddocUrl.pData,
139             (rtl_uString**)&rCommandArgs[0],
140             rCommandArgs.size(),
141             osl_Process_WAIT | osl_Process_DETACHED,
142             NULL,
143             NULL,
144             NULL,
145             0,
146             &proc);
147 
148         if (err != osl_Process_E_None)
149             return false;
150 
151         oslProcessInfo procInfo;
152         procInfo.Size = sizeof(oslProcessInfo);
153         osl_getProcessInfo(proc, osl_Process_EXITCODE, &procInfo);
154         osl_freeProcessHandle(proc);
155         return (procInfo.Code == SUCCESS_SUCCESS);
156     }
157 } // namespace private
158 
159 Reference<XSimpleMailMessage> SAL_CALL CSmplMailClient::createSimpleMailMessage()
160     throw (RuntimeException)
161 {
162     return Reference<XSimpleMailMessage>(new CSmplMailMsg());
163 }
164 
165 /**
166     Assemble a command line for SendDoc.exe out of the members
167     of the supplied SimpleMailMessage.
168 
169     @param xSimpleMailMessage
170     [in] the mail message.
171 
172     @param aFlags
173     [in] different flags to be used with the simple mail service.
174 
175     @param rCommandArgs
176     [in|out] a buffer for the command line arguments. The buffer
177     is assumed to be empty.
178 
179     @throws com::sun::star::lang::IllegalArgumentException
180     if an invalid file URL has been detected in the attachment list.
181 */
182 void CSmplMailClient::assembleCommandLine(
183     const Reference<XSimpleMailMessage>& xSimpleMailMessage,
184     sal_Int32 aFlag, StringList_t& rCommandArgs)
185 {
186     OSL_ENSURE(rCommandArgs.size() == 0, "Provided command argument buffer not empty");
187 
188     rtl::OUString to = xSimpleMailMessage->getRecipient();
189     if (to.getLength() > 0)
190     {
191         rCommandArgs.push_back(TO);
192         rCommandArgs.push_back(to);
193     }
194 
195     Sequence<rtl::OUString> ccRecipients = xSimpleMailMessage->getCcRecipient();
196     for (int i = 0; i < ccRecipients.getLength(); i++)
197     {
198         rCommandArgs.push_back(CC);
199         rCommandArgs.push_back(ccRecipients[i]);
200     }
201 
202     Sequence<rtl::OUString> bccRecipients = xSimpleMailMessage->getBccRecipient();
203     for (int i = 0; i < bccRecipients.getLength(); i++)
204     {
205         rCommandArgs.push_back(BCC);
206         rCommandArgs.push_back(bccRecipients[i]);
207     }
208 
209     rtl::OUString from = xSimpleMailMessage->getOriginator();
210     if (from.getLength() > 0)
211     {
212         rCommandArgs.push_back(FROM);
213         rCommandArgs.push_back(from);
214     }
215 
216     rtl::OUString subject = xSimpleMailMessage->getSubject();
217     if (subject.getLength() > 0)
218     {
219         rCommandArgs.push_back(SUBJECT);
220         rCommandArgs.push_back(subject);
221     }
222 
223     Sequence<rtl::OUString> attachments = xSimpleMailMessage->getAttachement();
224     for (int i = 0; i < attachments.getLength(); i++)
225     {
226         rtl::OUString sysPath;
227         osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(attachments[i], sysPath);
228         if (err != osl::FileBase::E_None)
229             throw IllegalArgumentException(
230                 rtl::OUString::createFromAscii("Invalid attachment file URL"),
231                 static_cast<XSimpleMailClient*>(this),
232                 1);
233 
234         rCommandArgs.push_back(ATTACH);
235         rCommandArgs.push_back(sysPath);
236     }
237 
238     if (!(aFlag & NO_USER_INTERFACE))
239         rCommandArgs.push_back(FLAG_MAPI_DIALOG);
240 
241     if (!(aFlag & NO_LOGON_DIALOG))
242         rCommandArgs.push_back(FLAG_MAPI_LOGON_UI);
243 }
244 
245 void SAL_CALL CSmplMailClient::sendSimpleMailMessage(
246     const Reference<XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag)
247     throw (IllegalArgumentException, Exception, RuntimeException)
248 {
249     validateParameter(xSimpleMailMessage, aFlag);
250 
251     StringList_t senddocParams;
252     assembleCommandLine(xSimpleMailMessage, aFlag, senddocParams);
253 
254     if (!executeSenddoc(senddocParams))
255         throw Exception(
256             rtl::OUString::createFromAscii("Send email failed"),
257             static_cast<XSimpleMailClient*>(this));
258 }
259 
260 void CSmplMailClient::validateParameter(
261     const Reference<XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag )
262 {
263     if (!xSimpleMailMessage.is())
264         throw IllegalArgumentException(
265             rtl::OUString::createFromAscii("Empty mail message reference"),
266             static_cast<XSimpleMailClient*>(this),
267             1);
268 
269     // #93077#
270     OSL_ENSURE(!(aFlag & NO_LOGON_DIALOG), "Flag NO_LOGON_DIALOG has currently no effect");
271 
272     // check the flags, the allowed range is 0 - (2^n - 1)
273     if (aFlag < 0 || aFlag > 3)
274         throw IllegalArgumentException(
275             rtl::OUString::createFromAscii("Invalid flag value"),
276             static_cast<XSimpleMailClient*>(this),
277             2);
278 
279     // check if a recipient is specified of the flags NO_USER_INTERFACE is specified
280     if ((aFlag & NO_USER_INTERFACE) && !xSimpleMailMessage->getRecipient().getLength())
281         throw IllegalArgumentException(
282             rtl::OUString::createFromAscii("No recipient specified"),
283             static_cast<XSimpleMailClient*>(this),
284             1);
285 }
286