1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_folders.h>
21 #include <config_features.h>
24 #include <dp_interact.h>
25 #include <dp_shared.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <rtl/uri.hxx>
28 #include <rtl/digest.h>
29 #include <rtl/random.h>
30 #include <rtl/bootstrap.hxx>
31 #include <rtl/ustrbuf.hxx>
32 #include <sal/log.hxx>
33 #include <unotools/bootstrap.hxx>
34 #include <osl/diagnose.h>
35 #include <osl/file.hxx>
36 #include <osl/pipe.hxx>
37 #include <osl/security.hxx>
38 #include <osl/thread.hxx>
39 #include <com/sun/star/ucb/CommandAbortedException.hpp>
40 #include <com/sun/star/task/XInteractionHandler.hpp>
41 #include <com/sun/star/bridge/BridgeFactory.hpp>
42 #include <com/sun/star/bridge/UnoUrlResolver.hpp>
43 #include <com/sun/star/bridge/XUnoUrlResolver.hpp>
44 #include <com/sun/star/deployment/ExtensionManager.hpp>
45 #include <com/sun/star/lang/DisposedException.hpp>
46 #include <com/sun/star/task/OfficeRestartManager.hpp>
48 #include <string_view>
50 #include <comphelper/lok.hxx>
51 #include <comphelper/processfactory.hxx>
52 #include <salhelper/linkhelper.hxx>
59 using namespace ::com::sun::star
;
60 using namespace ::com::sun::star::uno
;
65 std::shared_ptr
<rtl::Bootstrap
> & UnoRc()
67 static std::shared_ptr
<rtl::Bootstrap
> theRc
= []()
69 OUString
unorc( u
"$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("louno") ""_ustr
);
70 ::rtl::Bootstrap::expandMacros( unorc
);
71 auto ret
= std::make_shared
<::rtl::Bootstrap
>( unorc
);
72 OSL_ASSERT( ret
->getHandle() != nullptr );
78 OUString
generateOfficePipeId()
81 ::utl::Bootstrap::PathStatus aLocateResult
=
82 ::utl::Bootstrap::locateUserInstallation( userPath
);
83 if (aLocateResult
!= ::utl::Bootstrap::PATH_EXISTS
&&
84 aLocateResult
!= ::utl::Bootstrap::PATH_VALID
)
86 throw Exception(u
"Extension Manager: Could not obtain path for UserInstallation."_ustr
, nullptr);
89 rtlDigest digest
= rtl_digest_create( rtl_Digest_AlgorithmMD5
);
91 throw RuntimeException(u
"cannot get digest rtl_Digest_AlgorithmMD5!"_ustr
, nullptr );
94 sal_uInt8
const * data
=
95 reinterpret_cast<sal_uInt8
const *>(userPath
.getStr());
96 std::size_t size
= userPath
.getLength() * sizeof (sal_Unicode
);
97 sal_uInt32 md5_key_len
= rtl_digest_queryLength( digest
);
98 std::unique_ptr
<sal_uInt8
[]> md5_buf( new sal_uInt8
[ md5_key_len
] );
100 rtl_digest_init( digest
, data
, static_cast<sal_uInt32
>(size
) );
101 rtl_digest_update( digest
, data
, static_cast<sal_uInt32
>(size
) );
102 rtl_digest_get( digest
, md5_buf
.get(), md5_key_len
);
103 rtl_digest_destroy( digest
);
105 // create hex-value string from the MD5 value to keep
106 // the string size minimal
107 OUStringBuffer
buf( "SingleOfficeIPC_" );
108 for ( sal_uInt32 i
= 0; i
< md5_key_len
; ++i
) {
109 buf
.append( static_cast<sal_Int32
>(md5_buf
[ i
]), 0x10 );
111 return buf
.makeStringAndClear();
114 bool existsOfficePipe()
116 static OUString OfficePipeId
= generateOfficePipeId();
118 OUString
const & pipeId
= OfficePipeId
;
119 if (pipeId
.isEmpty())
122 ::osl::Pipe
pipe( pipeId
, osl_Pipe_OPEN
, sec
);
126 //get modification time
127 bool getModifyTimeTargetFile(const OUString
&rFileURL
, TimeValue
&rTime
)
129 salhelper::LinkResolver
aResolver(osl_FileStatus_Mask_ModifyTime
);
131 if (aResolver
.fetchFileStatus(rFileURL
) != osl::FileBase::E_None
)
134 rTime
= aResolver
.m_aStatus
.getModifyTime();
139 //Returns true if the Folder was more recently modified then
140 //the lastsynchronized file. That is the repository needs to
142 bool compareExtensionFolderWithLastSynchronizedFile(
143 OUString
const & folderURL
, OUString
const & fileURL
)
145 bool bNeedsSync
= false;
146 ::osl::DirectoryItem itemExtFolder
;
147 ::osl::File::RC err1
=
148 ::osl::DirectoryItem::get(folderURL
, itemExtFolder
);
149 //If it does not exist, then there is nothing to be done
150 if (err1
== ::osl::File::E_NOENT
)
154 else if (err1
!= ::osl::File::E_None
)
156 OSL_FAIL("Cannot access extension folder");
157 return true; //sync just in case
160 //If last synchronized does not exist, then OOo is started for the first time
161 ::osl::DirectoryItem itemFile
;
162 ::osl::File::RC err2
= ::osl::DirectoryItem::get(fileURL
, itemFile
);
163 if (err2
== ::osl::File::E_NOENT
)
168 else if (err2
!= ::osl::File::E_None
)
170 OSL_FAIL("Cannot access file lastsynchronized");
171 return true; //sync just in case
174 //compare the modification time of the extension folder and the last
176 TimeValue timeFolder
;
177 if (getModifyTimeTargetFile(folderURL
, timeFolder
))
180 if (getModifyTimeTargetFile(fileURL
, timeFile
))
182 if (timeFile
.Seconds
< timeFolder
.Seconds
)
200 bool needToSyncRepository(std::u16string_view name
)
204 if ( name
== u
"bundled" )
206 folder
= "$BUNDLED_EXTENSIONS";
207 file
= "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
209 else if ( name
== u
"shared" )
211 folder
= "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
212 file
= "$SHARED_EXTENSIONS_USER/lastsynchronized";
219 ::rtl::Bootstrap::expandMacros(folder
);
220 ::rtl::Bootstrap::expandMacros(file
);
221 return compareExtensionFolderWithLastSynchronizedFile(
230 OUString
encodeForRcFile( std::u16string_view str
)
232 // escape $\{} (=> rtl bootstrap files)
233 OUStringBuffer
buf(64);
235 const size_t len
= str
.size();
236 for ( ; pos
< len
; ++pos
) {
237 sal_Unicode c
= str
[ pos
];
248 return buf
.makeStringAndClear();
253 OUString
makeURL( std::u16string_view baseURL
, OUString
const & relPath_
)
255 OUStringBuffer
buf(128);
256 if (baseURL
.size() > 1 && baseURL
[ baseURL
.size() - 1 ] == '/')
257 buf
.append( baseURL
.substr(0, baseURL
.size() - 1) );
259 buf
.append( baseURL
);
260 OUString
relPath(relPath_
);
261 if( relPath
.startsWith("/") )
262 relPath
= relPath
.copy( 1 );
263 if (!relPath
.isEmpty())
266 if (o3tl::starts_with(baseURL
, u
"vnd.sun.star.expand:" )) {
267 // encode for macro expansion: relPath is supposed to have no
268 // macros, so encode $, {} \ (bootstrap mimic)
269 relPath
= encodeForRcFile(relPath
);
271 // encode once more for vnd.sun.star.expand schema:
272 // vnd.sun.star.expand:$UNO_...
273 // will expand to file-url
274 relPath
= ::rtl::Uri::encode( relPath
, rtl_UriCharClassUric
,
275 rtl_UriEncodeIgnoreEscapes
,
276 RTL_TEXTENCODING_UTF8
);
278 buf
.append( relPath
);
280 return buf
.makeStringAndClear();
283 OUString
makeURLAppendSysPathSegment( std::u16string_view baseURL
, OUString
const & segment
)
285 OSL_ASSERT(segment
.indexOf(u
'/') == -1);
288 segment
, rtl_UriCharClassPchar
, rtl_UriEncodeIgnoreEscapes
,
289 RTL_TEXTENCODING_UTF8
);
290 return makeURL(baseURL
, segment
);
294 OUString
expandUnoRcTerm( OUString
const & term_
)
296 OUString
term(term_
);
297 UnoRc()->expandMacrosFrom( term
);
301 OUString
makeRcTerm( OUString
const & url
)
303 OSL_ASSERT( url
.match( "vnd.sun.star.expand:" ));
304 if (OUString rcterm
; url
.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &rcterm
)) {
305 // decode uric class chars:
306 rcterm
= ::rtl::Uri::decode(
307 rcterm
, rtl_UriDecodeWithCharset
, RTL_TEXTENCODING_UTF8
);
315 OUString
expandUnoRcUrl( OUString
const & url
)
317 if (OUString rcurl
; url
.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &rcurl
)) {
318 // decode uric class chars:
319 rcurl
= ::rtl::Uri::decode(
320 rcurl
, rtl_UriDecodeWithCharset
, RTL_TEXTENCODING_UTF8
);
321 // expand macro string:
322 UnoRc()->expandMacrosFrom( rcurl
);
331 bool office_is_running()
333 //We need to check if we run within the office process. Then we must not use the pipe, because
334 //this could cause a deadlock. This is actually a workaround for i82778
336 oslProcessError err
= osl_getExecutableFile(& sFile
.pData
);
338 if (osl_Process_E_None
== err
)
340 sFile
= sFile
.copy(sFile
.lastIndexOf('/') + 1);
343 //osl_getExecutableFile should deliver "soffice.bin" on windows
344 //even if swriter.exe, scalc.exe etc. was started. This is a bug
345 //in osl_getExecutableFile
346 sFile
== "soffice.bin" || sFile
== "soffice.exe" || sFile
== "soffice.com"
347 || sFile
== "soffice" || sFile
== "swriter.exe" || sFile
== "swriter"
348 || sFile
== "scalc.exe" || sFile
== "scalc" || sFile
== "simpress.exe"
349 || sFile
== "simpress" || sFile
== "sdraw.exe" || sFile
== "sdraw"
350 || sFile
== "sbase.exe" || sFile
== "sbase"
354 sFile
== "soffice.bin"
356 #error "Unsupported platform"
362 ret
= existsOfficePipe();
366 OSL_FAIL("NOT osl_Process_E_None ");
367 //if osl_getExecutable file then we take the risk of creating a pipe
368 ret
= existsOfficePipe();
374 oslProcess
raiseProcess(
375 OUString
const & appURL
, Sequence
<OUString
> const & args
)
378 oslProcess hProcess
= nullptr;
379 oslProcessError rc
= osl_executeProcess(
381 reinterpret_cast<rtl_uString
**>(
382 const_cast<OUString
*>(args
.getConstArray()) ),
384 osl_Process_DETACHED
,
386 nullptr, // => current working dir
387 nullptr, 0, // => no env vars
391 case osl_Process_E_None
:
393 case osl_Process_E_NotFound
:
394 throw RuntimeException( u
"image not found!"_ustr
, nullptr );
395 case osl_Process_E_TimedOut
:
396 throw RuntimeException( u
"timeout occurred!"_ustr
, nullptr );
397 case osl_Process_E_NoPermission
:
398 throw RuntimeException( u
"permission denied!"_ustr
, nullptr );
399 case osl_Process_E_Unknown
:
400 throw RuntimeException( u
"unknown error!"_ustr
, nullptr );
401 case osl_Process_E_InvalidError
:
403 throw RuntimeException( u
"unmapped error!"_ustr
, nullptr );
410 OUString
generateRandomPipeId()
412 // compute some good pipe id:
413 sal_uInt8 bytes
[ 32 ];
414 if (rtl_random_getBytes(
415 nullptr, bytes
, std::size(bytes
) ) != rtl_Random_E_None
) {
416 throw RuntimeException( u
"random pool error!?"_ustr
, nullptr );
419 for (unsigned char byte
: bytes
) {
420 buf
.append( static_cast<sal_Int32
>(byte
), 0x10 );
422 return buf
.makeStringAndClear();
426 Reference
<XInterface
> resolveUnoURL(
427 OUString
const & connectString
,
428 Reference
<XComponentContext
> const & xLocalContext
,
429 AbortChannel
const * abortChannel
)
431 Reference
<bridge::XUnoUrlResolver
> xUnoUrlResolver(
432 bridge::UnoUrlResolver::create( xLocalContext
) );
434 for (int i
= 0; i
<= 40; ++i
) // 20 seconds
436 if (abortChannel
!= nullptr && abortChannel
->isAborted()) {
437 throw ucb::CommandAbortedException( u
"abort!"_ustr
);
440 return xUnoUrlResolver
->resolve( connectString
);
442 catch (const connection::NoConnectException
&) {
445 std::this_thread::sleep_for( std::chrono::milliseconds(500) );
450 return nullptr; // warning C4715
453 static void writeConsoleWithStream(std::u16string_view sText
, FILE * stream
)
455 OString s
= OUStringToOString(sText
, osl_getThreadTextEncoding());
456 fprintf(stream
, "%s", s
.getStr());
460 void writeConsole(std::u16string_view sText
)
462 writeConsoleWithStream(sText
, stdout
);
465 void writeConsoleError(std::u16string_view sText
)
467 writeConsoleWithStream(sText
, stderr
);
470 OUString
readConsole()
473 memset(buf
, 0, 1024);
474 // read one char less so that the last char in buf is always zero
475 if (fgets(buf
, 1024, stdin
) != nullptr)
477 OUString value
= OStringToOUString(std::string_view(buf
), osl_getThreadTextEncoding());
480 throw css::uno::RuntimeException(u
"reading from stdin failed"_ustr
);
483 void TRACE(OUString
const & sText
)
485 SAL_INFO("desktop.deployment", sText
);
488 void syncRepositories(
489 bool force
, Reference
<ucb::XCommandEnvironment
> const & xCmdEnv
)
492 ::rtl::Bootstrap::get( u
"DISABLE_EXTENSION_SYNCHRONIZATION"_ustr
, sDisable
, OUString() );
493 if (!sDisable
.isEmpty())
496 Reference
<deployment::XExtensionManager
> xExtensionManager
;
497 //synchronize shared before bundled otherwise there are
498 //more revoke and registration calls.
499 bool bModified
= false;
500 if (force
|| needToSyncRepository(u
"shared") || needToSyncRepository(u
"bundled"))
503 deployment::ExtensionManager::get(
504 comphelper::getProcessComponentContext());
506 if (xExtensionManager
.is())
508 bModified
= xExtensionManager
->synchronize(
509 Reference
<task::XAbortChannel
>(), xCmdEnv
);
512 #if HAVE_FEATURE_MACOSX_SANDBOX
515 if (bModified
&& !comphelper::LibreOfficeKit::isActive())
517 Reference
<task::XRestartManager
> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
520 restarter
->requestRestart(xCmdEnv
.is() ? xCmdEnv
->getInteractionHandler() :
521 Reference
<task::XInteractionHandler
>());
527 void disposeBridges(Reference
<css::uno::XComponentContext
> const & ctx
)
532 Reference
<css::bridge::XBridgeFactory2
> bridgeFac( css::bridge::BridgeFactory::create(ctx
) );
534 const Sequence
< Reference
<css::bridge::XBridge
> >seqBridges
= bridgeFac
->getExistingBridges();
535 for (const Reference
<css::bridge::XBridge
>& bridge
: seqBridges
)
537 Reference
<css::lang::XComponent
> comp(bridge
, UNO_QUERY
);
543 catch ( const css::lang::DisposedException
& )
552 OUString
DpResId(TranslateId aId
)
554 static std::locale SINGLETON
= Translate::Create("dkt");
555 return Translate::get(aId
, SINGLETON
);
559 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */