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