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