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 27 //------------------------------------------------------------------------ 28 // includes 29 //------------------------------------------------------------------------ 30 #include <osl/diagnose.h> 31 #include "SysShExec.hxx" 32 #include <osl/file.hxx> 33 34 #ifndef _COM_SUN_STAR_SYS_SHELL_SYSTEMSHELLEXECUTEFLAGS_HPP_ 35 #include <com/sun/star/system/SystemShellExecuteFlags.hpp> 36 #endif 37 38 #define WIN32_LEAN_AND_MEAN 39 #if defined _MSC_VER 40 #pragma warning(push, 1) 41 #endif 42 #include <windows.h> 43 #include <shellapi.h> 44 #include <objbase.h> 45 #if defined _MSC_VER 46 #pragma warning(pop) 47 #endif 48 49 //------------------------------------------------------------------------ 50 // namespace directives 51 //------------------------------------------------------------------------ 52 53 using com::sun::star::uno::Reference; 54 using com::sun::star::uno::RuntimeException; 55 using com::sun::star::uno::Sequence; 56 using com::sun::star::uno::XInterface; 57 using com::sun::star::lang::EventObject; 58 using com::sun::star::lang::XServiceInfo; 59 using com::sun::star::lang::IllegalArgumentException; 60 using rtl::OUString; 61 using osl::Mutex; 62 using com::sun::star::system::XSystemShellExecute; 63 using com::sun::star::system::SystemShellExecuteException; 64 65 using namespace ::com::sun::star::system::SystemShellExecuteFlags; 66 using namespace cppu; 67 68 //------------------------------------------------------------------------ 69 // defines 70 //------------------------------------------------------------------------ 71 72 #define SYSSHEXEC_IMPL_NAME "com.sun.star.sys.shell.SystemShellExecute" 73 74 //------------------------------------------------------------------------ 75 // helper functions 76 //------------------------------------------------------------------------ 77 78 namespace // private 79 { 80 Sequence< OUString > SAL_CALL SysShExec_getSupportedServiceNames() 81 { 82 Sequence< OUString > aRet(1); 83 aRet[0] = OUString::createFromAscii("com.sun.star.sys.shell.SystemShellExecute"); 84 return aRet; 85 } 86 87 /* This is the error table that defines the mapping between OS error 88 codes and errno values */ 89 90 struct errentry { 91 unsigned long oscode; /* OS return value */ 92 int errnocode; /* System V error code */ 93 }; 94 95 struct errentry errtable[] = { 96 { ERROR_SUCCESS, osl_File_E_None }, /* 0 */ 97 { ERROR_INVALID_FUNCTION, osl_File_E_INVAL }, /* 1 */ 98 { ERROR_FILE_NOT_FOUND, osl_File_E_NOENT }, /* 2 */ 99 { ERROR_PATH_NOT_FOUND, osl_File_E_NOENT }, /* 3 */ 100 { ERROR_TOO_MANY_OPEN_FILES, osl_File_E_MFILE }, /* 4 */ 101 { ERROR_ACCESS_DENIED, osl_File_E_ACCES }, /* 5 */ 102 { ERROR_INVALID_HANDLE, osl_File_E_BADF }, /* 6 */ 103 { ERROR_ARENA_TRASHED, osl_File_E_NOMEM }, /* 7 */ 104 { ERROR_NOT_ENOUGH_MEMORY, osl_File_E_NOMEM }, /* 8 */ 105 { ERROR_INVALID_BLOCK, osl_File_E_NOMEM }, /* 9 */ 106 { ERROR_BAD_ENVIRONMENT, osl_File_E_2BIG }, /* 10 */ 107 { ERROR_BAD_FORMAT, osl_File_E_NOEXEC }, /* 11 */ 108 { ERROR_INVALID_ACCESS, osl_File_E_INVAL }, /* 12 */ 109 { ERROR_INVALID_DATA, osl_File_E_INVAL }, /* 13 */ 110 { ERROR_INVALID_DRIVE, osl_File_E_NOENT }, /* 15 */ 111 { ERROR_CURRENT_DIRECTORY, osl_File_E_ACCES }, /* 16 */ 112 { ERROR_NOT_SAME_DEVICE, osl_File_E_XDEV }, /* 17 */ 113 { ERROR_NO_MORE_FILES, osl_File_E_NOENT }, /* 18 */ 114 { ERROR_LOCK_VIOLATION, osl_File_E_ACCES }, /* 33 */ 115 { ERROR_BAD_NETPATH, osl_File_E_NOENT }, /* 53 */ 116 { ERROR_NETWORK_ACCESS_DENIED, osl_File_E_ACCES }, /* 65 */ 117 { ERROR_BAD_NET_NAME, osl_File_E_NOENT }, /* 67 */ 118 { ERROR_FILE_EXISTS, osl_File_E_EXIST }, /* 80 */ 119 { ERROR_CANNOT_MAKE, osl_File_E_ACCES }, /* 82 */ 120 { ERROR_FAIL_I24, osl_File_E_ACCES }, /* 83 */ 121 { ERROR_INVALID_PARAMETER, osl_File_E_INVAL }, /* 87 */ 122 { ERROR_NO_PROC_SLOTS, osl_File_E_AGAIN }, /* 89 */ 123 { ERROR_DRIVE_LOCKED, osl_File_E_ACCES }, /* 108 */ 124 { ERROR_BROKEN_PIPE, osl_File_E_PIPE }, /* 109 */ 125 { ERROR_DISK_FULL, osl_File_E_NOSPC }, /* 112 */ 126 { ERROR_INVALID_TARGET_HANDLE, osl_File_E_BADF }, /* 114 */ 127 { ERROR_INVALID_HANDLE, osl_File_E_INVAL }, /* 124 */ 128 { ERROR_WAIT_NO_CHILDREN, osl_File_E_CHILD }, /* 128 */ 129 { ERROR_CHILD_NOT_COMPLETE, osl_File_E_CHILD }, /* 129 */ 130 { ERROR_DIRECT_ACCESS_HANDLE, osl_File_E_BADF }, /* 130 */ 131 { ERROR_NEGATIVE_SEEK, osl_File_E_INVAL }, /* 131 */ 132 { ERROR_SEEK_ON_DEVICE, osl_File_E_ACCES }, /* 132 */ 133 { ERROR_DIR_NOT_EMPTY, osl_File_E_NOTEMPTY }, /* 145 */ 134 { ERROR_NOT_LOCKED, osl_File_E_ACCES }, /* 158 */ 135 { ERROR_BAD_PATHNAME, osl_File_E_NOENT }, /* 161 */ 136 { ERROR_MAX_THRDS_REACHED, osl_File_E_AGAIN }, /* 164 */ 137 { ERROR_LOCK_FAILED, osl_File_E_ACCES }, /* 167 */ 138 { ERROR_ALREADY_EXISTS, osl_File_E_EXIST }, /* 183 */ 139 { ERROR_FILENAME_EXCED_RANGE, osl_File_E_NOENT }, /* 206 */ 140 { ERROR_NESTING_NOT_ALLOWED, osl_File_E_AGAIN }, /* 215 */ 141 { ERROR_NOT_ENOUGH_QUOTA, osl_File_E_NOMEM } /* 1816 */ 142 }; 143 144 /* size of the table */ 145 #define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0])) 146 147 /* The following two constants must be the minimum and maximum 148 values in the (contiguous) range of osl_File_E_xec Failure errors. */ 149 #define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG 150 #define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN 151 152 /* These are the low and high value in the range of errors that are 153 access violations */ 154 #define MIN_EACCES_RANGE ERROR_WRITE_PROTECT 155 #define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED 156 157 158 /*******************************************************************************/ 159 160 oslFileError _mapError( DWORD dwError ) 161 { 162 int i; 163 164 /* check the table for the OS error code */ 165 for ( i = 0; i < ERRTABLESIZE; ++i ) 166 { 167 if ( dwError == errtable[i].oscode ) 168 return (oslFileError)errtable[i].errnocode; 169 } 170 171 /* The error code wasn't in the table. We check for a range of */ 172 /* osl_File_E_ACCES errors or exec failure errors (ENOEXEC). Otherwise */ 173 /* osl_File_E_INVAL is returned. */ 174 175 if ( dwError >= MIN_EACCES_RANGE && dwError <= MAX_EACCES_RANGE) 176 return osl_File_E_ACCES; 177 else if ( dwError >= MIN_EXEC_ERROR && dwError <= MAX_EXEC_ERROR) 178 return osl_File_E_NOEXEC; 179 else 180 return osl_File_E_INVAL; 181 } 182 183 #define MapError( oserror ) _mapError( oserror ) 184 185 #define E_UNKNOWN_EXEC_ERROR -1 186 187 //----------------------------------------- 188 //----------------------------------------- 189 190 bool is_system_path(const OUString& path_or_uri) 191 { 192 OUString url; 193 osl::FileBase::RC rc = osl::FileBase::getFileURLFromSystemPath(path_or_uri, url); 194 return (rc == osl::FileBase::E_None); 195 } 196 197 //----------------------------------------- 198 // trying to identify a jump mark 199 //----------------------------------------- 200 201 const OUString JUMP_MARK_HTM = OUString::createFromAscii(".htm#"); 202 const OUString JUMP_MARK_HTML = OUString::createFromAscii(".html#"); 203 const sal_Unicode HASH_MARK = (sal_Unicode)'#'; 204 205 bool has_jump_mark(const OUString& system_path, sal_Int32* jmp_mark_start = NULL) 206 { 207 sal_Int32 jmp_mark = std::max<int>( 208 system_path.lastIndexOf(JUMP_MARK_HTM), 209 system_path.lastIndexOf(JUMP_MARK_HTML)); 210 211 if (jmp_mark_start) 212 *jmp_mark_start = jmp_mark; 213 214 return (jmp_mark > -1); 215 } 216 217 //----------------------------------------- 218 //----------------------------------------- 219 220 bool is_existing_file(const OUString& file_name) 221 { 222 OSL_ASSERT(is_system_path(file_name)); 223 224 bool exist = false; 225 226 OUString file_url; 227 osl::FileBase::RC rc = osl::FileBase::getFileURLFromSystemPath(file_name, file_url); 228 229 if (osl::FileBase::E_None == rc) 230 { 231 osl::DirectoryItem dir_item; 232 rc = osl::DirectoryItem::get(file_url, dir_item); 233 exist = (osl::FileBase::E_None == rc); 234 } 235 return exist; 236 } 237 238 //------------------------------------------------- 239 // Jump marks in file urls are illegal. 240 //------------------------------------------------- 241 242 void remove_jump_mark(OUString* p_command) 243 { 244 OSL_PRECOND(p_command, "invalid parameter"); 245 246 sal_Int32 pos; 247 if (has_jump_mark(*p_command, &pos)) 248 { 249 const sal_Unicode* p_jmp_mark = p_command->getStr() + pos; 250 while (*p_jmp_mark && (*p_jmp_mark != HASH_MARK)) 251 p_jmp_mark++; 252 253 *p_command = OUString(p_command->getStr(), p_jmp_mark - p_command->getStr()); 254 } 255 } 256 257 } // end namespace 258 259 //----------------------------------------------------------------------------------------- 260 // 261 //----------------------------------------------------------------------------------------- 262 263 CSysShExec::CSysShExec( ) : 264 WeakComponentImplHelper2< XSystemShellExecute, XServiceInfo >( m_aMutex ) 265 { 266 /* 267 * As this service is declared thread-affine, it is ensured to be called from a 268 * dedicated thread, so initialize COM here. 269 * 270 * We need COM to be initialized for STA, but osl thread get initialized for MTA. 271 * Once this changed, we can remove the uninitialize call. 272 */ 273 CoUninitialize(); 274 CoInitialize( NULL ); 275 } 276 277 //------------------------------------------------- 278 // 279 //------------------------------------------------- 280 281 void SAL_CALL CSysShExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags ) 282 throw (IllegalArgumentException, SystemShellExecuteException, RuntimeException) 283 { 284 // parameter checking 285 if (0 == aCommand.getLength()) 286 throw IllegalArgumentException( 287 OUString::createFromAscii( "Empty command" ), 288 static_cast< XSystemShellExecute* >( this ), 289 1 ); 290 291 if (!(nFlags >= DEFAULTS && nFlags <= NO_SYSTEM_ERROR_MESSAGE)) 292 throw IllegalArgumentException( 293 OUString::createFromAscii( "Invalid Flags specified" ), 294 static_cast< XSystemShellExecute* >( this ), 295 3 ); 296 297 /* #i4789#; jump mark detection on system paths 298 if the given command is a system path (not http or 299 other uri schemes) and seems to have a jump mark 300 and names no existing file (remeber the jump mark 301 sign '#' is a valid file name character we remove 302 the jump mark, else ShellExecuteEx fails */ 303 OUString preprocessed_command(aCommand); 304 if (is_system_path(preprocessed_command)) 305 { 306 if (has_jump_mark(preprocessed_command) && !is_existing_file(preprocessed_command)) 307 remove_jump_mark(&preprocessed_command); 308 } 309 /* Convert file uris to system paths */ 310 else 311 { 312 OUString aSystemPath; 313 if (::osl::FileBase::E_None == ::osl::FileBase::getSystemPathFromFileURL(preprocessed_command, aSystemPath)) 314 preprocessed_command = aSystemPath; 315 } 316 317 SHELLEXECUTEINFOW sei; 318 ZeroMemory(&sei, sizeof( sei)); 319 320 sei.cbSize = sizeof(sei); 321 sei.lpFile = reinterpret_cast<LPCWSTR>(preprocessed_command.getStr()); 322 sei.lpParameters = reinterpret_cast<LPCWSTR>(aParameter.getStr()); 323 sei.nShow = SW_SHOWNORMAL; 324 325 if (NO_SYSTEM_ERROR_MESSAGE & nFlags) 326 sei.fMask = SEE_MASK_FLAG_NO_UI; 327 328 SetLastError( 0 ); 329 330 sal_Bool bRet = ShellExecuteExW(&sei) ? sal_True : sal_False; 331 332 if (!bRet && (nFlags & NO_SYSTEM_ERROR_MESSAGE)) 333 { 334 // ShellExecuteEx fails to set an error code 335 // we return osl_File_E_INVAL 336 sal_Int32 psxErr = GetLastError(); 337 if (ERROR_SUCCESS == psxErr) 338 psxErr = E_UNKNOWN_EXEC_ERROR; 339 else 340 psxErr = MapError(psxErr); 341 342 throw SystemShellExecuteException( 343 OUString::createFromAscii("Error executing command"), 344 static_cast< XSystemShellExecute* >(this), 345 psxErr); 346 } 347 } 348 349 // ------------------------------------------------- 350 // XServiceInfo 351 // ------------------------------------------------- 352 353 OUString SAL_CALL CSysShExec::getImplementationName( ) 354 throw( RuntimeException ) 355 { 356 return OUString::createFromAscii( SYSSHEXEC_IMPL_NAME ); 357 } 358 359 // ------------------------------------------------- 360 // XServiceInfo 361 // ------------------------------------------------- 362 363 sal_Bool SAL_CALL CSysShExec::supportsService( const OUString& ServiceName ) 364 throw( RuntimeException ) 365 { 366 Sequence < OUString > SupportedServicesNames = SysShExec_getSupportedServiceNames(); 367 368 for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; ) 369 if (SupportedServicesNames[n].compareTo(ServiceName) == 0) 370 return sal_True; 371 372 return sal_False; 373 } 374 375 // ------------------------------------------------- 376 // XServiceInfo 377 // ------------------------------------------------- 378 379 Sequence< OUString > SAL_CALL CSysShExec::getSupportedServiceNames( ) 380 throw( RuntimeException ) 381 { 382 return SysShExec_getSupportedServiceNames(); 383 } 384 385