/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "unopkg_shared.h" #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::ucb; namespace unopkg { OUString toString( OptionInfo const * info ) { assert(info != nullptr); OUStringBuffer buf("--"); buf.appendAscii(info->m_name); if (info->m_short_option != '\0') { buf.append(" (short -" + OUStringChar(info->m_short_option) + ")"); } if (info->m_has_argument) buf.append(" " ); return buf.makeStringAndClear(); } OptionInfo const * getOptionInfo( OptionInfo const * list, OUString const & opt ) { for ( ; list->m_name != nullptr; ++list ) { OptionInfo const & option_info = *list; if (!opt.isEmpty()) { if (opt.equalsAsciiL( option_info.m_name, option_info.m_name_length )) { return &option_info; } } } SAL_WARN( "desktop", opt ); return nullptr; } bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex ) { assert(option_info != nullptr); 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(__FILE__ ": identified option \'\'" + OUStringChar( option_info->m_short_option ) + "\n"); return true; } if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare( arg.pData->buffer + 2, option_info->m_name ) == 0) { ++(*pIndex); dp_misc::TRACE(__FILE__ ": identified option \'" + OUString::createFromAscii(option_info->m_name) + "\'\n"); return true; } return false; } bool isBootstrapVariable(sal_uInt32 * pIndex) { OSL_ASSERT(osl_getCommandArgCount() >= *pIndex); OUString arg; osl_getCommandArg(*pIndex, &arg.pData); if (arg.match("-env:")) { ++(*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 != nullptr ); osl_getCommandArg( *pIndex, &pValue->pData ); dp_misc::TRACE(__FILE__ ": argument value: " + *pValue + "\n"); ++(*pIndex); return true; } --(*pIndex); } return false; } OUString const & getExecutableDir() { static const OUString EXEC = []() { OUString path; if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) { throw RuntimeException("cannot locate executable directory!",nullptr); } return path.copy( 0, path.lastIndexOf( '/' ) ); }(); return EXEC; } OUString const & getProcessWorkingDir() { static const OUString WORKING = []() { OUString workingDir; utl::Bootstrap::getProcessWorkingDir(workingDir); return workingDir; }(); return WORKING; } OUString makeAbsoluteFileUrl( OUString const & sys_path, OUString const & base_url ) { // 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 ) { throw RuntimeException("cannot get file url from system path: " + sys_path ); } file_url = sys_path; } OUString abs; if (osl_getAbsoluteFileURL( base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None) { throw RuntimeException( "making absolute file url failed: \"" + base_url + "\" (base-url) and \"" + file_url + "\" (file-url)!" ); } return abs[ abs.getLength() -1 ] == '/' ? abs.copy( 0, abs.getLength() -1 ) : abs; } namespace { void printf_space( sal_Int32 space ) { while (space--) dp_misc::writeConsole(u" "); } void printf_line( std::u16string_view name, std::u16string_view value, sal_Int32 level ) { printf_space( level ); dp_misc::writeConsole(Concat2View(OUString::Concat(name) + ": " + value + "\n")); } void printf_package( Reference const & xPackage, Reference 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( u"Identifier", id.Value, level ); OUString version(xPackage->getVersion()); if (!version.isEmpty()) printf_line( u"Version", version, level + 1 ); printf_line( u"URL", xPackage->getURL(), level + 1 ); beans::Optional< beans::Ambiguous > option( xPackage->isRegistered( Reference(), xCmdEnv ) ); OUString value; if (option.IsPresent) { beans::Ambiguous const & reg = option.Value; if (reg.IsAmbiguous) value = "unknown"; else value = reg.Value ? std::u16string_view(u"yes") : std::u16string_view(u"no"); } else value = "n/a"; printf_line( u"is registered", value, level + 1 ); const Reference xPackageType( xPackage->getPackageType() ); OSL_ASSERT( xPackageType.is() ); if (xPackageType.is()) { printf_line( u"Media-Type", xPackageType->getMediaType(), level + 1 ); } printf_line( u"Description", xPackage->getDescription(), level + 1 ); if (!xPackage->isBundle()) return; Sequence< Reference > seq( xPackage->getBundle( Reference(), xCmdEnv ) ); printf_space( level + 1 ); dp_misc::writeConsole(u"bundled Packages: {\n"); std::vector >vec_bundle; ::comphelper::sequenceToContainer(vec_bundle, seq); printf_packages( vec_bundle, std::vector(vec_bundle.size()), xCmdEnv, level + 2 ); printf_space( level + 1 ); dp_misc::writeConsole(u"}\n"); } } // anon namespace static void printf_unaccepted_licenses( Reference const & ext) { OUString id( dp_misc::getIdentifier(ext) ); printf_line( u"Identifier", id, 0 ); printf_space(1); dp_misc::writeConsole(u"License not accepted\n\n"); } void printf_packages( std::vector< Reference > const & allExtensions, std::vector const & vecUnaccepted, Reference const & xCmdEnv, sal_Int32 level ) { OSL_ASSERT(allExtensions.size() == vecUnaccepted.size()); if (allExtensions.empty()) { printf_space( level ); dp_misc::writeConsole(u"\n"); } else { int index = 0; for (auto const& extension : allExtensions) { if (vecUnaccepted[index]) printf_unaccepted_licenses(extension); else printf_package( extension, xCmdEnv, level ); dp_misc::writeConsole(u"\n"); ++index; } } } namespace { Reference bootstrapStandAlone() { Reference xContext = ::cppu::defaultBootstrap_InitialComponentContext(); Reference xServiceManager( xContext->getServiceManager(), UNO_QUERY_THROW ); // set global process service factory used by unotools config helpers ::comphelper::setProcessServiceFactory( xServiceManager ); // Initialize the UCB (for backwards compatibility, in case some code still // uses plain createInstance w/o args directly to obtain an instance): UniversalContentBroker::create( xContext ); return xContext; } Reference connectToOffice( Reference const & xLocalComponentContext, bool verbose ) { OUString pipeId( ::dp_misc::generateRandomPipeId() ); OUString acceptArg = "--accept=pipe,name=" + pipeId + ";urp;"; Sequence args { "--nologo", "--nodefault", acceptArg }; OUString appURL( getExecutableDir() + "/soffice" ); if (verbose) { dp_misc::writeConsole(Concat2View( "Raising process: " + appURL + "\nArguments: --nologo --nodefault " + args[2] + "\n")); } ::dp_misc::raiseProcess( appURL, args ); if (verbose) dp_misc::writeConsole(u"OK. Connecting..."); OUString sUnoUrl = "uno:pipe,name=" + pipeId + ";urp;StarOffice.ComponentContext"; Reference xRet( ::dp_misc::resolveUnoURL( sUnoUrl, xLocalComponentContext ), UNO_QUERY_THROW ); if (verbose) dp_misc::writeConsole(u"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. */ static OUString getLockFilePath() { OUString ret; OUString sBootstrap("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}"); rtl::Bootstrap::expandMacros(sBootstrap); OUString sAbs; if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL( sBootstrap, ".lock", sAbs)) { if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap)) { ret = sBootstrap; } } return ret; } Reference getUNO( bool verbose, bool bGui, const OUString& sTempDir, Reference & out_localContext) { // do not create any user data (for the root user) in --shared mode: if (!sTempDir.isEmpty()) rtl::Bootstrap::set("UserInstallation", sTempDir); // hold lock during process runtime: static ::desktop::Lockfile s_lockfile( false /* no IPC server */ ); Reference xComponentContext( bootstrapStandAlone() ); out_localContext = xComponentContext; if (::dp_misc::office_is_running()) { xComponentContext.set( connectToOffice( xComponentContext, verbose ) ); } else { if (! s_lockfile.check( nullptr )) { OUString sMsg(DpResId(RID_STR_CONCURRENTINSTANCE)); OUString sError(DpResId(RID_STR_UNOPKG_ERROR)); sMsg += "\n" + getLockFilePath(); if (bGui) { //We show a message box or print to the console that there //is another instance already running if ( ! InitVCL() ) throw RuntimeException( "Cannot initialize VCL!" ); { std::unique_ptr xWarn(Application::CreateMessageDialog(nullptr, VclMessageType::Warning, VclButtonsType::Ok, sMsg)); xWarn->set_title(utl::ConfigManager::getProductName()); xWarn->run(); } DeInitVCL(); } throw LockFileException(sError + sMsg); } } return xComponentContext; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */