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 <comphelper/lok.hxx>
52
53#ifdef _WIN32
54#include <prewin.h>
55#include <postwin.h>
56#endif
57
58using namespace ::com::sun::star;
59using namespace ::com::sun::star::uno;
60
61namespace dp_misc {
62namespace {
63
64struct UnoRc : public rtl::StaticWithInit<
65 std::shared_ptr<rtl::Bootstrap>, UnoRc> {
66 std::shared_ptr<rtl::Bootstrap> operator () () {
67 OUString unorc( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("louno") );
68 ::rtl::Bootstrap::expandMacros( unorc );
69 auto ret = std::make_shared<::rtl::Bootstrap>( unorc );
70 OSL_ASSERT( ret->getHandle() != nullptr );
71 return ret;
72 }
73};
74
75OUString generateOfficePipeId()
76{
77 OUString userPath;
78 ::utl::Bootstrap::PathStatus aLocateResult =
80 if (aLocateResult != ::utl::Bootstrap::PATH_EXISTS &&
81 aLocateResult != ::utl::Bootstrap::PATH_VALID)
82 {
83 throw Exception("Extension Manager: Could not obtain path for UserInstallation.", nullptr);
84 }
85
86 rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
87 if (!digest) {
88 throw RuntimeException("cannot get digest rtl_Digest_AlgorithmMD5!", nullptr );
89 }
90
91 sal_uInt8 const * data =
92 reinterpret_cast<sal_uInt8 const *>(userPath.getStr());
93 std::size_t size = userPath.getLength() * sizeof (sal_Unicode);
94 sal_uInt32 md5_key_len = rtl_digest_queryLength( digest );
95 std::unique_ptr<sal_uInt8[]> md5_buf( new sal_uInt8 [ md5_key_len ] );
96
97 rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) );
98 rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) );
99 rtl_digest_get( digest, md5_buf.get(), md5_key_len );
100 rtl_digest_destroy( digest );
101
102 // create hex-value string from the MD5 value to keep
103 // the string size minimal
104 OUStringBuffer buf;
105 buf.append( "SingleOfficeIPC_" );
106 for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) {
107 buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 );
108 }
109 return buf.makeStringAndClear();
110}
111
112bool existsOfficePipe()
113{
114 static OUString OfficePipeId = generateOfficePipeId();
115
116 OUString const & pipeId = OfficePipeId;
117 if (pipeId.isEmpty())
118 return false;
119 ::osl::Security sec;
120 ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
121 return pipe.is();
122}
123
124//get modification time
125bool getModifyTimeTargetFile(const OUString &rFileURL, TimeValue &rTime)
126{
127 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_ModifyTime);
128
129 if (aResolver.fetchFileStatus(rFileURL) != osl::FileBase::E_None)
130 return false;
131
132 rTime = aResolver.m_aStatus.getModifyTime();
133
134 return true;
135}
136
137//Returns true if the Folder was more recently modified then
138//the lastsynchronized file. That is the repository needs to
139//be synchronized.
140bool compareExtensionFolderWithLastSynchronizedFile(
141 OUString const & folderURL, OUString const & fileURL)
142{
143 bool bNeedsSync = false;
144 ::osl::DirectoryItem itemExtFolder;
145 ::osl::File::RC err1 =
146 ::osl::DirectoryItem::get(folderURL, itemExtFolder);
147 //If it does not exist, then there is nothing to be done
148 if (err1 == ::osl::File::E_NOENT)
149 {
150 return false;
151 }
152 else if (err1 != ::osl::File::E_None)
153 {
154 OSL_FAIL("Cannot access extension folder");
155 return true; //sync just in case
156 }
157
158 //If last synchronized does not exist, then OOo is started for the first time
159 ::osl::DirectoryItem itemFile;
160 ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile);
161 if (err2 == ::osl::File::E_NOENT)
162 {
163 return true;
164
165 }
166 else if (err2 != ::osl::File::E_None)
167 {
168 OSL_FAIL("Cannot access file lastsynchronized");
169 return true; //sync just in case
170 }
171
172 //compare the modification time of the extension folder and the last
173 //modified file
174 TimeValue timeFolder;
175 if (getModifyTimeTargetFile(folderURL, timeFolder))
176 {
177 TimeValue timeFile;
178 if (getModifyTimeTargetFile(fileURL, timeFile))
179 {
180 if (timeFile.Seconds < timeFolder.Seconds)
181 bNeedsSync = true;
182 }
183 else
184 {
185 OSL_ASSERT(false);
186 bNeedsSync = true;
187 }
188 }
189 else
190 {
191 OSL_ASSERT(false);
192 bNeedsSync = true;
193 }
194
195 return bNeedsSync;
196}
197
198bool needToSyncRepository(std::u16string_view name)
199{
200 OUString folder;
201 OUString file;
202 if ( name == u"bundled" )
203 {
204 folder = "$BUNDLED_EXTENSIONS";
205 file = "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
206 }
207 else if ( name == u"shared" )
208 {
209 folder = "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
210 file = "$SHARED_EXTENSIONS_USER/lastsynchronized";
211 }
212 else
213 {
214 OSL_ASSERT(false);
215 return true;
216 }
217 ::rtl::Bootstrap::expandMacros(folder);
218 ::rtl::Bootstrap::expandMacros(file);
219 return compareExtensionFolderWithLastSynchronizedFile(
220 folder, file);
221}
222
223
224} // anon namespace
225
226
227namespace {
228OUString encodeForRcFile( std::u16string_view str )
229{
230 // escape $\{} (=> rtl bootstrap files)
231 OUStringBuffer buf(64);
232 size_t pos = 0;
233 const size_t len = str.size();
234 for ( ; pos < len; ++pos ) {
235 sal_Unicode c = str[ pos ];
236 switch (c) {
237 case '$':
238 case '\\':
239 case '{':
240 case '}':
241 buf.append( '\\' );
242 break;
243 }
244 buf.append( c );
245 }
246 return buf.makeStringAndClear();
247}
248}
249
250
251OUString makeURL( std::u16string_view baseURL, OUString const & relPath_ )
252{
253 OUStringBuffer buf(128);
254 if (baseURL.size() > 1 && baseURL[ baseURL.size() - 1 ] == '/')
255 buf.append( baseURL.substr(0, baseURL.size() - 1) );
256 else
257 buf.append( baseURL );
258 OUString relPath(relPath_);
259 if( relPath.startsWith("/") )
260 relPath = relPath.copy( 1 );
261 if (!relPath.isEmpty())
262 {
263 buf.append( '/' );
264 if (o3tl::starts_with(baseURL, u"vnd.sun.star.expand:" )) {
265 // encode for macro expansion: relPath is supposed to have no
266 // macros, so encode $, {} \ (bootstrap mimic)
267 relPath = encodeForRcFile(relPath);
268
269 // encode once more for vnd.sun.star.expand schema:
270 // vnd.sun.star.expand:$UNO_...
271 // will expand to file-url
272 relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric,
273 rtl_UriEncodeIgnoreEscapes,
274 RTL_TEXTENCODING_UTF8 );
275 }
276 buf.append( relPath );
277 }
278 return buf.makeStringAndClear();
279}
280
281OUString makeURLAppendSysPathSegment( std::u16string_view baseURL, OUString const & segment )
282{
283 OSL_ASSERT(segment.indexOf(u'/') == -1);
284
285 ::rtl::Uri::encode(
286 segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
287 RTL_TEXTENCODING_UTF8);
288 return makeURL(baseURL, segment);
289}
290
291
292OUString expandUnoRcTerm( OUString const & term_ )
293{
294 OUString term(term_);
295 UnoRc::get()->expandMacrosFrom( term );
296 return term;
297}
298
299OUString makeRcTerm( OUString const & url )
300{
301 OSL_ASSERT( url.match( "vnd.sun.star.expand:" ));
302 if (url.match( "vnd.sun.star.expand:" )) {
303 // cut protocol:
304 OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
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 (url.match( "vnd.sun.star.expand:" )) {
318 // cut protocol:
319 OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
320 // decode uric class chars:
321 rcurl = ::rtl::Uri::decode(
322 rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
323 // expand macro string:
324 UnoRc::get()->expandMacrosFrom( rcurl );
325 return rcurl;
326 }
327 else {
328 return url;
329 }
330}
331
332
334{
335 //We need to check if we run within the office process. Then we must not use the pipe, because
336 //this could cause a deadlock. This is actually a workaround for i82778
337 OUString sFile;
338 oslProcessError err = osl_getExecutableFile(& sFile.pData);
339 bool ret = false;
340 if (osl_Process_E_None == err)
341 {
342 sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
343 if (
344#if defined _WIN32
345 //osl_getExecutableFile should deliver "soffice.bin" on windows
346 //even if swriter.exe, scalc.exe etc. was started. This is a bug
347 //in osl_getExecutableFile
348 sFile == "soffice.bin" || sFile == "soffice.exe" || sFile == "soffice.com"
349 || sFile == "soffice" || sFile == "swriter.exe" || sFile == "swriter"
350 || sFile == "scalc.exe" || sFile == "scalc" || sFile == "simpress.exe"
351 || sFile == "simpress" || sFile == "sdraw.exe" || sFile == "sdraw"
352 || sFile == "sbase.exe" || sFile == "sbase"
353#elif defined MACOSX
354 sFile == "soffice"
355#elif defined UNIX
356 sFile == "soffice.bin"
357#else
358#error "Unsupported platform"
359#endif
360
361 )
362 ret = true;
363 else
364 ret = existsOfficePipe();
365 }
366 else
367 {
368 OSL_FAIL("NOT osl_Process_E_None ");
369 //if osl_getExecutable file then we take the risk of creating a pipe
370 ret = existsOfficePipe();
371 }
372 return ret;
373}
374
375
376oslProcess raiseProcess(
377 OUString const & appURL, Sequence<OUString> const & args )
378{
379 ::osl::Security sec;
380 oslProcess hProcess = nullptr;
381 oslProcessError rc = osl_executeProcess(
382 appURL.pData,
383 reinterpret_cast<rtl_uString **>(
384 const_cast<OUString *>(args.getConstArray()) ),
385 args.getLength(),
386 osl_Process_DETACHED,
387 sec.getHandle(),
388 nullptr, // => current working dir
389 nullptr, 0, // => no env vars
390 &hProcess );
391
392 switch (rc) {
393 case osl_Process_E_None:
394 break;
395 case osl_Process_E_NotFound:
396 throw RuntimeException( "image not found!", nullptr );
397 case osl_Process_E_TimedOut:
398 throw RuntimeException( "timeout occurred!", nullptr );
399 case osl_Process_E_NoPermission:
400 throw RuntimeException( "permission denied!", nullptr );
401 case osl_Process_E_Unknown:
402 throw RuntimeException( "unknown error!", nullptr );
403 case osl_Process_E_InvalidError:
404 default:
405 throw RuntimeException( "unmapped error!", nullptr );
406 }
407
408 return hProcess;
409}
410
411
413{
414 // compute some good pipe id:
415 static rtlRandomPool s_hPool = rtl_random_createPool();
416 if (s_hPool == nullptr)
417 throw RuntimeException( "cannot create random pool!?", nullptr );
418 sal_uInt8 bytes[ 32 ];
419 if (rtl_random_getBytes(
420 s_hPool, bytes, std::size(bytes) ) != rtl_Random_E_None) {
421 throw RuntimeException( "random pool error!?", nullptr );
422 }
423 OUStringBuffer buf;
424 for (unsigned char byte : bytes) {
425 buf.append( static_cast<sal_Int32>(byte), 0x10 );
426 }
427 return buf.makeStringAndClear();
428}
429
430
431Reference<XInterface> resolveUnoURL(
432 OUString const & connectString,
433 Reference<XComponentContext> const & xLocalContext,
434 AbortChannel const * abortChannel )
435{
436 Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
437 bridge::UnoUrlResolver::create( xLocalContext ) );
438
439 for (int i = 0; i <= 40; ++i) // 20 seconds
440 {
441 if (abortChannel != nullptr && abortChannel->isAborted()) {
442 throw ucb::CommandAbortedException( "abort!" );
443 }
444 try {
445 return xUnoUrlResolver->resolve( connectString );
446 }
447 catch (const connection::NoConnectException &) {
448 if (i < 40)
449 {
450 ::osl::Thread::wait( std::chrono::milliseconds(500) );
451 }
452 else throw;
453 }
454 }
455 return nullptr; // warning C4715
456}
457
458static void writeConsoleWithStream(std::u16string_view sText, FILE * stream)
459{
460 OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
461 fprintf(stream, "%s", s.getStr());
462 fflush(stream);
463}
464
465void writeConsole(std::u16string_view sText)
466{
467 writeConsoleWithStream(sText, stdout);
468}
469
470void writeConsoleError(std::u16string_view sText)
471{
472 writeConsoleWithStream(sText, stderr);
473}
474
475OUString readConsole()
476{
477 char buf[1024];
478 memset(buf, 0, 1024);
479 // read one char less so that the last char in buf is always zero
480 if (fgets(buf, 1024, stdin) != nullptr)
481 {
482 OUString value = OStringToOUString(std::string_view(buf), osl_getThreadTextEncoding());
483 return value.trim();
484 }
485 throw css::uno::RuntimeException("reading from stdin failed");
486}
487
488void TRACE(OUString const & sText)
489{
490 SAL_INFO("desktop.deployment", sText);
491}
492
494 bool force, Reference<ucb::XCommandEnvironment> const & xCmdEnv)
495{
496 OUString sDisable;
497 ::rtl::Bootstrap::get( "DISABLE_EXTENSION_SYNCHRONIZATION", sDisable, OUString() );
498 if (!sDisable.isEmpty())
499 return;
500
501 Reference<deployment::XExtensionManager> xExtensionManager;
502 //synchronize shared before bundled otherwise there are
503 //more revoke and registration calls.
504 bool bModified = false;
505 if (force || needToSyncRepository(u"shared") || needToSyncRepository(u"bundled"))
506 {
507 xExtensionManager =
508 deployment::ExtensionManager::get(
510
511 if (xExtensionManager.is())
512 {
513 bModified = xExtensionManager->synchronize(
514 Reference<task::XAbortChannel>(), xCmdEnv);
515 }
516 }
517#if !HAVE_FEATURE_MACOSX_SANDBOX
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:251
void syncRepositories(bool force, Reference< ucb::XCommandEnvironment > const &xCmdEnv)
Definition: dp_misc.cxx:493
OUString makeRcTerm(OUString const &url)
Definition: dp_misc.cxx:299
OUString expandUnoRcUrl(OUString const &url)
Definition: dp_misc.cxx:315
OUString readConsole()
reads from the console.
Definition: dp_misc.cxx:475
void writeConsoleError(std::u16string_view sText)
writes the argument to the console using the error stream.
Definition: dp_misc.cxx:470
bool office_is_running()
Definition: dp_misc.cxx:333
void TRACE(OUString const &sText)
print the text to the console in a debug build.
Definition: dp_misc.cxx:488
Reference< XInterface > resolveUnoURL(OUString const &connectString, Reference< XComponentContext > const &xLocalContext, AbortChannel const *abortChannel)
Definition: dp_misc.cxx:431
static void writeConsoleWithStream(std::u16string_view sText, FILE *stream)
Definition: dp_misc.cxx:458
OUString generateRandomPipeId()
Definition: dp_misc.cxx:412
OUString expandUnoRcTerm(OUString const &term_)
Definition: dp_misc.cxx:292
void writeConsole(std::u16string_view sText)
writes the argument string to the console.
Definition: dp_misc.cxx:465
oslProcess raiseProcess(OUString const &appURL, Sequence< OUString > const &args)
Definition: dp_misc.cxx:376
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:281
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