Get the style color and number just once
[LibreOffice.git] / desktop / source / deployment / misc / dp_misc.cxx
blob79a8f2a5878749f81bf6e1a4cf8bb70e06ec8c9b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
23 #include <dp_misc.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>
47 #include <memory>
48 #include <string_view>
49 #include <thread>
50 #include <comphelper/lok.hxx>
51 #include <comphelper/processfactory.hxx>
52 #include <salhelper/linkhelper.hxx>
54 #ifdef _WIN32
55 #include <prewin.h>
56 #include <postwin.h>
57 #endif
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::uno;
62 namespace dp_misc {
63 namespace {
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 );
73 return ret;
74 }();
75 return theRc;
78 OUString generateOfficePipeId()
80 OUString userPath;
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 );
90 if (!digest) {
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())
120 return false;
121 ::osl::Security sec;
122 ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
123 return pipe.is();
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)
132 return false;
134 rTime = aResolver.m_aStatus.getModifyTime();
136 return true;
139 //Returns true if the Folder was more recently modified then
140 //the lastsynchronized file. That is the repository needs to
141 //be synchronized.
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)
152 return false;
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)
165 return true;
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
175 //modified file
176 TimeValue timeFolder;
177 if (getModifyTimeTargetFile(folderURL, timeFolder))
179 TimeValue timeFile;
180 if (getModifyTimeTargetFile(fileURL, timeFile))
182 if (timeFile.Seconds < timeFolder.Seconds)
183 bNeedsSync = true;
185 else
187 OSL_ASSERT(false);
188 bNeedsSync = true;
191 else
193 OSL_ASSERT(false);
194 bNeedsSync = true;
197 return bNeedsSync;
200 bool needToSyncRepository(std::u16string_view name)
202 OUString folder;
203 OUString file;
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";
214 else
216 OSL_ASSERT(false);
217 return true;
219 ::rtl::Bootstrap::expandMacros(folder);
220 ::rtl::Bootstrap::expandMacros(file);
221 return compareExtensionFolderWithLastSynchronizedFile(
222 folder, file);
226 } // anon namespace
229 namespace {
230 OUString encodeForRcFile( std::u16string_view str )
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;
246 buf.append( c );
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) );
258 else
259 buf.append( baseURL );
260 OUString relPath(relPath_);
261 if( relPath.startsWith("/") )
262 relPath = relPath.copy( 1 );
263 if (!relPath.isEmpty())
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);
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);
287 ::rtl::Uri::encode(
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 );
298 return 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 );
308 return rcterm;
310 else
311 return url;
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 );
323 return rcurl;
325 else {
326 return url;
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
335 OUString sFile;
336 oslProcessError err = osl_getExecutableFile(& sFile.pData);
337 bool ret = false;
338 if (osl_Process_E_None == err)
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
360 ret = true;
361 else
362 ret = existsOfficePipe();
364 else
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();
370 return ret;
374 oslProcess raiseProcess(
375 OUString const & appURL, Sequence<OUString> const & args )
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 );
390 switch (rc) {
391 case osl_Process_E_None:
392 break;
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:
402 default:
403 throw RuntimeException( u"unmapped error!"_ustr, nullptr );
406 return hProcess;
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 );
418 OUStringBuffer buf;
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 );
439 try {
440 return xUnoUrlResolver->resolve( connectString );
442 catch (const connection::NoConnectException &) {
443 if (i < 40)
445 std::this_thread::sleep_for( std::chrono::milliseconds(500) );
447 else throw;
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());
457 fflush(stream);
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()
472 char buf[1024];
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());
478 return value.trim();
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)
491 OUString sDisable;
492 ::rtl::Bootstrap::get( u"DISABLE_EXTENSION_SYNCHRONIZATION"_ustr, sDisable, OUString() );
493 if (!sDisable.isEmpty())
494 return;
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"))
502 xExtensionManager =
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
513 (void) bModified;
514 #else
515 if (bModified && !comphelper::LibreOfficeKit::isActive())
517 Reference<task::XRestartManager> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
518 if (restarter.is())
520 restarter->requestRestart(xCmdEnv.is() ? xCmdEnv->getInteractionHandler() :
521 Reference<task::XInteractionHandler>());
524 #endif
527 void disposeBridges(Reference<css::uno::XComponentContext> const & ctx)
529 if (!ctx.is())
530 return;
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);
538 if (comp.is())
540 try {
541 comp->dispose();
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: */