LibreOffice Module desktop (master) 1
dp_misc.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <config_folders.h>
21#include <config_features.h>
22#include <chrono>
23
24#include <dp_misc.h>
25#include <dp_interact.h>
26#include <dp_shared.hxx>
27#include <o3tl/string_view.hxx>
28#include <rtl/uri.hxx>
29#include <rtl/digest.h>
30#include <rtl/random.h>
31#include <rtl/bootstrap.hxx>
32#include <rtl/ustrbuf.hxx>
33#include <sal/log.hxx>
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>
47#include <memory>
48#include <string_view>
49#include <thread>
50#include <comphelper/lok.hxx>
53
54#ifdef _WIN32
55#include <prewin.h>
56#include <postwin.h>
57#endif
58
59using namespace ::com::sun::star;
60using namespace ::com::sun::star::uno;
61
62namespace dp_misc {
63namespace {
64
65std::shared_ptr<rtl::Bootstrap> & UnoRc()
66{
67 static std::shared_ptr<rtl::Bootstrap> theRc = []()
68 {
69 OUString unorc( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("louno") );
70 ::rtl::Bootstrap::expandMacros( unorc );
71 auto ret = std::make_shared<::rtl::Bootstrap>( unorc );
72 OSL_ASSERT( ret->getHandle() != nullptr );
73 return ret;
74 }();
75 return theRc;
76};
77
78OUString generateOfficePipeId()
79{
80 OUString userPath;
81 ::utl::Bootstrap::PathStatus aLocateResult =
83 if (aLocateResult != ::utl::Bootstrap::PATH_EXISTS &&
84 aLocateResult != ::utl::Bootstrap::PATH_VALID)
85 {
86 throw Exception("Extension Manager: Could not obtain path for UserInstallation.", nullptr);
87 }
88
89 rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
90 if (!digest) {
91 throw RuntimeException("cannot get digest rtl_Digest_AlgorithmMD5!", nullptr );
92 }
93
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 ] );
99
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 );
104
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 );
110 }
111 return buf.makeStringAndClear();
112}
113
114bool existsOfficePipe()
115{
116 static OUString OfficePipeId = generateOfficePipeId();
117
118 OUString const & pipeId = OfficePipeId;
119 if (pipeId.isEmpty())
120 return false;
121 ::osl::Security sec;
122 ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
123 return pipe.is();
124}
125
126//get modification time
127bool getModifyTimeTargetFile(const OUString &rFileURL, TimeValue &rTime)
128{
129 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_ModifyTime);
130
131 if (aResolver.fetchFileStatus(rFileURL) != osl::FileBase::E_None)
132 return false;
133
134 rTime = aResolver.m_aStatus.getModifyTime();
135
136 return true;
137}
138
139//Returns true if the Folder was more recently modified then
140//the lastsynchronized file. That is the repository needs to
141//be synchronized.
142bool compareExtensionFolderWithLastSynchronizedFile(
143 OUString const & folderURL, OUString const & fileURL)
144{
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)
151 {
152 return false;
153 }
154 else if (err1 != ::osl::File::E_None)
155 {
156 OSL_FAIL("Cannot access extension folder");
157 return true; //sync just in case
158 }
159
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)
164 {
165 return true;
166
167 }
168 else if (err2 != ::osl::File::E_None)
169 {
170 OSL_FAIL("Cannot access file lastsynchronized");
171 return true; //sync just in case
172 }
173
174 //compare the modification time of the extension folder and the last
175 //modified file
176 TimeValue timeFolder;
177 if (getModifyTimeTargetFile(folderURL, timeFolder))
178 {
179 TimeValue timeFile;
180 if (getModifyTimeTargetFile(fileURL, timeFile))
181 {
182 if (timeFile.Seconds < timeFolder.Seconds)
183 bNeedsSync = true;
184 }
185 else
186 {
187 OSL_ASSERT(false);
188 bNeedsSync = true;
189 }
190 }
191 else
192 {
193 OSL_ASSERT(false);
194 bNeedsSync = true;
195 }
196
197 return bNeedsSync;
198}
199
200bool needToSyncRepository(std::u16string_view name)
201{
202 OUString folder;
203 OUString file;
204 if ( name == u"bundled" )
205 {
206 folder = "$BUNDLED_EXTENSIONS";
207 file = "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
208 }
209 else if ( name == u"shared" )
210 {
211 folder = "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
212 file = "$SHARED_EXTENSIONS_USER/lastsynchronized";
213 }
214 else
215 {
216 OSL_ASSERT(false);
217 return true;
218 }
219 ::rtl::Bootstrap::expandMacros(folder);
220 ::rtl::Bootstrap::expandMacros(file);
221 return compareExtensionFolderWithLastSynchronizedFile(
222 folder, file);
223}
224
225
226} // anon namespace
227
228
229namespace {
230OUString encodeForRcFile( std::u16string_view str )
231{
232 // escape $\{} (=> rtl bootstrap files)
233 OUStringBuffer buf(64);
234 size_t pos = 0;
235 const size_t len = str.size();
236 for ( ; pos < len; ++pos ) {
237 sal_Unicode c = str[ pos ];
238 switch (c) {
239 case '$':
240 case '\\':
241 case '{':
242 case '}':
243 buf.append( '\\' );
244 break;
245 }
246 buf.append( c );
247 }
248 return buf.makeStringAndClear();
249}
250}
251
252
253OUString makeURL( std::u16string_view baseURL, OUString const & relPath_ )
254{
255 OUStringBuffer buf(128);
256 if (baseURL.size() > 1 && baseURL[ baseURL.size() - 1 ] == '/')
257 buf.append( baseURL.substr(0, baseURL.size() - 1) );
258 else
259 buf.append( baseURL );
260 OUString relPath(relPath_);
261 if( relPath.startsWith("/") )
262 relPath = relPath.copy( 1 );
263 if (!relPath.isEmpty())
264 {
265 buf.append( '/' );
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);
270
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 );
277 }
278 buf.append( relPath );
279 }
280 return buf.makeStringAndClear();
281}
282
283OUString makeURLAppendSysPathSegment( std::u16string_view baseURL, OUString const & segment )
284{
285 OSL_ASSERT(segment.indexOf(u'/') == -1);
286
287 ::rtl::Uri::encode(
288 segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
289 RTL_TEXTENCODING_UTF8);
290 return makeURL(baseURL, segment);
291}
292
293
294OUString expandUnoRcTerm( OUString const & term_ )
295{
296 OUString term(term_);
297 UnoRc()->expandMacrosFrom( term );
298 return term;
299}
300
301OUString makeRcTerm( OUString const & url )
302{
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 );
308 return rcterm;
309 }
310 else
311 return url;
312}
313
314
315OUString expandUnoRcUrl( OUString const & url )
316{
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 );
323 return rcurl;
324 }
325 else {
326 return url;
327 }
328}
329
330
332{
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
335 OUString sFile;
336 oslProcessError err = osl_getExecutableFile(& sFile.pData);
337 bool ret = false;
338 if (osl_Process_E_None == err)
339 {
340 sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
341 if (
342#if defined _WIN32
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"
351#elif defined MACOSX
352 sFile == "soffice"
353#elif defined UNIX
354 sFile == "soffice.bin"
355#else
356#error "Unsupported platform"
357#endif
358
359 )
360 ret = true;
361 else
362 ret = existsOfficePipe();
363 }
364 else
365 {
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();
369 }
370 return ret;
371}
372
373
374oslProcess raiseProcess(
375 OUString const & appURL, Sequence<OUString> const & args )
376{
377 ::osl::Security sec;
378 oslProcess hProcess = nullptr;
379 oslProcessError rc = osl_executeProcess(
380 appURL.pData,
381 reinterpret_cast<rtl_uString **>(
382 const_cast<OUString *>(args.getConstArray()) ),
383 args.getLength(),
384 osl_Process_DETACHED,
385 sec.getHandle(),
386 nullptr, // => current working dir
387 nullptr, 0, // => no env vars
388 &hProcess );
389
390 switch (rc) {
391 case osl_Process_E_None:
392 break;
393 case osl_Process_E_NotFound:
394 throw RuntimeException( "image not found!", nullptr );
395 case osl_Process_E_TimedOut:
396 throw RuntimeException( "timeout occurred!", nullptr );
397 case osl_Process_E_NoPermission:
398 throw RuntimeException( "permission denied!", nullptr );
399 case osl_Process_E_Unknown:
400 throw RuntimeException( "unknown error!", nullptr );
401 case osl_Process_E_InvalidError:
402 default:
403 throw RuntimeException( "unmapped error!", nullptr );
404 }
405
406 return hProcess;
407}
408
409
411{
412 // compute some good pipe id:
413 static rtlRandomPool s_hPool = rtl_random_createPool();
414 if (s_hPool == nullptr)
415 throw RuntimeException( "cannot create random pool!?", nullptr );
416 sal_uInt8 bytes[ 32 ];
417 if (rtl_random_getBytes(
418 s_hPool, bytes, std::size(bytes) ) != rtl_Random_E_None) {
419 throw RuntimeException( "random pool error!?", nullptr );
420 }
421 OUStringBuffer buf;
422 for (unsigned char byte : bytes) {
423 buf.append( static_cast<sal_Int32>(byte), 0x10 );
424 }
425 return buf.makeStringAndClear();
426}
427
428
429Reference<XInterface> resolveUnoURL(
430 OUString const & connectString,
431 Reference<XComponentContext> const & xLocalContext,
432 AbortChannel const * abortChannel )
433{
434 Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
435 bridge::UnoUrlResolver::create( xLocalContext ) );
436
437 for (int i = 0; i <= 40; ++i) // 20 seconds
438 {
439 if (abortChannel != nullptr && abortChannel->isAborted()) {
440 throw ucb::CommandAbortedException( "abort!" );
441 }
442 try {
443 return xUnoUrlResolver->resolve( connectString );
444 }
445 catch (const connection::NoConnectException &) {
446 if (i < 40)
447 {
448 std::this_thread::sleep_for( std::chrono::milliseconds(500) );
449 }
450 else throw;
451 }
452 }
453 return nullptr; // warning C4715
454}
455
456static void writeConsoleWithStream(std::u16string_view sText, FILE * stream)
457{
458 OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
459 fprintf(stream, "%s", s.getStr());
460 fflush(stream);
461}
462
463void writeConsole(std::u16string_view sText)
464{
465 writeConsoleWithStream(sText, stdout);
466}
467
468void writeConsoleError(std::u16string_view sText)
469{
470 writeConsoleWithStream(sText, stderr);
471}
472
473OUString readConsole()
474{
475 char buf[1024];
476 memset(buf, 0, 1024);
477 // read one char less so that the last char in buf is always zero
478 if (fgets(buf, 1024, stdin) != nullptr)
479 {
480 OUString value = OStringToOUString(std::string_view(buf), osl_getThreadTextEncoding());
481 return value.trim();
482 }
483 throw css::uno::RuntimeException("reading from stdin failed");
484}
485
486void TRACE(OUString const & sText)
487{
488 SAL_INFO("desktop.deployment", sText);
489}
490
492 bool force, Reference<ucb::XCommandEnvironment> const & xCmdEnv)
493{
494 OUString sDisable;
495 ::rtl::Bootstrap::get( "DISABLE_EXTENSION_SYNCHRONIZATION", sDisable, OUString() );
496 if (!sDisable.isEmpty())
497 return;
498
499 Reference<deployment::XExtensionManager> xExtensionManager;
500 //synchronize shared before bundled otherwise there are
501 //more revoke and registration calls.
502 bool bModified = false;
503 if (force || needToSyncRepository(u"shared") || needToSyncRepository(u"bundled"))
504 {
505 xExtensionManager =
506 deployment::ExtensionManager::get(
508
509 if (xExtensionManager.is())
510 {
511 bModified = xExtensionManager->synchronize(
512 Reference<task::XAbortChannel>(), xCmdEnv);
513 }
514 }
515#if HAVE_FEATURE_MACOSX_SANDBOX
516 (void) bModified;
517#else
518 if (bModified && !comphelper::LibreOfficeKit::isActive())
519 {
520 Reference<task::XRestartManager> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
521 if (restarter.is())
522 {
523 restarter->requestRestart(xCmdEnv.is() ? xCmdEnv->getInteractionHandler() :
524 Reference<task::XInteractionHandler>());
525 }
526 }
527#endif
528}
529
530void disposeBridges(Reference<css::uno::XComponentContext> const & ctx)
531{
532 if (!ctx.is())
533 return;
534
535 Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(ctx) );
536
537 const Sequence< Reference<css::bridge::XBridge> >seqBridges = bridgeFac->getExistingBridges();
538 for (const Reference<css::bridge::XBridge>& bridge : seqBridges)
539 {
540 Reference<css::lang::XComponent> comp(bridge, UNO_QUERY);
541 if (comp.is())
542 {
543 try {
544 comp->dispose();
545 }
546 catch ( const css::lang::DisposedException& )
547 {
548 }
549 }
550 }
551}
552
553}
554
555OUString DpResId(TranslateId aId)
556{
557 static std::locale SINGLETON = Translate::Create("dkt");
558 return Translate::get(aId, SINGLETON);
559}
560
561
562/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void * rtlRandomPool
static PathStatus locateUserInstallation(OUString &_rURL)
Any value
#define SAL_CONFIGFILE(name)
Reference< XOutputStream > stream
OUString DpResId(TranslateId aId)
Definition: dp_misc.cxx:555
float u
#define SAL_INFO(area, stream)
err
std::locale Create(std::string_view aPrefixName, const LanguageTag &rLocale)
OUString get(TranslateId sContextAndId, const std::locale &loc)
size
@ Exception
Reference< XComponentContext > getProcessComponentContext()
OUString makeURL(std::u16string_view baseURL, OUString const &relPath_)
appends a relative path to a url.
Definition: dp_misc.cxx:253
void syncRepositories(bool force, Reference< ucb::XCommandEnvironment > const &xCmdEnv)
Definition: dp_misc.cxx:491
OUString makeRcTerm(OUString const &url)
Definition: dp_misc.cxx:301
OUString expandUnoRcUrl(OUString const &url)
Definition: dp_misc.cxx:315
OUString readConsole()
reads from the console.
Definition: dp_misc.cxx:473
void writeConsoleError(std::u16string_view sText)
writes the argument to the console using the error stream.
Definition: dp_misc.cxx:468
bool office_is_running()
Definition: dp_misc.cxx:331
void TRACE(OUString const &sText)
print the text to the console in a debug build.
Definition: dp_misc.cxx:486
Reference< XInterface > resolveUnoURL(OUString const &connectString, Reference< XComponentContext > const &xLocalContext, AbortChannel const *abortChannel)
Definition: dp_misc.cxx:429
static void writeConsoleWithStream(std::u16string_view sText, FILE *stream)
Definition: dp_misc.cxx:456
OUString generateRandomPipeId()
Definition: dp_misc.cxx:410
OUString expandUnoRcTerm(OUString const &term_)
Definition: dp_misc.cxx:294
void writeConsole(std::u16string_view sText)
writes the argument string to the console.
Definition: dp_misc.cxx:463
oslProcess raiseProcess(OUString const &appURL, Sequence< OUString > const &args)
Definition: dp_misc.cxx:374
void disposeBridges(Reference< css::uno::XComponentContext > const &ctx)
Definition: dp_misc.cxx:530
OUString makeURLAppendSysPathSegment(std::u16string_view baseURL, OUString const &segment)
appends a relative path to a url.
Definition: dp_misc.cxx:283
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
int i
ctx
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
args
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
comp
std::vector< sal_uInt8 > bytes
unsigned char sal_uInt8
sal_uInt16 sal_Unicode
size_t pos