| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_desktop.hxx" |
| |
| #include "deployment.hrc" |
| #include "unopkg_shared.h" |
| #include "dp_identifier.hxx" |
| #include "../../deployment/gui/dp_gui.hrc" |
| #include "../../app/lockfile.hxx" |
| #include "vcl/svapp.hxx" |
| #include "vcl/msgbox.hxx" |
| #include "rtl/bootstrap.hxx" |
| #include "rtl/strbuf.hxx" |
| #include "rtl/ustrbuf.hxx" |
| #include "osl/process.h" |
| #include "osl/file.hxx" |
| #include "osl/thread.hxx" |
| #include "tools/getprocessworkingdir.hxx" |
| #include "ucbhelper/contentbroker.hxx" |
| #include "ucbhelper/configurationkeys.hxx" |
| #include "unotools/processfactory.hxx" |
| #include "unotools/configmgr.hxx" |
| #include "com/sun/star/lang/XMultiServiceFactory.hpp" |
| #include "cppuhelper/bootstrap.hxx" |
| #include "comphelper/sequence.hxx" |
| #include <stdio.h> |
| |
| using ::rtl::OUString; |
| using ::rtl::OString; |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::ucb; |
| |
| namespace unopkg { |
| |
| bool getLockFilePath(OUString & out); |
| |
| ::rtl::OUString toString( OptionInfo const * info ) |
| { |
| OSL_ASSERT( info != 0 ); |
| ::rtl::OUStringBuffer buf; |
| buf.appendAscii("--"); |
| buf.appendAscii(info->m_name); |
| if (info->m_short_option != '\0') |
| { |
| buf.appendAscii(" (short -" ); |
| buf.append(info->m_short_option ); |
| buf.appendAscii(")"); |
| } |
| if (info->m_has_argument) |
| buf.appendAscii(" <argument>" ); |
| return buf.makeStringAndClear(); |
| } |
| |
| //============================================================================== |
| OptionInfo const * getOptionInfo( |
| OptionInfo const * list, |
| OUString const & opt, sal_Unicode copt ) |
| { |
| for ( ; list->m_name != 0; ++list ) |
| { |
| OptionInfo const & option_info = *list; |
| if (opt.getLength() > 0) |
| { |
| if (opt.equalsAsciiL( |
| option_info.m_name, option_info.m_name_length ) && |
| (copt == '\0' || copt == option_info.m_short_option)) |
| { |
| return &option_info; |
| } |
| } |
| else |
| { |
| OSL_ASSERT( copt != '\0' ); |
| if (copt == option_info.m_short_option) |
| { |
| return &option_info; |
| } |
| } |
| } |
| OSL_ENSURE( 0, ::rtl::OUStringToOString( |
| opt, osl_getThreadTextEncoding() ).getStr() ); |
| return 0; |
| } |
| |
| //============================================================================== |
| bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex ) |
| { |
| OSL_ASSERT( option_info != 0 ); |
| if (osl_getCommandArgCount() <= *pIndex) |
| return false; |
| |
| OUString arg; |
| osl_getCommandArg( *pIndex, &arg.pData ); |
| sal_Int32 len = arg.getLength(); |
| |
| if (len < 2 || arg[ 0 ] != '-') |
| return false; |
| |
| if (len == 2 && arg[ 1 ] == option_info->m_short_option) |
| { |
| ++(*pIndex); |
| dp_misc::TRACE(OUSTR(__FILE__": identified option \'") |
| + OUSTR("\'") + OUString( option_info->m_short_option ) + OUSTR("\n")); |
| return true; |
| } |
| if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare( |
| arg.pData->buffer + 2, option_info->m_name ) == 0) |
| { |
| ++(*pIndex); |
| dp_misc::TRACE(OUSTR( __FILE__": identified option \'") |
| + OUString::createFromAscii(option_info->m_name) + OUSTR("\'\n")); |
| return true; |
| } |
| return false; |
| } |
| //============================================================================== |
| |
| bool isBootstrapVariable(sal_uInt32 * pIndex) |
| { |
| OSL_ASSERT(osl_getCommandArgCount() >= *pIndex); |
| |
| OUString arg; |
| osl_getCommandArg(*pIndex, &arg.pData); |
| if (arg.matchAsciiL("-env:", 5)) |
| { |
| ++(*pIndex); |
| return true; |
| } |
| return false; |
| } |
| |
| //============================================================================== |
| bool readArgument( |
| OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex ) |
| { |
| if (isOption( option_info, pIndex )) |
| { |
| if (*pIndex < osl_getCommandArgCount()) |
| { |
| OSL_ASSERT( pValue != 0 ); |
| osl_getCommandArg( *pIndex, &pValue->pData ); |
| dp_misc::TRACE(OUSTR( __FILE__": argument value: ") |
| + *pValue + OUSTR("\n")); |
| ++(*pIndex); |
| return true; |
| } |
| --(*pIndex); |
| } |
| return false; |
| } |
| |
| //############################################################################## |
| |
| namespace { |
| struct ExecutableDir : public rtl::StaticWithInit< |
| const OUString, ExecutableDir> { |
| const OUString operator () () { |
| OUString path; |
| if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) { |
| throw RuntimeException( |
| OUSTR("cannot locate executable directory!"),0 ); |
| } |
| return path.copy( 0, path.lastIndexOf( '/' ) ); |
| } |
| }; |
| struct ProcessWorkingDir : public rtl::StaticWithInit< |
| const OUString, ProcessWorkingDir> { |
| const OUString operator () () { |
| OUString workingDir; |
| tools::getProcessWorkingDir(&workingDir); |
| return workingDir; |
| } |
| }; |
| } // anon namespace |
| |
| //============================================================================== |
| OUString const & getExecutableDir() |
| { |
| return ExecutableDir::get(); |
| } |
| |
| //============================================================================== |
| OUString const & getProcessWorkingDir() |
| { |
| return ProcessWorkingDir::get(); |
| } |
| |
| //============================================================================== |
| OUString makeAbsoluteFileUrl( |
| OUString const & sys_path, OUString const & base_url, bool throw_exc ) |
| { |
| // system path to file url |
| OUString file_url; |
| oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData ); |
| if ( rc != osl_File_E_None) { |
| OUString tempPath; |
| if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) == osl_File_E_None ) |
| { |
| file_url = sys_path; |
| } |
| else if (throw_exc) |
| { |
| throw RuntimeException( |
| OUSTR("cannot get file url from system path: ") + |
| sys_path, Reference< XInterface >() ); |
| } |
| } |
| |
| OUString abs; |
| if (osl_getAbsoluteFileURL( |
| base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None) |
| { |
| if (throw_exc) { |
| ::rtl::OUStringBuffer buf; |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( |
| "making absolute file url failed: \"") ); |
| buf.append( base_url ); |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( |
| "\" (base-url) and \"") ); |
| buf.append( file_url ); |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" (file-url)!") ); |
| throw RuntimeException( |
| buf.makeStringAndClear(), Reference< XInterface >() ); |
| } |
| return OUString(); |
| } |
| return abs[ abs.getLength() -1 ] == '/' |
| ? abs.copy( 0, abs.getLength() -1 ) : abs; |
| } |
| |
| //############################################################################## |
| |
| namespace { |
| |
| //------------------------------------------------------------------------------ |
| inline void printf_space( sal_Int32 space ) |
| { |
| while (space--) |
| dp_misc::writeConsole(" "); |
| } |
| |
| //------------------------------------------------------------------------------ |
| void printf_line( |
| OUString const & name, OUString const & value, sal_Int32 level ) |
| { |
| printf_space( level ); |
| dp_misc::writeConsole(name + OUSTR(": ") + value + OUSTR("\n")); |
| } |
| |
| //------------------------------------------------------------------------------ |
| void printf_package( |
| Reference<deployment::XPackage> const & xPackage, |
| Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level ) |
| { |
| beans::Optional< OUString > id( |
| level == 0 |
| ? beans::Optional< OUString >( |
| true, dp_misc::getIdentifier( xPackage ) ) |
| : xPackage->getIdentifier() ); |
| if (id.IsPresent) |
| printf_line( OUSTR("Identifier"), id.Value, level ); |
| OUString version(xPackage->getVersion()); |
| if (version.getLength() != 0) |
| printf_line( OUSTR("Version"), version, level + 1 ); |
| printf_line( OUSTR("URL"), xPackage->getURL(), level + 1 ); |
| |
| beans::Optional< beans::Ambiguous<sal_Bool> > option( |
| xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) ); |
| OUString value; |
| if (option.IsPresent) { |
| beans::Ambiguous<sal_Bool> const & reg = option.Value; |
| if (reg.IsAmbiguous) |
| value = OUSTR("unknown"); |
| else |
| value = reg.Value ? OUSTR("yes") : OUSTR("no"); |
| } |
| else |
| value = OUSTR("n/a"); |
| printf_line( OUSTR("is registered"), value, level + 1 ); |
| |
| const Reference<deployment::XPackageTypeInfo> xPackageType( |
| xPackage->getPackageType() ); |
| OSL_ASSERT( xPackageType.is() ); |
| if (xPackageType.is()) { |
| printf_line( OUSTR("Media-Type"), |
| xPackageType->getMediaType(), level + 1 ); |
| } |
| printf_line( OUSTR("Description"), xPackage->getDescription(), level + 1 ); |
| if (xPackage->isBundle()) { |
| Sequence< Reference<deployment::XPackage> > seq( |
| xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) ); |
| printf_space( level + 1 ); |
| dp_misc::writeConsole("bundled Packages: {\n"); |
| ::std::vector<Reference<deployment::XPackage> >vec_bundle; |
| ::comphelper::sequenceToContainer(vec_bundle, seq); |
| printf_packages( vec_bundle, ::std::vector<bool>(vec_bundle.size()), |
| xCmdEnv, level + 2 ); |
| printf_space( level + 1 ); |
| dp_misc::writeConsole("}\n"); |
| } |
| } |
| |
| } // anon namespace |
| |
| void printf_unaccepted_licenses( |
| Reference<deployment::XPackage> const & ext) |
| { |
| OUString id( |
| dp_misc::getIdentifier(ext) ); |
| printf_line( OUSTR("Identifier"), id, 0 ); |
| printf_space(1); |
| dp_misc::writeConsole(OUSTR("License not accepted\n\n")); |
| } |
| |
| //============================================================================== |
| void printf_packages( |
| ::std::vector< Reference<deployment::XPackage> > const & allExtensions, |
| ::std::vector<bool> const & vecUnaccepted, |
| Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level ) |
| { |
| OSL_ASSERT(allExtensions.size() == vecUnaccepted.size()); |
| |
| if (allExtensions.size() == 0) |
| { |
| printf_space( level ); |
| dp_misc::writeConsole("<none>\n"); |
| } |
| else |
| { |
| typedef ::std::vector< Reference<deployment::XPackage> >::const_iterator I_EXT; |
| int index = 0; |
| for (I_EXT i = allExtensions.begin(); i != allExtensions.end(); i++, index++) |
| { |
| if (vecUnaccepted[index]) |
| printf_unaccepted_licenses(*i); |
| else |
| printf_package( *i, xCmdEnv, level ); |
| dp_misc::writeConsole(OUSTR("\n")); |
| } |
| } |
| } |
| |
| |
| //############################################################################## |
| |
| namespace { |
| |
| //------------------------------------------------------------------------------ |
| Reference<XComponentContext> bootstrapStandAlone( |
| DisposeGuard & disposeGuard, bool /*verbose */) |
| { |
| Reference<XComponentContext> xContext = |
| ::cppu::defaultBootstrap_InitialComponentContext(); |
| |
| // assure disposing of local component context: |
| disposeGuard.reset( |
| Reference<lang::XComponent>( xContext, UNO_QUERY ) ); |
| |
| Reference<lang::XMultiServiceFactory> xServiceManager( |
| xContext->getServiceManager(), UNO_QUERY_THROW ); |
| // set global process service factory used by unotools config helpers |
| ::utl::setProcessServiceFactory( xServiceManager ); |
| |
| // initialize the ucbhelper ucb, |
| // because the package implementation uses it |
| Sequence<Any> ucb_args( 2 ); |
| ucb_args[ 0 ] <<= OUSTR(UCB_CONFIGURATION_KEY1_LOCAL); |
| ucb_args[ 1 ] <<= OUSTR(UCB_CONFIGURATION_KEY2_OFFICE); |
| if (! ::ucbhelper::ContentBroker::initialize( xServiceManager, ucb_args )) |
| throw RuntimeException( OUSTR("cannot initialize UCB!"), 0 ); |
| |
| disposeGuard.setDeinitUCB(); |
| return xContext; |
| } |
| |
| //------------------------------------------------------------------------------ |
| Reference<XComponentContext> connectToOffice( |
| Reference<XComponentContext> const & xLocalComponentContext, |
| bool verbose ) |
| { |
| Sequence<OUString> args( 3 ); |
| args[ 0 ] = OUSTR("-nologo"); |
| args[ 1 ] = OUSTR("-nodefault"); |
| |
| OUString pipeId( ::dp_misc::generateRandomPipeId() ); |
| ::rtl::OUStringBuffer buf; |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("-accept=pipe,name=") ); |
| buf.append( pipeId ); |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(";urp;") ); |
| args[ 2 ] = buf.makeStringAndClear(); |
| OUString appURL( getExecutableDir() + OUSTR("/soffice") ); |
| |
| if (verbose) |
| { |
| dp_misc::writeConsole( |
| OUSTR("Raising process: ") + |
| appURL + |
| OUSTR("\nArguments: -nologo -nodefault ") + |
| args[2] + |
| OUSTR("\n")); |
| } |
| |
| ::dp_misc::raiseProcess( appURL, args ); |
| |
| if (verbose) |
| dp_misc::writeConsole("Ok. Connecting..."); |
| |
| OSL_ASSERT( buf.getLength() == 0 ); |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("uno:pipe,name=") ); |
| buf.append( pipeId ); |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( |
| ";urp;StarOffice.ComponentContext") ); |
| Reference<XComponentContext> xRet( |
| ::dp_misc::resolveUnoURL( |
| buf.makeStringAndClear(), xLocalComponentContext ), |
| UNO_QUERY_THROW ); |
| if (verbose) |
| dp_misc::writeConsole("Ok.\n"); |
| |
| return xRet; |
| } |
| |
| } // anon namespace |
| |
| /** returns the path to the lock file used by unopkg. |
| @return the path. An empty string signifies an error. |
| */ |
| OUString getLockFilePath() |
| { |
| OUString ret; |
| OUString sBootstrap(RTL_CONSTASCII_USTRINGPARAM("${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}")); |
| rtl::Bootstrap::expandMacros(sBootstrap); |
| OUString sAbs; |
| if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL( |
| sBootstrap, OUSTR(".lock"), sAbs)) |
| { |
| if (::osl::File::E_None == |
| ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap)) |
| { |
| ret = sBootstrap; |
| } |
| } |
| |
| return ret; |
| } |
| //============================================================================== |
| Reference<XComponentContext> getUNO( |
| DisposeGuard & disposeGuard, bool verbose, bool shared, bool bGui, |
| Reference<XComponentContext> & out_localContext) |
| { |
| // do not create any user data (for the root user) in --shared mode: |
| if (shared) { |
| rtl::Bootstrap::set( |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CFG_CacheUrl")), |
| rtl::OUString()); |
| } |
| |
| // hold lock during process runtime: |
| static ::desktop::Lockfile s_lockfile( false /* no IPC server */ ); |
| Reference<XComponentContext> xComponentContext( |
| bootstrapStandAlone( disposeGuard, verbose ) ); |
| out_localContext = xComponentContext; |
| if (::dp_misc::office_is_running()) { |
| xComponentContext.set( |
| connectToOffice( xComponentContext, verbose ) ); |
| } |
| else |
| { |
| if (! s_lockfile.check( 0 )) |
| { |
| String sMsg(ResId(RID_STR_CONCURRENTINSTANCE, *DeploymentResMgr::get())); |
| //Create this string before we call DeInitVCL, because this will kill |
| //the ResMgr |
| String sError(ResId(RID_STR_UNOPKG_ERROR, *DeploymentResMgr::get())); |
| |
| sMsg = sMsg + OUSTR("\n") + getLockFilePath(); |
| |
| if (bGui) |
| { |
| //We show a message box or print to the console that there |
| //is another instance already running |
| if ( ! InitVCL( Reference<lang::XMultiServiceFactory>( |
| xComponentContext->getServiceManager(), |
| UNO_QUERY_THROW ) )) |
| throw RuntimeException( OUSTR("Cannot initialize VCL!"), |
| NULL ); |
| { |
| WarningBox warn(NULL, WB_OK | WB_DEF_OK, sMsg); |
| warn.SetText(::utl::ConfigManager::GetDirectConfigProperty( |
| ::utl::ConfigManager::PRODUCTNAME).get<OUString>()); |
| warn.SetIcon(0); |
| warn.Execute(); |
| } |
| DeInitVCL(); |
| } |
| |
| throw LockFileException( |
| OUSTR("\n") + sError + sMsg + OUSTR("\n")); |
| } |
| } |
| |
| return xComponentContext; |
| } |
| |
| //Determines if a folder does not contains a folder. |
| //Return false may also mean that the status could not be determined |
| //because some error occurred. |
| bool hasNoFolder(OUString const & folderUrl) |
| { |
| bool ret = false; |
| OUString url = folderUrl; |
| ::rtl::Bootstrap::expandMacros(url); |
| ::osl::Directory dir(url); |
| osl::File::RC rc = dir.open(); |
| if (rc == osl::File::E_None) |
| { |
| bool bFolderExist = false; |
| osl::DirectoryItem i; |
| osl::File::RC rcNext = osl::File::E_None; |
| while ( (rcNext = dir.getNextItem(i)) == osl::File::E_None) |
| { |
| osl::FileStatus stat(FileStatusMask_Type); |
| if (i.getFileStatus(stat) == osl::File::E_None) |
| { |
| if (stat.getFileType() == osl::FileStatus::Directory) |
| { |
| bFolderExist = true; |
| break; |
| } |
| } |
| else |
| { |
| dp_misc::writeConsole( |
| OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); |
| break; |
| } |
| i = osl::DirectoryItem(); |
| } |
| |
| if (rcNext == osl::File::E_NOENT || |
| rcNext == osl::File::E_None) |
| { |
| if (!bFolderExist) |
| ret = true; |
| } |
| else |
| { |
| dp_misc::writeConsole( |
| OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); |
| } |
| |
| dir.close(); |
| } |
| else |
| { |
| dp_misc::writeConsole( |
| OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); |
| } |
| return ret; |
| } |
| |
| void removeFolder(OUString const & folderUrl) |
| { |
| OUString url = folderUrl; |
| ::rtl::Bootstrap::expandMacros(url); |
| ::osl::Directory dir(url); |
| ::osl::File::RC rc = dir.open(); |
| if (rc == osl::File::E_None) |
| { |
| ::osl::DirectoryItem i; |
| ::osl::File::RC rcNext = ::osl::File::E_None; |
| while ( (rcNext = dir.getNextItem(i)) == ::osl::File::E_None) |
| { |
| ::osl::FileStatus stat(FileStatusMask_Type | FileStatusMask_FileURL); |
| if (i.getFileStatus(stat) == ::osl::File::E_None) |
| { |
| ::osl::FileStatus::Type t = stat.getFileType(); |
| if (t == ::osl::FileStatus::Directory) |
| { |
| //remove folder |
| removeFolder(stat.getFileURL()); |
| } |
| else if (t == ::osl::FileStatus::Regular) |
| { |
| //remove file |
| ::osl::File::remove(stat.getFileURL()); |
| } |
| else |
| { |
| OSL_ASSERT(0); |
| } |
| } |
| else |
| { |
| dp_misc::writeConsole( |
| OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n")); |
| break; |
| } |
| i = ::osl::DirectoryItem(); |
| } |
| dir.close(); |
| ::osl::Directory::remove(url); |
| } |
| else if (rc != osl::File::E_NOENT) |
| { |
| dp_misc::writeConsole( |
| OUSTR("unopkg: Error while removing ") + url + OUSTR("\n")); |
| } |
| } |
| |
| } |