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 #include "deployment.hrc" 32 #include "unopkg_shared.h" 33 #include "dp_identifier.hxx" 34 #include "../../deployment/gui/dp_gui.hrc" 35 #include "../../app/lockfile.hxx" 36 #include "vcl/svapp.hxx" 37 #include "vcl/msgbox.hxx" 38 #include "rtl/bootstrap.hxx" 39 #include "rtl/strbuf.hxx" 40 #include "rtl/ustrbuf.hxx" 41 #include "osl/process.h" 42 #include "osl/file.hxx" 43 #include "osl/thread.hxx" 44 #include "tools/getprocessworkingdir.hxx" 45 #include "ucbhelper/contentbroker.hxx" 46 #include "ucbhelper/configurationkeys.hxx" 47 #include "unotools/processfactory.hxx" 48 #include "unotools/configmgr.hxx" 49 #include "com/sun/star/lang/XMultiServiceFactory.hpp" 50 #include "cppuhelper/bootstrap.hxx" 51 #include "comphelper/sequence.hxx" 52 #include <stdio.h> 53 54 using ::rtl::OUString; 55 using ::rtl::OString; 56 using namespace ::com::sun::star; 57 using namespace ::com::sun::star::uno; 58 using namespace ::com::sun::star::ucb; 59 60 namespace unopkg { 61 62 bool getLockFilePath(OUString & out); 63 64 ::rtl::OUString toString( OptionInfo const * info ) 65 { 66 OSL_ASSERT( info != 0 ); 67 ::rtl::OUStringBuffer buf; 68 buf.appendAscii("--"); 69 buf.appendAscii(info->m_name); 70 if (info->m_short_option != '\0') 71 { 72 buf.appendAscii(" (short -" ); 73 buf.append(info->m_short_option ); 74 buf.appendAscii(")"); 75 } 76 if (info->m_has_argument) 77 buf.appendAscii(" <argument>" ); 78 return buf.makeStringAndClear(); 79 } 80 81 //============================================================================== 82 OptionInfo const * getOptionInfo( 83 OptionInfo const * list, 84 OUString const & opt, sal_Unicode copt ) 85 { 86 for ( ; list->m_name != 0; ++list ) 87 { 88 OptionInfo const & option_info = *list; 89 if (opt.getLength() > 0) 90 { 91 if (opt.equalsAsciiL( 92 option_info.m_name, option_info.m_name_length ) && 93 (copt == '\0' || copt == option_info.m_short_option)) 94 { 95 return &option_info; 96 } 97 } 98 else 99 { 100 OSL_ASSERT( copt != '\0' ); 101 if (copt == option_info.m_short_option) 102 { 103 return &option_info; 104 } 105 } 106 } 107 OSL_ENSURE( 0, ::rtl::OUStringToOString( 108 opt, osl_getThreadTextEncoding() ).getStr() ); 109 return 0; 110 } 111 112 //============================================================================== 113 bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex ) 114 { 115 OSL_ASSERT( option_info != 0 ); 116 if (osl_getCommandArgCount() <= *pIndex) 117 return false; 118 119 OUString arg; 120 osl_getCommandArg( *pIndex, &arg.pData ); 121 sal_Int32 len = arg.getLength(); 122 123 if (len < 2 || arg[ 0 ] != '-') 124 return false; 125 126 if (len == 2 && arg[ 1 ] == option_info->m_short_option) 127 { 128 ++(*pIndex); 129 dp_misc::TRACE(OUSTR(__FILE__": identified option \'") 130 + OUSTR("\'") + OUString( option_info->m_short_option ) + OUSTR("\n")); 131 return true; 132 } 133 if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare( 134 arg.pData->buffer + 2, option_info->m_name ) == 0) 135 { 136 ++(*pIndex); 137 dp_misc::TRACE(OUSTR( __FILE__": identified option \'") 138 + OUString::createFromAscii(option_info->m_name) + OUSTR("\'\n")); 139 return true; 140 } 141 return false; 142 } 143 //============================================================================== 144 145 bool isBootstrapVariable(sal_uInt32 * pIndex) 146 { 147 OSL_ASSERT(osl_getCommandArgCount() >= *pIndex); 148 149 OUString arg; 150 osl_getCommandArg(*pIndex, &arg.pData); 151 if (arg.matchAsciiL("-env:", 5)) 152 { 153 ++(*pIndex); 154 return true; 155 } 156 return false; 157 } 158 159 //============================================================================== 160 bool readArgument( 161 OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex ) 162 { 163 if (isOption( option_info, pIndex )) 164 { 165 if (*pIndex < osl_getCommandArgCount()) 166 { 167 OSL_ASSERT( pValue != 0 ); 168 osl_getCommandArg( *pIndex, &pValue->pData ); 169 dp_misc::TRACE(OUSTR( __FILE__": argument value: ") 170 + *pValue + OUSTR("\n")); 171 ++(*pIndex); 172 return true; 173 } 174 --(*pIndex); 175 } 176 return false; 177 } 178 179 //############################################################################## 180 181 namespace { 182 struct ExecutableDir : public rtl::StaticWithInit< 183 const OUString, ExecutableDir> { 184 const OUString operator () () { 185 OUString path; 186 if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) { 187 throw RuntimeException( 188 OUSTR("cannot locate executable directory!"),0 ); 189 } 190 return path.copy( 0, path.lastIndexOf( '/' ) ); 191 } 192 }; 193 struct ProcessWorkingDir : public rtl::StaticWithInit< 194 const OUString, ProcessWorkingDir> { 195 const OUString operator () () { 196 OUString workingDir; 197 tools::getProcessWorkingDir(&workingDir); 198 return workingDir; 199 } 200 }; 201 } // anon namespace 202 203 //============================================================================== 204 OUString const & getExecutableDir() 205 { 206 return ExecutableDir::get(); 207 } 208 209 //============================================================================== 210 OUString const & getProcessWorkingDir() 211 { 212 return ProcessWorkingDir::get(); 213 } 214 215 //============================================================================== 216 OUString makeAbsoluteFileUrl( 217 OUString const & sys_path, OUString const & base_url, bool throw_exc ) 218 { 219 // system path to file url 220 OUString file_url; 221 oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData ); 222 if ( rc != osl_File_E_None) { 223 OUString tempPath; 224 if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) == osl_File_E_None ) 225 { 226 file_url = sys_path; 227 } 228 else if (throw_exc) 229 { 230 throw RuntimeException( 231 OUSTR("cannot get file url from system path: ") + 232 sys_path, Reference< XInterface >() ); 233 } 234 } 235 236 OUString abs; 237 if (osl_getAbsoluteFileURL( 238 base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None) 239 { 240 if (throw_exc) { 241 ::rtl::OUStringBuffer buf; 242 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( 243 "making absolute file url failed: \"") ); 244 buf.append( base_url ); 245 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( 246 "\" (base-url) and \"") ); 247 buf.append( file_url ); 248 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" (file-url)!") ); 249 throw RuntimeException( 250 buf.makeStringAndClear(), Reference< XInterface >() ); 251 } 252 return OUString(); 253 } 254 return abs[ abs.getLength() -1 ] == '/' 255 ? abs.copy( 0, abs.getLength() -1 ) : abs; 256 } 257 258 //############################################################################## 259 260 namespace { 261 262 //------------------------------------------------------------------------------ 263 inline void printf_space( sal_Int32 space ) 264 { 265 while (space--) 266 dp_misc::writeConsole(" "); 267 } 268 269 //------------------------------------------------------------------------------ 270 void printf_line( 271 OUString const & name, OUString const & value, sal_Int32 level ) 272 { 273 printf_space( level ); 274 dp_misc::writeConsole(name + OUSTR(": ") + value + OUSTR("\n")); 275 } 276 277 //------------------------------------------------------------------------------ 278 void printf_package( 279 Reference<deployment::XPackage> const & xPackage, 280 Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level ) 281 { 282 beans::Optional< OUString > id( 283 level == 0 284 ? beans::Optional< OUString >( 285 true, dp_misc::getIdentifier( xPackage ) ) 286 : xPackage->getIdentifier() ); 287 if (id.IsPresent) 288 printf_line( OUSTR("Identifier"), id.Value, level ); 289 OUString version(xPackage->getVersion()); 290 if (version.getLength() != 0) 291 printf_line( OUSTR("Version"), version, level + 1 ); 292 printf_line( OUSTR("URL"), xPackage->getURL(), level + 1 ); 293 294 beans::Optional< beans::Ambiguous<sal_Bool> > option( 295 xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) ); 296 OUString value; 297 if (option.IsPresent) { 298 beans::Ambiguous<sal_Bool> const & reg = option.Value; 299 if (reg.IsAmbiguous) 300 value = OUSTR("unknown"); 301 else 302 value = reg.Value ? OUSTR("yes") : OUSTR("no"); 303 } 304 else 305 value = OUSTR("n/a"); 306 printf_line( OUSTR("is registered"), value, level + 1 ); 307 308 const Reference<deployment::XPackageTypeInfo> xPackageType( 309 xPackage->getPackageType() ); 310 OSL_ASSERT( xPackageType.is() ); 311 if (xPackageType.is()) { 312 printf_line( OUSTR("Media-Type"), 313 xPackageType->getMediaType(), level + 1 ); 314 } 315 printf_line( OUSTR("Description"), xPackage->getDescription(), level + 1 ); 316 if (xPackage->isBundle()) { 317 Sequence< Reference<deployment::XPackage> > seq( 318 xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) ); 319 printf_space( level + 1 ); 320 dp_misc::writeConsole("bundled Packages: {\n"); 321 ::std::vector<Reference<deployment::XPackage> >vec_bundle; 322 ::comphelper::sequenceToContainer(vec_bundle, seq); 323 printf_packages( vec_bundle, ::std::vector<bool>(vec_bundle.size()), 324 xCmdEnv, level + 2 ); 325 printf_space( level + 1 ); 326 dp_misc::writeConsole("}\n"); 327 } 328 } 329 330 } // anon namespace 331 332 void printf_unaccepted_licenses( 333 Reference<deployment::XPackage> const & ext) 334 { 335 OUString id( 336 dp_misc::getIdentifier(ext) ); 337 printf_line( OUSTR("Identifier"), id, 0 ); 338 printf_space(1); 339 dp_misc::writeConsole(OUSTR("License not accepted\n\n")); 340 } 341 342 //============================================================================== 343 void printf_packages( 344 ::std::vector< Reference<deployment::XPackage> > const & allExtensions, 345 ::std::vector<bool> const & vecUnaccepted, 346 Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level ) 347 { 348 OSL_ASSERT(allExtensions.size() == vecUnaccepted.size()); 349 350 if (allExtensions.size() == 0) 351 { 352 printf_space( level ); 353 dp_misc::writeConsole("<none>\n"); 354 } 355 else 356 { 357 typedef ::std::vector< Reference<deployment::XPackage> >::const_iterator I_EXT; 358 int index = 0; 359 for (I_EXT i = allExtensions.begin(); i != allExtensions.end(); i++, index++) 360 { 361 if (vecUnaccepted[index]) 362 printf_unaccepted_licenses(*i); 363 else 364 printf_package( *i, xCmdEnv, level ); 365 dp_misc::writeConsole(OUSTR("\n")); 366 } 367 } 368 } 369 370 371 //############################################################################## 372 373 namespace { 374 375 //------------------------------------------------------------------------------ 376 Reference<XComponentContext> bootstrapStandAlone( 377 DisposeGuard & disposeGuard, bool /*verbose */) 378 { 379 Reference<XComponentContext> xContext = 380 ::cppu::defaultBootstrap_InitialComponentContext(); 381 382 // assure disposing of local component context: 383 disposeGuard.reset( 384 Reference<lang::XComponent>( xContext, UNO_QUERY ) ); 385 386 Reference<lang::XMultiServiceFactory> xServiceManager( 387 xContext->getServiceManager(), UNO_QUERY_THROW ); 388 // set global process service factory used by unotools config helpers 389 ::utl::setProcessServiceFactory( xServiceManager ); 390 391 // initialize the ucbhelper ucb, 392 // because the package implementation uses it 393 Sequence<Any> ucb_args( 2 ); 394 ucb_args[ 0 ] <<= OUSTR(UCB_CONFIGURATION_KEY1_LOCAL); 395 ucb_args[ 1 ] <<= OUSTR(UCB_CONFIGURATION_KEY2_OFFICE); 396 if (! ::ucbhelper::ContentBroker::initialize( xServiceManager, ucb_args )) 397 throw RuntimeException( OUSTR("cannot initialize UCB!"), 0 ); 398 399 disposeGuard.setDeinitUCB(); 400 return xContext; 401 } 402 403 //------------------------------------------------------------------------------ 404 Reference<XComponentContext> connectToOffice( 405 Reference<XComponentContext> const & xLocalComponentContext, 406 bool verbose ) 407 { 408 Sequence<OUString> args( 3 ); 409 args[ 0 ] = OUSTR("-nologo"); 410 args[ 1 ] = OUSTR("-nodefault"); 411 412 OUString pipeId( ::dp_misc::generateRandomPipeId() ); 413 ::rtl::OUStringBuffer buf; 414 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("-accept=pipe,name=") ); 415 buf.append( pipeId ); 416 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(";urp;") ); 417 args[ 2 ] = buf.makeStringAndClear(); 418 OUString appURL( getExecutableDir() + OUSTR("/soffice") ); 419 420 if (verbose) 421 { 422 dp_misc::writeConsole( 423 OUSTR("Raising process: ") + 424 appURL + 425 OUSTR("\nArguments: -nologo -nodefault ") + 426 args[2] + 427 OUSTR("\n")); 428 } 429 430 ::dp_misc::raiseProcess( appURL, args ); 431 432 if (verbose) 433 dp_misc::writeConsole("Ok. Connecting..."); 434 435 OSL_ASSERT( buf.getLength() == 0 ); 436 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("uno:pipe,name=") ); 437 buf.append( pipeId ); 438 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( 439 ";urp;StarOffice.ComponentContext") ); 440 Reference<XComponentContext> xRet( 441 ::dp_misc::resolveUnoURL( 442 buf.makeStringAndClear(), xLocalComponentContext ), 443 UNO_QUERY_THROW ); 444 if (verbose) 445 dp_misc::writeConsole("Ok.\n"); 446 447 return xRet; 448 } 449 450 } // anon namespace 451 452 /** returns the path to the lock file used by unopkg. 453 @return the path. An empty string signifies an error. 454 */ 455 OUString getLockFilePath() 456 { 457 OUString ret; 458 OUString sBootstrap(RTL_CONSTASCII_USTRINGPARAM("${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}")); 459 rtl::Bootstrap::expandMacros(sBootstrap); 460 OUString sAbs; 461 if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL( 462 sBootstrap, OUSTR(".lock"), sAbs)) 463 { 464 if (::osl::File::E_None == 465 ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap)) 466 { 467 ret = sBootstrap; 468 } 469 } 470 471 return ret; 472 } 473 //============================================================================== 474 Reference<XComponentContext> getUNO( 475 DisposeGuard & disposeGuard, bool verbose, bool shared, bool bGui, 476 Reference<XComponentContext> & out_localContext) 477 { 478 // do not create any user data (for the root user) in --shared mode: 479 if (shared) { 480 rtl::Bootstrap::set( 481 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CFG_CacheUrl")), 482 rtl::OUString()); 483 } 484 485 // hold lock during process runtime: 486 static ::desktop::Lockfile s_lockfile( false /* no IPC server */ ); 487 Reference<XComponentContext> xComponentContext( 488 bootstrapStandAlone( disposeGuard, verbose ) ); 489 out_localContext = xComponentContext; 490 if (::dp_misc::office_is_running()) { 491 xComponentContext.set( 492 connectToOffice( xComponentContext, verbose ) ); 493 } 494 else 495 { 496 if (! s_lockfile.check( 0 )) 497 { 498 String sMsg(ResId(RID_STR_CONCURRENTINSTANCE, *DeploymentResMgr::get())); 499 //Create this string before we call DeInitVCL, because this will kill 500 //the ResMgr 501 String sError(ResId(RID_STR_UNOPKG_ERROR, *DeploymentResMgr::get())); 502 503 sMsg = sMsg + OUSTR("\n") + getLockFilePath(); 504 505 if (bGui) 506 { 507 //We show a message box or print to the console that there 508 //is another instance already running 509 if ( ! InitVCL( Reference<lang::XMultiServiceFactory>( 510 xComponentContext->getServiceManager(), 511 UNO_QUERY_THROW ) )) 512 throw RuntimeException( OUSTR("Cannot initialize VCL!"), 513 NULL ); 514 { 515 WarningBox warn(NULL, WB_OK | WB_DEF_OK, sMsg); 516 warn.SetText(::utl::ConfigManager::GetDirectConfigProperty( 517 ::utl::ConfigManager::PRODUCTNAME).get<OUString>()); 518 warn.SetIcon(0); 519 warn.Execute(); 520 } 521 DeInitVCL(); 522 } 523 524 throw LockFileException( 525 OUSTR("\n") + sError + sMsg + OUSTR("\n")); 526 } 527 } 528 529 return xComponentContext; 530 } 531 532 //Determines if a folder does not contains a folder. 533 //Return false may also mean that the status could not be determined 534 //because some error occurred. 535 bool hasNoFolder(OUString const & folderUrl) 536 { 537 bool ret = false; 538 OUString url = folderUrl; 539 ::rtl::Bootstrap::expandMacros(url); 540 ::osl::Directory dir(url); 541 osl::File::RC rc = dir.open(); 542 if (rc == osl::File::E_None) 543 { 544 bool bFolderExist = false; 545 osl::DirectoryItem i; 546 osl::File::RC rcNext = osl::File::E_None; 547 while ( (rcNext = dir.getNextItem(i)) == osl::File::E_None) 548 { 549 osl::FileStatus stat(FileStatusMask_Type); 550 if (i.getFileStatus(stat) == osl::File::E_None) 551 { 552 if (stat.getFileType() == osl::FileStatus::Directory) 553 { 554 bFolderExist = true; 555 break; 556 } 557 } 558 else 559 { 560 dp_misc::writeConsole( 561 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); 562 break; 563 } 564 i = osl::DirectoryItem(); 565 } 566 567 if (rcNext == osl::File::E_NOENT || 568 rcNext == osl::File::E_None) 569 { 570 if (!bFolderExist) 571 ret = true; 572 } 573 else 574 { 575 dp_misc::writeConsole( 576 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); 577 } 578 579 dir.close(); 580 } 581 else 582 { 583 dp_misc::writeConsole( 584 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); 585 } 586 return ret; 587 } 588 589 void removeFolder(OUString const & folderUrl) 590 { 591 OUString url = folderUrl; 592 ::rtl::Bootstrap::expandMacros(url); 593 ::osl::Directory dir(url); 594 ::osl::File::RC rc = dir.open(); 595 if (rc == osl::File::E_None) 596 { 597 ::osl::DirectoryItem i; 598 ::osl::File::RC rcNext = ::osl::File::E_None; 599 while ( (rcNext = dir.getNextItem(i)) == ::osl::File::E_None) 600 { 601 ::osl::FileStatus stat(FileStatusMask_Type | FileStatusMask_FileURL); 602 if (i.getFileStatus(stat) == ::osl::File::E_None) 603 { 604 ::osl::FileStatus::Type t = stat.getFileType(); 605 if (t == ::osl::FileStatus::Directory) 606 { 607 //remove folder 608 removeFolder(stat.getFileURL()); 609 } 610 else if (t == ::osl::FileStatus::Regular) 611 { 612 //remove file 613 ::osl::File::remove(stat.getFileURL()); 614 } 615 else 616 { 617 OSL_ASSERT(0); 618 } 619 } 620 else 621 { 622 dp_misc::writeConsole( 623 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); 624 break; 625 } 626 i = ::osl::DirectoryItem(); 627 } 628 dir.close(); 629 ::osl::Directory::remove(url); 630 } 631 else if (rc != osl::File::E_NOENT) 632 { 633 dp_misc::writeConsole( 634 OUSTR("unopkg: Error while removing ") + url + OUSTR("\n")); 635 } 636 } 637 638 } 639