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.org\\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 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 */ 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 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 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