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 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 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 */ 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 */ 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 */ 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 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 */ 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 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 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