1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_desktop.hxx" 30 31 32 #include "dp_misc.h" 33 #include "dp_version.hxx" 34 #include "dp_interact.h" 35 #include "rtl/uri.hxx" 36 #include "rtl/digest.h" 37 #include "rtl/random.h" 38 #include "rtl/bootstrap.hxx" 39 #include "unotools/bootstrap.hxx" 40 #include "osl/file.hxx" 41 #include "osl/pipe.hxx" 42 #include "osl/security.hxx" 43 #include "osl/thread.hxx" 44 #include "osl/mutex.hxx" 45 #include "com/sun/star/ucb/CommandAbortedException.hpp" 46 #include "com/sun/star/task/XInteractionHandler.hpp" 47 #include "com/sun/star/bridge/UnoUrlResolver.hpp" 48 #include "com/sun/star/bridge/XUnoUrlResolver.hpp" 49 #include "com/sun/star/deployment/ExtensionManager.hpp" 50 #include "com/sun/star/task/XRestartManager.hpp" 51 #include "boost/scoped_array.hpp" 52 #include "boost/shared_ptr.hpp" 53 #include <comphelper/processfactory.hxx> 54 55 #ifdef WNT 56 //#include "tools/prewin.h" 57 #define UNICODE 58 #define _UNICODE 59 #define WIN32_LEAN_AND_MEAN 60 #include <Windows.h> 61 //#include "tools/postwin.h" 62 #endif 63 64 using namespace ::com::sun::star; 65 using namespace ::com::sun::star::uno; 66 using ::rtl::OUString; 67 using ::rtl::OString; 68 69 70 #define SOFFICE1 "soffice.exe" 71 #define SOFFICE2 "soffice.bin" 72 #define SBASE "sbase.exe" 73 #define SCALC "scalc.exe" 74 #define SDRAW "sdraw.exe" 75 #define SIMPRESS "simpress.exe" 76 #define SWRITER "swriter.exe" 77 78 namespace dp_misc { 79 namespace { 80 81 struct UnoRc : public rtl::StaticWithInit< 82 const boost::shared_ptr<rtl::Bootstrap>, UnoRc> { 83 const boost::shared_ptr<rtl::Bootstrap> operator () () { 84 OUString unorc( RTL_CONSTASCII_USTRINGPARAM( 85 "$OOO_BASE_DIR/program/" SAL_CONFIGFILE("uno")) ); 86 ::rtl::Bootstrap::expandMacros( unorc ); 87 ::boost::shared_ptr< ::rtl::Bootstrap > ret( 88 new ::rtl::Bootstrap( unorc ) ); 89 OSL_ASSERT( ret->getHandle() != 0 ); 90 return ret; 91 } 92 }; 93 94 struct OfficePipeId : public rtl::StaticWithInit<const OUString, OfficePipeId> { 95 const OUString operator () (); 96 }; 97 98 const OUString OfficePipeId::operator () () 99 { 100 OUString userPath; 101 ::utl::Bootstrap::PathStatus aLocateResult = 102 ::utl::Bootstrap::locateUserInstallation( userPath ); 103 if (!(aLocateResult == ::utl::Bootstrap::PATH_EXISTS || 104 aLocateResult == ::utl::Bootstrap::PATH_VALID)) 105 { 106 throw Exception(OUSTR("Extension Manager: Could not obtain path for UserInstallation."), 0); 107 } 108 109 rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 ); 110 if (digest <= 0) { 111 throw RuntimeException( 112 OUSTR("cannot get digest rtl_Digest_AlgorithmMD5!"), 0 ); 113 } 114 115 sal_uInt8 const * data = 116 reinterpret_cast<sal_uInt8 const *>(userPath.getStr()); 117 sal_Size size = (userPath.getLength() * sizeof (sal_Unicode)); 118 sal_uInt32 md5_key_len = rtl_digest_queryLength( digest ); 119 ::boost::scoped_array<sal_uInt8> md5_buf( new sal_uInt8 [ md5_key_len ] ); 120 121 rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) ); 122 rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) ); 123 rtl_digest_get( digest, md5_buf.get(), md5_key_len ); 124 rtl_digest_destroy( digest ); 125 126 // create hex-value string from the MD5 value to keep 127 // the string size minimal 128 ::rtl::OUStringBuffer buf; 129 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("SingleOfficeIPC_") ); 130 for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) { 131 buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 ); 132 } 133 return buf.makeStringAndClear(); 134 } 135 136 bool existsOfficePipe() 137 { 138 OUString const & pipeId = OfficePipeId::get(); 139 if (pipeId.getLength() == 0) 140 return false; 141 ::osl::Security sec; 142 ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec ); 143 return pipe.is(); 144 } 145 146 147 //Returns true if the Folder was more recently modified then 148 //the lastsynchronized file. That is the repository needs to 149 //be synchronized. 150 bool compareExtensionFolderWithLastSynchronizedFile( 151 OUString const & folderURL, OUString const & fileURL) 152 { 153 bool bNeedsSync = false; 154 ::osl::DirectoryItem itemExtFolder; 155 ::osl::File::RC err1 = 156 ::osl::DirectoryItem::get(folderURL, itemExtFolder); 157 //If it does not exist, then there is nothing to be done 158 if (err1 == ::osl::File::E_NOENT) 159 { 160 return false; 161 } 162 else if (err1 != ::osl::File::E_None) 163 { 164 OSL_ENSURE(0, "Cannot access extension folder"); 165 return true; //sync just in case 166 } 167 168 //If last synchronized does not exist, then OOo is started for the first time 169 ::osl::DirectoryItem itemFile; 170 ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile); 171 if (err2 == ::osl::File::E_NOENT) 172 { 173 return true; 174 175 } 176 else if (err2 != ::osl::File::E_None) 177 { 178 OSL_ENSURE(0, "Cannot access file lastsynchronized"); 179 return true; //sync just in case 180 } 181 182 //compare the modification time of the extension folder and the last 183 //modified file 184 ::osl::FileStatus statFolder(FileStatusMask_ModifyTime); 185 ::osl::FileStatus statFile(FileStatusMask_ModifyTime); 186 if (itemExtFolder.getFileStatus(statFolder) == ::osl::File::E_None) 187 { 188 if (itemFile.getFileStatus(statFile) == ::osl::File::E_None) 189 { 190 TimeValue timeFolder = statFolder.getModifyTime(); 191 TimeValue timeFile = statFile.getModifyTime(); 192 193 if (timeFile.Seconds < timeFolder.Seconds) 194 bNeedsSync = true; 195 } 196 else 197 { 198 OSL_ASSERT(0); 199 bNeedsSync = true; 200 } 201 } 202 else 203 { 204 OSL_ASSERT(0); 205 bNeedsSync = true; 206 } 207 return bNeedsSync; 208 } 209 210 bool needToSyncRepostitory(OUString const & name) 211 { 212 OUString folder; 213 OUString file; 214 if (name.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("bundled")))) 215 { 216 folder = OUString( 217 RTL_CONSTASCII_USTRINGPARAM("$BUNDLED_EXTENSIONS")); 218 file = OUString ( 219 RTL_CONSTASCII_USTRINGPARAM( 220 "$BUNDLED_EXTENSIONS_USER/lastsynchronized")); 221 } 222 else if (name.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("shared")))) 223 { 224 folder = OUString( 225 RTL_CONSTASCII_USTRINGPARAM( 226 "$UNO_SHARED_PACKAGES_CACHE/uno_packages")); 227 file = OUString ( 228 RTL_CONSTASCII_USTRINGPARAM( 229 "$SHARED_EXTENSIONS_USER/lastsynchronized")); 230 } 231 else 232 { 233 OSL_ASSERT(0); 234 return true; 235 } 236 ::rtl::Bootstrap::expandMacros(folder); 237 ::rtl::Bootstrap::expandMacros(file); 238 return compareExtensionFolderWithLastSynchronizedFile( 239 folder, file); 240 } 241 242 243 } // anon namespace 244 245 //============================================================================== 246 247 namespace { 248 inline OUString encodeForRcFile( OUString const & str ) 249 { 250 // escape $\{} (=> rtl bootstrap files) 251 ::rtl::OUStringBuffer buf; 252 sal_Int32 pos = 0; 253 const sal_Int32 len = str.getLength(); 254 for ( ; pos < len; ++pos ) { 255 sal_Unicode c = str[ pos ]; 256 switch (c) { 257 case '$': 258 case '\\': 259 case '{': 260 case '}': 261 buf.append( static_cast<sal_Unicode>('\\') ); 262 break; 263 } 264 buf.append( c ); 265 } 266 return buf.makeStringAndClear(); 267 } 268 } 269 270 //============================================================================== 271 OUString makeURL( OUString const & baseURL, OUString const & relPath_ ) 272 { 273 ::rtl::OUStringBuffer buf; 274 if (baseURL.getLength() > 1 && baseURL[ baseURL.getLength() - 1 ] == '/') 275 buf.append( baseURL.copy( 0, baseURL.getLength() - 1 ) ); 276 else 277 buf.append( baseURL ); 278 OUString relPath(relPath_); 279 if (relPath.getLength() > 0 && relPath[ 0 ] == '/') 280 relPath = relPath.copy( 1 ); 281 if (relPath.getLength() > 0) 282 { 283 buf.append( static_cast<sal_Unicode>('/') ); 284 if (baseURL.matchAsciiL( 285 RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:") )) { 286 // encode for macro expansion: relPath is supposed to have no 287 // macros, so encode $, {} \ (bootstrap mimic) 288 relPath = encodeForRcFile(relPath); 289 290 // encode once more for vnd.sun.star.expand schema: 291 // vnd.sun.star.expand:$UNO_... 292 // will expand to file-url 293 relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric, 294 rtl_UriEncodeIgnoreEscapes, 295 RTL_TEXTENCODING_UTF8 ); 296 } 297 buf.append( relPath ); 298 } 299 return buf.makeStringAndClear(); 300 } 301 302 OUString makeURLAppendSysPathSegment( OUString const & baseURL, OUString const & relPath_ ) 303 { 304 OUString segment = relPath_; 305 OSL_ASSERT(segment.indexOf(static_cast<sal_Unicode>('/')) == -1); 306 307 ::rtl::Uri::encode( 308 segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, 309 RTL_TEXTENCODING_UTF8); 310 return makeURL(baseURL, segment); 311 } 312 313 314 315 //============================================================================== 316 OUString expandUnoRcTerm( OUString const & term_ ) 317 { 318 OUString term(term_); 319 UnoRc::get()->expandMacrosFrom( term ); 320 return term; 321 } 322 323 OUString makeRcTerm( OUString const & url ) 324 { 325 OSL_ASSERT( url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( 326 "vnd.sun.star.expand:") ) ); 327 if (url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:") )) { 328 // cut protocol: 329 OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) ); 330 // decode uric class chars: 331 rcterm = ::rtl::Uri::decode( 332 rcterm, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 ); 333 return rcterm; 334 } 335 else 336 return url; 337 } 338 339 //============================================================================== 340 OUString expandUnoRcUrl( OUString const & url ) 341 { 342 if (url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:") )) { 343 // cut protocol: 344 OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) ); 345 // decode uric class chars: 346 rcurl = ::rtl::Uri::decode( 347 rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 ); 348 // expand macro string: 349 UnoRc::get()->expandMacrosFrom( rcurl ); 350 return rcurl; 351 } 352 else { 353 return url; 354 } 355 } 356 357 //============================================================================== 358 bool office_is_running() 359 { 360 //We need to check if we run within the office process. Then we must not use the pipe, because 361 //this could cause a deadlock. This is actually a workaround for i82778 362 OUString sFile; 363 oslProcessError err = osl_getExecutableFile(& sFile.pData); 364 bool ret = false; 365 if (osl_Process_E_None == err) 366 { 367 sFile = sFile.copy(sFile.lastIndexOf('/') + 1); 368 if ( 369 #if defined UNIX 370 sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SOFFICE2))) 371 #elif defined WNT || defined OS2 372 //osl_getExecutableFile should deliver "soffice.bin" on windows 373 //even if swriter.exe, scalc.exe etc. was started. This is a bug 374 //in osl_getExecutableFile 375 sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SOFFICE1))) 376 || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SOFFICE2))) 377 || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SBASE))) 378 || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SCALC))) 379 || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SDRAW))) 380 || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SIMPRESS))) 381 || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SWRITER))) 382 #else 383 #error "Unsupported platform" 384 #endif 385 386 ) 387 ret = true; 388 else 389 ret = existsOfficePipe(); 390 } 391 else 392 { 393 OSL_ENSURE(0, "NOT osl_Process_E_None "); 394 //if osl_getExecutable file than we take the risk of creating a pipe 395 ret = existsOfficePipe(); 396 } 397 return ret; 398 } 399 400 //============================================================================== 401 oslProcess raiseProcess( 402 OUString const & appURL, Sequence<OUString> const & args ) 403 { 404 ::osl::Security sec; 405 oslProcess hProcess = 0; 406 oslProcessError rc = osl_executeProcess( 407 appURL.pData, 408 reinterpret_cast<rtl_uString **>( 409 const_cast<OUString *>(args.getConstArray()) ), 410 args.getLength(), 411 osl_Process_DETACHED, 412 sec.getHandle(), 413 0, // => current working dir 414 0, 0, // => no env vars 415 &hProcess ); 416 417 switch (rc) { 418 case osl_Process_E_None: 419 break; 420 case osl_Process_E_NotFound: 421 throw RuntimeException( OUSTR("image not found!"), 0 ); 422 case osl_Process_E_TimedOut: 423 throw RuntimeException( OUSTR("timout occured!"), 0 ); 424 case osl_Process_E_NoPermission: 425 throw RuntimeException( OUSTR("permission denied!"), 0 ); 426 case osl_Process_E_Unknown: 427 throw RuntimeException( OUSTR("unknown error!"), 0 ); 428 case osl_Process_E_InvalidError: 429 default: 430 throw RuntimeException( OUSTR("unmapped error!"), 0 ); 431 } 432 433 return hProcess; 434 } 435 436 //============================================================================== 437 OUString generateRandomPipeId() 438 { 439 // compute some good pipe id: 440 static rtlRandomPool s_hPool = rtl_random_createPool(); 441 if (s_hPool == 0) 442 throw RuntimeException( OUSTR("cannot create random pool!?"), 0 ); 443 sal_uInt8 bytes[ 32 ]; 444 if (rtl_random_getBytes( 445 s_hPool, bytes, ARLEN(bytes) ) != rtl_Random_E_None) { 446 throw RuntimeException( OUSTR("random pool error!?"), 0 ); 447 } 448 ::rtl::OUStringBuffer buf; 449 for ( sal_uInt32 i = 0; i < ARLEN(bytes); ++i ) { 450 buf.append( static_cast<sal_Int32>(bytes[ i ]), 0x10 ); 451 } 452 return buf.makeStringAndClear(); 453 } 454 455 //============================================================================== 456 Reference<XInterface> resolveUnoURL( 457 OUString const & connectString, 458 Reference<XComponentContext> const & xLocalContext, 459 AbortChannel * abortChannel ) 460 { 461 Reference<bridge::XUnoUrlResolver> xUnoUrlResolver( 462 bridge::UnoUrlResolver::create( xLocalContext ) ); 463 464 for (;;) 465 { 466 if (abortChannel != 0 && abortChannel->isAborted()) { 467 throw ucb::CommandAbortedException( 468 OUSTR("abort!"), Reference<XInterface>() ); 469 } 470 try { 471 return xUnoUrlResolver->resolve( connectString ); 472 } 473 catch (connection::NoConnectException &) { 474 TimeValue tv = { 0 /* secs */, 500000000 /* nanosecs */ }; 475 ::osl::Thread::wait( tv ); 476 } 477 } 478 } 479 480 #ifdef WNT 481 void writeConsoleWithStream(::rtl::OUString const & sText, HANDLE stream) 482 { 483 DWORD nWrittenChars = 0; 484 WriteFile(stream, sText.getStr(), 485 sText.getLength() * 2, &nWrittenChars, NULL); 486 } 487 #else 488 void writeConsoleWithStream(::rtl::OUString const & sText, FILE * stream) 489 { 490 OString s = OUStringToOString(sText, osl_getThreadTextEncoding()); 491 fprintf(stream, "%s", s.getStr()); 492 fflush(stream); 493 } 494 #endif 495 496 #ifdef WNT 497 void writeConsoleWithStream(::rtl::OString const & sText, HANDLE stream) 498 { 499 writeConsoleWithStream(OStringToOUString( 500 sText, RTL_TEXTENCODING_UTF8), stream); 501 } 502 #else 503 void writeConsoleWithStream(::rtl::OString const & sText, FILE * stream) 504 { 505 fprintf(stream, "%s", sText.getStr()); 506 fflush(stream); 507 } 508 #endif 509 510 void writeConsole(::rtl::OUString const & sText) 511 { 512 #ifdef WNT 513 writeConsoleWithStream(sText, GetStdHandle(STD_OUTPUT_HANDLE)); 514 #else 515 writeConsoleWithStream(sText, stdout); 516 #endif 517 } 518 519 void writeConsole(::rtl::OString const & sText) 520 { 521 #ifdef WNT 522 writeConsoleWithStream(sText, GetStdHandle(STD_OUTPUT_HANDLE)); 523 #else 524 writeConsoleWithStream(sText, stdout); 525 #endif 526 } 527 528 void writeConsoleError(::rtl::OUString const & sText) 529 { 530 #ifdef WNT 531 writeConsoleWithStream(sText, GetStdHandle(STD_ERROR_HANDLE)); 532 #else 533 writeConsoleWithStream(sText, stderr); 534 #endif 535 } 536 537 538 void writeConsoleError(::rtl::OString const & sText) 539 { 540 #ifdef WNT 541 writeConsoleWithStream(sText, GetStdHandle(STD_ERROR_HANDLE)); 542 #else 543 writeConsoleWithStream(sText, stderr); 544 #endif 545 } 546 547 548 549 OUString readConsole() 550 { 551 #ifdef WNT 552 sal_Unicode aBuffer[1024]; 553 DWORD dwRead = 0; 554 //unopkg.com feeds unopkg.exe with wchar_t|s 555 if (ReadFile( GetStdHandle(STD_INPUT_HANDLE), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 556 { 557 OSL_ASSERT((dwRead % 2) == 0); 558 OUString value( aBuffer, dwRead / 2); 559 return value.trim(); 560 } 561 #else 562 char buf[1024]; 563 rtl_zeroMemory(buf, 1024); 564 // read one char less so that the last char in buf is always zero 565 if (fgets(buf, 1024, stdin) != NULL) 566 { 567 OUString value = ::rtl::OStringToOUString(::rtl::OString(buf), osl_getThreadTextEncoding()); 568 return value.trim(); 569 } 570 #endif 571 return OUString(); 572 } 573 574 void TRACE(::rtl::OUString const & sText) 575 { 576 (void) sText; 577 #if OSL_DEBUG_LEVEL > 1 578 writeConsole(sText); 579 #endif 580 } 581 582 void TRACE(::rtl::OString const & sText) 583 { 584 (void) sText; 585 #if OSL_DEBUG_LEVEL > 1 586 writeConsole(sText); 587 #endif 588 } 589 590 void syncRepositories(Reference<ucb::XCommandEnvironment> const & xCmdEnv) 591 { 592 OUString sDisable; 593 ::rtl::Bootstrap::get( OUSTR( "DISABLE_EXTENSION_SYNCHRONIZATION" ), sDisable, OUString() ); 594 if (sDisable.getLength() > 0) 595 return; 596 597 Reference<deployment::XExtensionManager> xExtensionManager; 598 //synchronize shared before bundled otherewise there are 599 //more revoke and registration calls. 600 sal_Bool bModified = false; 601 if (needToSyncRepostitory(OUString(RTL_CONSTASCII_USTRINGPARAM("shared"))) 602 || needToSyncRepostitory(OUString(RTL_CONSTASCII_USTRINGPARAM("bundled")))) 603 { 604 xExtensionManager = 605 deployment::ExtensionManager::get( 606 comphelper_getProcessComponentContext()); 607 608 if (xExtensionManager.is()) 609 { 610 bModified = xExtensionManager->synchronize( 611 Reference<task::XAbortChannel>(), xCmdEnv); 612 } 613 } 614 615 if (bModified) 616 { 617 Reference<task::XRestartManager> restarter( 618 comphelper_getProcessComponentContext()->getValueByName( 619 OUSTR( "/singletons/com.sun.star.task.OfficeRestartManager") ), UNO_QUERY ); 620 if (restarter.is()) 621 { 622 restarter->requestRestart(xCmdEnv.is() == sal_True ? xCmdEnv->getInteractionHandler() : 623 Reference<task::XInteractionHandler>()); 624 } 625 } 626 } 627 628 629 630 } 631