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