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