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