Line data Source code
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 :
23 : #include "dp_misc.h"
24 : #include "dp_version.hxx"
25 : #include "dp_interact.h"
26 : #include <rtl/uri.hxx>
27 : #include <rtl/digest.h>
28 : #include <rtl/random.h>
29 : #include <rtl/bootstrap.hxx>
30 : #include <sal/log.hxx>
31 : #include <unotools/bootstrap.hxx>
32 : #include <osl/file.hxx>
33 : #include <osl/pipe.hxx>
34 : #include <osl/security.hxx>
35 : #include <osl/thread.hxx>
36 : #include <osl/mutex.hxx>
37 : #include <com/sun/star/ucb/CommandAbortedException.hpp>
38 : #include <com/sun/star/task/XInteractionHandler.hpp>
39 : #include <com/sun/star/bridge/BridgeFactory.hpp>
40 : #include <com/sun/star/bridge/UnoUrlResolver.hpp>
41 : #include <com/sun/star/bridge/XUnoUrlResolver.hpp>
42 : #include <com/sun/star/deployment/ExtensionManager.hpp>
43 : #include <com/sun/star/task/OfficeRestartManager.hpp>
44 : #include <boost/scoped_array.hpp>
45 : #include <boost/shared_ptr.hpp>
46 : #include <comphelper/lok.hxx>
47 : #include <comphelper/processfactory.hxx>
48 : #include <salhelper/linkhelper.hxx>
49 :
50 : #ifdef WNT
51 : #define UNICODE
52 : #define WIN32_LEAN_AND_MEAN
53 : #include <windows.h>
54 : #endif
55 :
56 : using namespace ::com::sun::star;
57 : using namespace ::com::sun::star::uno;
58 :
59 : #if defined WNT
60 : #define SOFFICE1 "soffice.exe"
61 : #define SBASE "sbase.exe"
62 : #define SCALC "scalc.exe"
63 : #define SDRAW "sdraw.exe"
64 : #define SIMPRESS "simpress.exe"
65 : #define SWRITER "swriter.exe"
66 : #endif
67 :
68 : #ifdef MACOSX
69 : #define SOFFICE2 "soffice"
70 : #else
71 : #define SOFFICE2 "soffice.bin"
72 : #endif
73 :
74 : namespace dp_misc {
75 : namespace {
76 :
77 : struct UnoRc : public rtl::StaticWithInit<
78 : boost::shared_ptr<rtl::Bootstrap>, UnoRc> {
79 108 : const boost::shared_ptr<rtl::Bootstrap> operator () () {
80 108 : OUString unorc( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("louno") );
81 108 : ::rtl::Bootstrap::expandMacros( unorc );
82 : ::boost::shared_ptr< ::rtl::Bootstrap > ret(
83 108 : new ::rtl::Bootstrap( unorc ) );
84 : OSL_ASSERT( ret->getHandle() != 0 );
85 108 : return ret;
86 : }
87 : };
88 :
89 : struct OfficePipeId : public rtl::StaticWithInit<OUString, OfficePipeId> {
90 : const OUString operator () ();
91 : };
92 :
93 0 : const OUString OfficePipeId::operator () ()
94 : {
95 0 : OUString userPath;
96 : ::utl::Bootstrap::PathStatus aLocateResult =
97 0 : ::utl::Bootstrap::locateUserInstallation( userPath );
98 0 : if (!(aLocateResult == ::utl::Bootstrap::PATH_EXISTS ||
99 0 : aLocateResult == ::utl::Bootstrap::PATH_VALID))
100 : {
101 0 : throw Exception("Extension Manager: Could not obtain path for UserInstallation.", 0);
102 : }
103 :
104 0 : rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
105 0 : if (!digest) {
106 0 : throw RuntimeException("cannot get digest rtl_Digest_AlgorithmMD5!", 0 );
107 : }
108 :
109 : sal_uInt8 const * data =
110 0 : reinterpret_cast<sal_uInt8 const *>(userPath.getStr());
111 0 : sal_Size size = (userPath.getLength() * sizeof (sal_Unicode));
112 0 : sal_uInt32 md5_key_len = rtl_digest_queryLength( digest );
113 0 : ::boost::scoped_array<sal_uInt8> md5_buf( new sal_uInt8 [ md5_key_len ] );
114 :
115 0 : rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) );
116 0 : rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) );
117 0 : rtl_digest_get( digest, md5_buf.get(), md5_key_len );
118 0 : rtl_digest_destroy( digest );
119 :
120 : // create hex-value string from the MD5 value to keep
121 : // the string size minimal
122 0 : OUStringBuffer buf;
123 0 : buf.appendAscii( "SingleOfficeIPC_" );
124 0 : for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) {
125 0 : buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 );
126 : }
127 0 : return buf.makeStringAndClear();
128 : }
129 :
130 0 : bool existsOfficePipe()
131 : {
132 0 : OUString const & pipeId = OfficePipeId::get();
133 0 : if (pipeId.isEmpty())
134 0 : return false;
135 0 : ::osl::Security sec;
136 0 : ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
137 0 : return pipe.is();
138 : }
139 :
140 : //get modification time
141 252 : static bool getModifyTimeTargetFile(const OUString &rFileURL, TimeValue &rTime)
142 : {
143 252 : salhelper::LinkResolver aResolver(osl_FileStatus_Mask_ModifyTime);
144 :
145 252 : if (aResolver.fetchFileStatus(rFileURL) != osl::FileBase::E_None)
146 0 : return false;
147 :
148 252 : rTime = aResolver.m_aStatus.getModifyTime();
149 :
150 252 : return true;
151 : }
152 :
153 : //Returns true if the Folder was more recently modified then
154 : //the lastsynchronized file. That is the repository needs to
155 : //be synchronized.
156 126 : bool compareExtensionFolderWithLastSynchronizedFile(
157 : OUString const & folderURL, OUString const & fileURL)
158 : {
159 126 : bool bNeedsSync = false;
160 126 : ::osl::DirectoryItem itemExtFolder;
161 : ::osl::File::RC err1 =
162 126 : ::osl::DirectoryItem::get(folderURL, itemExtFolder);
163 : //If it does not exist, then there is nothing to be done
164 126 : if (err1 == ::osl::File::E_NOENT)
165 : {
166 0 : return false;
167 : }
168 126 : else if (err1 != ::osl::File::E_None)
169 : {
170 : OSL_FAIL("Cannot access extension folder");
171 0 : return true; //sync just in case
172 : }
173 :
174 : //If last synchronized does not exist, then OOo is started for the first time
175 252 : ::osl::DirectoryItem itemFile;
176 126 : ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile);
177 126 : if (err2 == ::osl::File::E_NOENT)
178 : {
179 0 : return true;
180 :
181 : }
182 126 : else if (err2 != ::osl::File::E_None)
183 : {
184 : OSL_FAIL("Cannot access file lastsynchronized");
185 0 : return true; //sync just in case
186 : }
187 :
188 : //compare the modification time of the extension folder and the last
189 : //modified file
190 : TimeValue timeFolder;
191 126 : if (getModifyTimeTargetFile(folderURL, timeFolder))
192 : {
193 : TimeValue timeFile;
194 126 : if (getModifyTimeTargetFile(fileURL, timeFile))
195 : {
196 126 : if (timeFile.Seconds < timeFolder.Seconds)
197 0 : bNeedsSync = true;
198 : }
199 : else
200 : {
201 : OSL_ASSERT(false);
202 0 : bNeedsSync = true;
203 : }
204 : }
205 : else
206 : {
207 : OSL_ASSERT(false);
208 0 : bNeedsSync = true;
209 : }
210 :
211 252 : return bNeedsSync;
212 : }
213 :
214 126 : bool needToSyncRepository(OUString const & name)
215 : {
216 126 : OUString folder;
217 252 : OUString file;
218 126 : if ( name == "bundled" )
219 : {
220 63 : folder = "$BUNDLED_EXTENSIONS";
221 63 : file = "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
222 : }
223 63 : else if ( name == "shared" )
224 : {
225 63 : folder = "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
226 63 : file = "$SHARED_EXTENSIONS_USER/lastsynchronized";
227 : }
228 : else
229 : {
230 : OSL_ASSERT(false);
231 0 : return true;
232 : }
233 126 : ::rtl::Bootstrap::expandMacros(folder);
234 126 : ::rtl::Bootstrap::expandMacros(file);
235 : return compareExtensionFolderWithLastSynchronizedFile(
236 252 : folder, file);
237 : }
238 :
239 :
240 : } // anon namespace
241 :
242 :
243 :
244 : namespace {
245 6709 : inline OUString encodeForRcFile( OUString const & str )
246 : {
247 : // escape $\{} (=> rtl bootstrap files)
248 6709 : OUStringBuffer buf;
249 6709 : sal_Int32 pos = 0;
250 6709 : const sal_Int32 len = str.getLength();
251 241500 : for ( ; pos < len; ++pos ) {
252 234791 : sal_Unicode c = str[ pos ];
253 234791 : switch (c) {
254 : case '$':
255 : case '\\':
256 : case '{':
257 : case '}':
258 0 : buf.append( '\\' );
259 0 : break;
260 : }
261 234791 : buf.append( c );
262 : }
263 6709 : return buf.makeStringAndClear();
264 : }
265 : }
266 :
267 :
268 7503 : OUString makeURL( OUString const & baseURL, OUString const & relPath_ )
269 : {
270 7503 : OUStringBuffer buf;
271 7503 : if (baseURL.getLength() > 1 && baseURL[ baseURL.getLength() - 1 ] == '/')
272 0 : buf.append( baseURL.copy( 0, baseURL.getLength() - 1 ) );
273 : else
274 7503 : buf.append( baseURL );
275 15006 : OUString relPath(relPath_);
276 7503 : if( relPath.startsWith("/") )
277 0 : relPath = relPath.copy( 1 );
278 7503 : if (!relPath.isEmpty())
279 : {
280 6719 : buf.append( '/' );
281 6719 : if (baseURL.match( "vnd.sun.star.expand:" )) {
282 : // encode for macro expansion: relPath is supposed to have no
283 : // macros, so encode $, {} \ (bootstrap mimic)
284 6709 : relPath = encodeForRcFile(relPath);
285 :
286 : // encode once more for vnd.sun.star.expand schema:
287 : // vnd.sun.star.expand:$UNO_...
288 : // will expand to file-url
289 13418 : relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric,
290 : rtl_UriEncodeIgnoreEscapes,
291 6709 : RTL_TEXTENCODING_UTF8 );
292 : }
293 6719 : buf.append( relPath );
294 : }
295 15006 : return buf.makeStringAndClear();
296 : }
297 :
298 3 : OUString makeURLAppendSysPathSegment( OUString const & baseURL, OUString const & relPath_ )
299 : {
300 3 : OUString segment = relPath_;
301 : OSL_ASSERT(segment.indexOf(static_cast<sal_Unicode>('/')) == -1);
302 :
303 : ::rtl::Uri::encode(
304 : segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
305 3 : RTL_TEXTENCODING_UTF8);
306 3 : return makeURL(baseURL, segment);
307 : }
308 :
309 :
310 :
311 :
312 0 : OUString expandUnoRcTerm( OUString const & term_ )
313 : {
314 0 : OUString term(term_);
315 0 : UnoRc::get()->expandMacrosFrom( term );
316 0 : return term;
317 : }
318 :
319 5 : OUString makeRcTerm( OUString const & url )
320 : {
321 : OSL_ASSERT( url.match( "vnd.sun.star.expand:" ));
322 5 : if (url.match( "vnd.sun.star.expand:" )) {
323 : // cut protocol:
324 5 : OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
325 : // decode uric class chars:
326 10 : rcterm = ::rtl::Uri::decode(
327 5 : rcterm, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
328 5 : return rcterm;
329 : }
330 : else
331 0 : return url;
332 : }
333 :
334 :
335 3924 : OUString expandUnoRcUrl( OUString const & url )
336 : {
337 3924 : if (url.match( "vnd.sun.star.expand:" )) {
338 : // cut protocol:
339 3582 : OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
340 : // decode uric class chars:
341 7164 : rcurl = ::rtl::Uri::decode(
342 3582 : rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
343 : // expand macro string:
344 3582 : UnoRc::get()->expandMacrosFrom( rcurl );
345 3582 : return rcurl;
346 : }
347 : else {
348 342 : return url;
349 : }
350 : }
351 :
352 :
353 0 : bool office_is_running()
354 : {
355 : //We need to check if we run within the office process. Then we must not use the pipe, because
356 : //this could cause a deadlock. This is actually a workaround for i82778
357 0 : OUString sFile;
358 0 : oslProcessError err = osl_getExecutableFile(& sFile.pData);
359 0 : bool ret = false;
360 0 : if (osl_Process_E_None == err)
361 : {
362 0 : sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
363 0 : if (
364 : #if defined UNIX
365 : sFile == SOFFICE2
366 : #elif defined WNT
367 : //osl_getExecutableFile should deliver "soffice.bin" on windows
368 : //even if swriter.exe, scalc.exe etc. was started. This is a bug
369 : //in osl_getExecutableFile
370 : sFile == SOFFICE1 || sFile == SOFFICE2 || sFile == SBASE || sFile == SCALC
371 : || sFile == SDRAW || sFile == SIMPRESS || sFile == SWRITER
372 : #else
373 : #error "Unsupported platform"
374 : #endif
375 :
376 : )
377 0 : ret = true;
378 : else
379 0 : ret = existsOfficePipe();
380 : }
381 : else
382 : {
383 : OSL_FAIL("NOT osl_Process_E_None ");
384 : //if osl_getExecutable file than we take the risk of creating a pipe
385 0 : ret = existsOfficePipe();
386 : }
387 0 : return ret;
388 : }
389 :
390 :
391 1 : oslProcess raiseProcess(
392 : OUString const & appURL, Sequence<OUString> const & args )
393 : {
394 1 : ::osl::Security sec;
395 1 : oslProcess hProcess = 0;
396 : oslProcessError rc = osl_executeProcess(
397 : appURL.pData,
398 : reinterpret_cast<rtl_uString **>(
399 1 : const_cast<OUString *>(args.getConstArray()) ),
400 1 : args.getLength(),
401 : osl_Process_DETACHED,
402 : sec.getHandle(),
403 : 0, // => current working dir
404 : 0, 0, // => no env vars
405 3 : &hProcess );
406 :
407 1 : switch (rc) {
408 : case osl_Process_E_None:
409 1 : break;
410 : case osl_Process_E_NotFound:
411 0 : throw RuntimeException( "image not found!", 0 );
412 : case osl_Process_E_TimedOut:
413 0 : throw RuntimeException( "timeout occurred!", 0 );
414 : case osl_Process_E_NoPermission:
415 0 : throw RuntimeException( "permission denied!", 0 );
416 : case osl_Process_E_Unknown:
417 0 : throw RuntimeException( "unknown error!", 0 );
418 : case osl_Process_E_InvalidError:
419 : default:
420 0 : throw RuntimeException( "unmapped error!", 0 );
421 : }
422 :
423 1 : return hProcess;
424 : }
425 :
426 :
427 1 : OUString generateRandomPipeId()
428 : {
429 : // compute some good pipe id:
430 1 : static rtlRandomPool s_hPool = rtl_random_createPool();
431 1 : if (s_hPool == 0)
432 0 : throw RuntimeException( "cannot create random pool!?", 0 );
433 : sal_uInt8 bytes[ 32 ];
434 1 : if (rtl_random_getBytes(
435 1 : s_hPool, bytes, ARLEN(bytes) ) != rtl_Random_E_None) {
436 0 : throw RuntimeException( "random pool error!?", 0 );
437 : }
438 1 : OUStringBuffer buf;
439 33 : for ( sal_uInt32 i = 0; i < ARLEN(bytes); ++i ) {
440 32 : buf.append( static_cast<sal_Int32>(bytes[ i ]), 0x10 );
441 : }
442 1 : return buf.makeStringAndClear();
443 : }
444 :
445 :
446 1 : Reference<XInterface> resolveUnoURL(
447 : OUString const & connectString,
448 : Reference<XComponentContext> const & xLocalContext,
449 : AbortChannel * abortChannel )
450 : {
451 : Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
452 1 : bridge::UnoUrlResolver::create( xLocalContext ) );
453 :
454 6 : for (int i = 0; i <= 20; ++i) // 10 seconds
455 : {
456 3 : if (abortChannel != 0 && abortChannel->isAborted()) {
457 0 : throw ucb::CommandAbortedException( "abort!" );
458 : }
459 : try {
460 3 : return xUnoUrlResolver->resolve( connectString );
461 : }
462 2 : catch (const connection::NoConnectException &) {
463 2 : if (i < 20)
464 : {
465 2 : TimeValue tv = { 0 /* secs */, 500000000 /* nanosecs */ };
466 2 : ::osl::Thread::wait( tv );
467 : }
468 0 : else throw;
469 : }
470 : }
471 0 : return 0; // warning C4715
472 : }
473 :
474 : #ifdef WNT
475 : void writeConsoleWithStream(OUString const & sText, HANDLE stream)
476 : {
477 : DWORD nWrittenChars = 0;
478 : WriteFile(stream, sText.getStr(),
479 : sText.getLength() * 2, &nWrittenChars, NULL);
480 : }
481 : #else
482 0 : void writeConsoleWithStream(OUString const & sText, FILE * stream)
483 : {
484 0 : OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
485 0 : fprintf(stream, "%s", s.getStr());
486 0 : fflush(stream);
487 0 : }
488 : #endif
489 :
490 0 : void writeConsole(OUString const & sText)
491 : {
492 : #ifdef WNT
493 : writeConsoleWithStream(sText, GetStdHandle(STD_OUTPUT_HANDLE));
494 : #else
495 0 : writeConsoleWithStream(sText, stdout);
496 : #endif
497 0 : }
498 :
499 0 : void writeConsoleError(OUString const & sText)
500 : {
501 : #ifdef WNT
502 : writeConsoleWithStream(sText, GetStdHandle(STD_ERROR_HANDLE));
503 : #else
504 0 : writeConsoleWithStream(sText, stderr);
505 : #endif
506 0 : }
507 :
508 0 : OUString readConsole()
509 : {
510 : #ifdef WNT
511 : sal_Unicode aBuffer[1024];
512 : DWORD dwRead = 0;
513 : //unopkg.com feeds unopkg.exe with wchar_t|s
514 : if (ReadFile( GetStdHandle(STD_INPUT_HANDLE), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
515 : {
516 : OSL_ASSERT((dwRead % 2) == 0);
517 : OUString value( aBuffer, dwRead / 2);
518 : return value.trim();
519 : }
520 : #else
521 : char buf[1024];
522 0 : memset(buf, 0, 1024);
523 : // read one char less so that the last char in buf is always zero
524 0 : if (fgets(buf, 1024, stdin) != NULL)
525 : {
526 0 : OUString value = OStringToOUString(OString(buf), osl_getThreadTextEncoding());
527 0 : return value.trim();
528 : }
529 : #endif
530 0 : return OUString();
531 : }
532 :
533 0 : void TRACE(OUString const & sText)
534 : {
535 : SAL_INFO("desktop.deployment", sText);
536 0 : }
537 :
538 116 : void syncRepositories(
539 : bool force, Reference<ucb::XCommandEnvironment> const & xCmdEnv)
540 : {
541 116 : OUString sDisable;
542 116 : ::rtl::Bootstrap::get( "DISABLE_EXTENSION_SYNCHRONIZATION", sDisable, OUString() );
543 116 : if (!sDisable.isEmpty())
544 116 : return;
545 :
546 232 : Reference<deployment::XExtensionManager> xExtensionManager;
547 : //synchronize shared before bundled otherewise there are
548 : //more revoke and registration calls.
549 116 : bool bModified = false;
550 116 : if (force || needToSyncRepository("shared") || needToSyncRepository("bundled"))
551 : {
552 106 : xExtensionManager =
553 : deployment::ExtensionManager::get(
554 53 : comphelper::getProcessComponentContext());
555 :
556 53 : if (xExtensionManager.is())
557 : {
558 53 : bModified = xExtensionManager->synchronize(
559 53 : Reference<task::XAbortChannel>(), xCmdEnv);
560 : }
561 : }
562 : #if !HAVE_FEATURE_MACOSX_SANDBOX
563 116 : if (bModified && !comphelper::LibreOfficeKit::isActive())
564 : {
565 0 : Reference<task::XRestartManager> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
566 0 : if (restarter.is())
567 : {
568 : OSL_TRACE( "Request restart for modified extensions manager" );
569 0 : restarter->requestRestart(xCmdEnv.is() ? xCmdEnv->getInteractionHandler() :
570 0 : Reference<task::XInteractionHandler>());
571 0 : }
572 116 : }
573 : #endif
574 : }
575 :
576 0 : void disposeBridges(Reference<css::uno::XComponentContext> const & ctx)
577 : {
578 0 : if (!ctx.is())
579 0 : return;
580 :
581 0 : Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(ctx) );
582 :
583 0 : const Sequence< Reference<css::bridge::XBridge> >seqBridges = bridgeFac->getExistingBridges();
584 0 : for (sal_Int32 i = 0; i < seqBridges.getLength(); i++)
585 : {
586 0 : Reference<css::lang::XComponent> comp(seqBridges[i], UNO_QUERY);
587 0 : if (comp.is())
588 : {
589 : try {
590 0 : comp->dispose();
591 : }
592 0 : catch ( const css::lang::DisposedException& )
593 : {
594 : }
595 : }
596 0 : }
597 : }
598 :
599 : }
600 :
601 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|