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