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 <sal/config.h>
21
22 #include <sal/log.hxx>
23 #include <sfx2/docfilt.hxx>
24 #include <sfx2/fcontnr.hxx>
25 #include <svl/fstathelper.hxx>
26
27 #include <app.hxx>
28 #include "dispatchwatcher.hxx"
29 #include "officeipcthread.hxx"
30 #include <rtl/ustring.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/synchronousdispatch.hxx>
33 #include <com/sun/star/io/IOException.hpp>
34 #include <com/sun/star/util/XCloseable.hpp>
35 #include <com/sun/star/util/CloseVetoException.hpp>
36 #include <com/sun/star/task/InteractionHandler.hpp>
37 #include <com/sun/star/util/URL.hpp>
38 #include <com/sun/star/frame/Desktop.hpp>
39 #include <com/sun/star/container/XContainerQuery.hpp>
40 #include <com/sun/star/container/XEnumeration.hpp>
41 #include <com/sun/star/frame/XDispatch.hpp>
42 #include <com/sun/star/frame/XNotifyingDispatch.hpp>
43 #include <com/sun/star/beans/PropertyValue.hpp>
44 #include <com/sun/star/view/XPrintable.hpp>
45 #include <com/sun/star/util/URLTransformer.hpp>
46 #include <com/sun/star/util/XURLTransformer.hpp>
47 #include <com/sun/star/document/MacroExecMode.hpp>
48 #include <com/sun/star/document/XTypeDetection.hpp>
49 #include <com/sun/star/document/UpdateDocMode.hpp>
50 #include <com/sun/star/frame/XStorable.hpp>
51 #include <com/sun/star/script/XLibraryContainer2.hpp>
52 #include <com/sun/star/document/XEmbeddedScripts.hpp>
53
54 #include <comphelper/propertyvalue.hxx>
55 #include <comphelper/sequence.hxx>
56 #include <comphelper/diagnose_ex.hxx>
57 #include <tools/urlobj.hxx>
58 #include <unotools/mediadescriptor.hxx>
59 #include <unotools/tempfile.hxx>
60
61 #include <osl/thread.hxx>
62 #include <osl/file.hxx>
63 #include <iostream>
64 #include <string_view>
65 #include <utility>
66
67 using namespace ::osl;
68 using namespace ::com::sun::star::uno;
69 using namespace ::com::sun::star::util;
70 using namespace ::com::sun::star::lang;
71 using namespace ::com::sun::star::frame;
72 using namespace ::com::sun::star::container;
73 using namespace ::com::sun::star::beans;
74 using namespace ::com::sun::star::view;
75 using namespace ::com::sun::star::task;
76 using namespace ::com::sun::star::document;
77
78 namespace document = ::com::sun::star::document;
79
80 namespace desktop
81 {
82
83 namespace {
84
85 struct DispatchHolder
86 {
DispatchHolderdesktop::__anon328ace6d0111::DispatchHolder87 DispatchHolder( URL _aURL, Reference< XDispatch > const & rDispatch ) :
88 aURL(std::move( _aURL )), xDispatch( rDispatch ) {}
89
90 URL aURL;
91 Reference< XDispatch > xDispatch;
92 };
93
impl_lookupExportFilterForUrl(std::u16string_view rUrl,std::u16string_view rFactory)94 std::shared_ptr<const SfxFilter> impl_lookupExportFilterForUrl( std::u16string_view rUrl, std::u16string_view rFactory )
95 {
96 // create the list of filters
97 OUString sQuery = "getSortedFilterList()"
98 ":module=" +
99 OUString::Concat(rFactory) + // use long name here !
100 ":iflags=" +
101 OUString::number(static_cast<sal_Int32>(SfxFilterFlags::EXPORT)) +
102 ":eflags=" +
103 OUString::number(static_cast<int>(SFX_FILTER_NOTINSTALLED));
104
105 const Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
106 const Reference< XContainerQuery > xFilterFactory(
107 xContext->getServiceManager()->createInstanceWithContext( u"com.sun.star.document.FilterFactory"_ustr, xContext ),
108 UNO_QUERY_THROW );
109
110 std::shared_ptr<const SfxFilter> pBestMatch;
111
112 const Reference< XEnumeration > xFilterEnum(
113 xFilterFactory->createSubSetEnumerationByQuery( sQuery ), UNO_SET_THROW );
114 while ( xFilterEnum->hasMoreElements() )
115 {
116 comphelper::SequenceAsHashMap aFilterProps( xFilterEnum->nextElement() );
117 const OUString aName( aFilterProps.getUnpackedValueOrDefault( u"Name"_ustr, OUString() ) );
118 if ( !aName.isEmpty() )
119 {
120 std::shared_ptr<const SfxFilter> pFilter( SfxFilter::GetFilterByName( aName ) );
121 if ( pFilter && pFilter->CanExport() && pFilter->GetWildcard().Matches( rUrl ) )
122 {
123 if ( !pBestMatch || ( SfxFilterFlags::PREFERED & pFilter->GetFilterFlags() ) )
124 pBestMatch = pFilter;
125 }
126 }
127 }
128
129 return pBestMatch;
130 }
131
impl_getExportFilterFromUrl(const OUString & rUrl,const OUString & rFactory)132 std::shared_ptr<const SfxFilter> impl_getExportFilterFromUrl(
133 const OUString& rUrl, const OUString& rFactory)
134 {
135 try
136 {
137 const Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
138 const Reference< document::XTypeDetection > xTypeDetector(
139 xContext->getServiceManager()->createInstanceWithContext( u"com.sun.star.document.TypeDetection"_ustr, xContext ),
140 UNO_QUERY_THROW );
141 const OUString aTypeName( xTypeDetector->queryTypeByURL( rUrl ) );
142
143 std::shared_ptr<const SfxFilter> pFilter( SfxFilterMatcher( rFactory ).GetFilter4EA( aTypeName, SfxFilterFlags::EXPORT ) );
144 if ( !pFilter )
145 pFilter = impl_lookupExportFilterForUrl( rUrl, rFactory );
146 if ( !pFilter )
147 {
148 OUString aTempName;
149 FileBase::getSystemPathFromFileURL( rUrl, aTempName );
150 OString aSource = OUStringToOString ( aTempName, osl_getThreadTextEncoding() );
151 std::cerr << "Error: no export filter for " << aSource << " found, aborting." << std::endl;
152 }
153
154 return pFilter;
155 }
156 catch ( const Exception& )
157 {
158 return nullptr;
159 }
160 }
161
impl_GuessFilter(const OUString & rUrlOut,const OUString & rDocService)162 OUString impl_GuessFilter( const OUString& rUrlOut, const OUString& rDocService )
163 {
164 OUString aOutFilter;
165 std::shared_ptr<const SfxFilter> pOutFilter = impl_getExportFilterFromUrl( rUrlOut, rDocService );
166 if (pOutFilter)
167 aOutFilter = pOutFilter->GetFilterName();
168
169 return aOutFilter;
170 }
171
172 /// dump scripts in a document to the console.
scriptCat(const Reference<XModel> & xDoc)173 void scriptCat(const Reference< XModel >& xDoc )
174 {
175 Reference< XEmbeddedScripts > xScriptAccess( xDoc, UNO_QUERY );
176 if (!xScriptAccess)
177 {
178 std::cout << "No script access\n";
179 return;
180 }
181
182 // ignore xScriptAccess->getDialogLibraries() for now
183 Reference< css::script::XLibraryContainer2 > xLibraries(
184 xScriptAccess->getBasicLibraries() );
185
186 if ( !xLibraries.is() )
187 {
188 std::cout << "No script libraries\n";
189 return;
190 }
191
192 const Sequence< OUString > aLibNames = xLibraries->getElementNames();
193 std::cout << "Libraries: " << aLibNames.getLength() << "\n";
194 for (OUString const & libName : aLibNames)
195 {
196 std::cout << "Library: '" << libName << "' children: ";
197 Reference< XNameContainer > xContainer;
198 try {
199 if (!xLibraries->isLibraryLoaded( libName ))
200 xLibraries->loadLibrary( libName );
201 xContainer = Reference< XNameContainer >(
202 xLibraries->getByName( libName ), UNO_QUERY );
203 }
204 catch (const css::uno::Exception &e)
205 {
206 std::cout << "[" << libName << "] - failed to load library: " << e.Message << "\n";
207 continue;
208 }
209 if( !xContainer.is() )
210 std::cout << "0\n";
211 else
212 {
213 Sequence< OUString > aObjectNames = xContainer->getElementNames();
214
215 std::cout << aObjectNames.getLength() << "\n\n";
216 for ( sal_Int32 j = 0 ; j < aObjectNames.getLength() ; ++j )
217 {
218 const OUString &rObjectName = aObjectNames[j];
219
220 try
221 {
222 Any aCode = xContainer->getByName( rObjectName );
223 OUString aCodeString;
224
225 if (! (aCode >>= aCodeString ) )
226 std::cout << "[" << rObjectName << "] - error fetching code\n";
227 else
228 std::cout << "[" << rObjectName << "]\n"
229 << aCodeString.trim()
230 << "\n[/" << rObjectName << "]\n";
231 }
232 catch (const css::uno::Exception &e)
233 {
234 std::cout << "[" << rObjectName << "] - exception " << e.Message << " fetching code\n";
235 }
236
237 if (j < aObjectNames.getLength() - 1)
238 std::cout << "\n----------------------------------------------------------\n";
239 std::cout << "\n";
240 }
241 }
242 }
243 }
244
245 // Perform batch print
batchPrint(std::u16string_view rPrinterName,const Reference<XPrintable> & xDoc,const INetURLObject & aObj,const OUString & aName)246 void batchPrint( std::u16string_view rPrinterName, const Reference< XPrintable > &xDoc,
247 const INetURLObject &aObj, const OUString &aName )
248 {
249 OUString aFilterOut;
250 OUString aPrinterName;
251 size_t nPathIndex = rPrinterName.rfind( ';' );
252 if( nPathIndex != std::u16string_view::npos )
253 aFilterOut=rPrinterName.substr( nPathIndex+1 );
254 if( nPathIndex != 0 )
255 aPrinterName=rPrinterName.substr( 0, nPathIndex );
256
257 INetURLObject aOutFilename( aObj );
258 aOutFilename.SetExtension( u"pdf" );
259 FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
260 OUString aOutFile = aFilterOut + "/" + aOutFilename.getName();
261
262 OUString aTempName;
263 FileBase::getSystemPathFromFileURL( aName, aTempName );
264 OString aSource8 = OUStringToOString ( aTempName, osl_getThreadTextEncoding() );
265 FileBase::getSystemPathFromFileURL( aOutFile, aTempName );
266 OString aTargetURL8 = OUStringToOString(aTempName, osl_getThreadTextEncoding() );
267
268 std::cout << "print " << aSource8 << " -> " << aTargetURL8;
269 std::cout << " using " << (aPrinterName.isEmpty() ? "<default_printer>"_ostr : OUStringToOString( aPrinterName, osl_getThreadTextEncoding() ));
270 std::cout << std::endl;
271
272 // create the custom printer, if given
273 Sequence < PropertyValue > aPrinterArgs;
274 if( !aPrinterName.isEmpty() )
275 {
276 aPrinterArgs = { comphelper::makePropertyValue(u"Name"_ustr, aPrinterName) };
277 xDoc->setPrinter( aPrinterArgs );
278 }
279
280 // print ( also without user interaction )
281 aPrinterArgs = { comphelper::makePropertyValue(u"FileName"_ustr, aOutFile),
282 comphelper::makePropertyValue(u"Wait"_ustr, true) };
283 xDoc->print( aPrinterArgs );
284 }
285
286 // Get xDoc module name
getName(const Reference<XInterface> & xDoc)287 OUString getName(const Reference< XInterface > & xDoc)
288 {
289 Reference< XModel > xModel( xDoc, UNO_QUERY );
290 if (!xModel)
291 return OUString();
292 utl::MediaDescriptor aMediaDesc( xModel->getArgs() );
293 OUString aDocService = aMediaDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, OUString() );
294 if (aDocService == "com.sun.star.text.TextDocument")
295 return u"Writer"_ustr;
296 else if (aDocService == "com.sun.star.text.GlobalDocument")
297 return u"Writer master"_ustr;
298 else if (aDocService == "com.sun.star.text.WebDocument")
299 return u"Writer/Web"_ustr;
300 else if (aDocService == "com.sun.star.drawing.DrawingDocument")
301 return u"Draw"_ustr;
302 else if (aDocService == "com.sun.star.presentation.PresentationDocument")
303 return u"Impress"_ustr;
304 else if (aDocService == "com.sun.star.sheet.SpreadsheetDocument")
305 return u"Calc"_ustr;
306 else if (aDocService == "com.sun.star.script.BasicIDE")
307 return u"Basic"_ustr;
308 else if (aDocService == "com.sun.star.formula.FormulaProperties")
309 return u"Math"_ustr;
310 else if (aDocService == "com.sun.star.sdb.RelationDesign")
311 return u"Relation Design"_ustr;
312 else if (aDocService == "com.sun.star.sdb.QueryDesign")
313 return u"Query Design"_ustr;
314 else if (aDocService == "com.sun.star.sdb.TableDesign")
315 return u"Table Design"_ustr;
316 else if (aDocService == "com.sun.star.sdb.DataSourceBrowser")
317 return u"Data Source Browser"_ustr;
318 else if (aDocService == "com.sun.star.sdb.DatabaseDocument")
319 return u"Database"_ustr;
320
321 return OUString();
322 }
323
324 } // anonymous namespace
325
DispatchWatcher()326 DispatchWatcher::DispatchWatcher()
327 : m_nRequestCount(0)
328 {
329 }
330
331
~DispatchWatcher()332 DispatchWatcher::~DispatchWatcher()
333 {
334 }
335
336
executeDispatchRequests(const std::vector<DispatchRequest> & aDispatchRequestsList,bool bNoTerminate)337 bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest>& aDispatchRequestsList, bool bNoTerminate )
338 {
339 Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
340
341 std::vector< DispatchHolder > aDispatches;
342 bool bSetInputFilter = false;
343 OUString aForcedInputFilter;
344
345 for (auto const & aDispatchRequest: aDispatchRequestsList)
346 {
347 // Set Input Filter
348 if ( aDispatchRequest.aRequestType == REQUEST_INFILTER )
349 {
350 bSetInputFilter = true;
351 aForcedInputFilter = aDispatchRequest.aURL;
352 RequestHandler::RequestsCompleted();
353 continue;
354 }
355
356 // create parameter array
357 std::vector<PropertyValue> aArgs;
358
359 // mark request as user interaction from outside
360 aArgs.emplace_back("Referer", 0, Any(u"private:OpenEvent"_ustr),
361 PropertyState_DIRECT_VALUE);
362
363 OUString aTarget(u"_default"_ustr);
364
365 if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
366 aDispatchRequest.aRequestType == REQUEST_PRINTTO ||
367 aDispatchRequest.aRequestType == REQUEST_BATCHPRINT ||
368 aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
369 aDispatchRequest.aRequestType == REQUEST_CAT ||
370 aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT)
371 {
372 // documents opened for printing are opened readonly because they must be opened as a
373 // new document and this document could be open already
374 aArgs.emplace_back("ReadOnly", 0, Any(true), PropertyState_DIRECT_VALUE);
375 // always open a new document for printing, because it must be disposed afterwards
376 aArgs.emplace_back("OpenNewView", 0, Any(true), PropertyState_DIRECT_VALUE);
377 // printing is done in a hidden view
378 aArgs.emplace_back("Hidden", 0, Any(true), PropertyState_DIRECT_VALUE);
379 // load document for printing without user interaction
380 aArgs.emplace_back("Silent", 0, Any(true), PropertyState_DIRECT_VALUE);
381
382 // hidden documents should never be put into open tasks
383 aTarget = "_blank";
384 }
385 else
386 {
387 Reference < XInteractionHandler2 > xInteraction(
388 InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), nullptr) );
389
390 aArgs.emplace_back("InteractionHandler", 0, Any(xInteraction),
391 PropertyState_DIRECT_VALUE);
392
393 aArgs.emplace_back("MacroExecutionMode", 0,
394 Any(css::document::MacroExecMode::USE_CONFIG),
395 PropertyState_DIRECT_VALUE);
396
397 aArgs.emplace_back("UpdateDocMode", 0,
398 Any(css::document::UpdateDocMode::ACCORDING_TO_CONFIG),
399 PropertyState_DIRECT_VALUE);
400 }
401
402 if ( !aDispatchRequest.aPreselectedFactory.isEmpty() )
403 {
404 aArgs.emplace_back(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, 0,
405 Any(aDispatchRequest.aPreselectedFactory),
406 PropertyState_DIRECT_VALUE);
407 }
408
409 OUString aName( GetURL_Impl( aDispatchRequest.aURL, aDispatchRequest.aCwdUrl ) );
410
411 // load the document ... if they are loadable!
412 // Otherwise try to dispatch it ...
413 Reference < XPrintable > xDoc;
414 if(
415 ( aName.startsWith( ".uno" ) ) ||
416 ( aName.startsWith( "slot:" ) ) ||
417 ( aName.startsWith( "macro:" ) ) ||
418 ( aName.startsWith("vnd.sun.star.script") )
419 )
420 {
421 // Attention: URL must be parsed full. Otherwise some detections on it will fail!
422 // It doesn't matter, if parser isn't available. Because; We try loading of URL then ...
423 URL aURL ;
424 aURL.Complete = aName;
425
426 Reference < XDispatch > xDispatcher ;
427 Reference < XURLTransformer > xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
428
429 if( xParser.is() )
430 xParser->parseStrict( aURL );
431
432 xDispatcher = xDesktop->queryDispatch( aURL, OUString(), 0 );
433 SAL_WARN_IF(
434 !xDispatcher.is(), "desktop.app",
435 "unsupported dispatch request <" << aName << ">");
436 if( xDispatcher.is() )
437 {
438 // Remember request so we can find it in statusChanged!
439 m_nRequestCount++;
440
441 // Use local vector to store dispatcher because we have to fill our request container before
442 // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!!
443 aDispatches.emplace_back( aURL, xDispatcher );
444 }
445 }
446 else if ( aName.startsWith( "service:" ) )
447 {
448 // TODO: the dispatch has to be done for loadComponentFromURL as well.
449 URL aURL ;
450 aURL.Complete = aName;
451
452 Reference < XDispatch > xDispatcher ;
453 Reference < XURLTransformer > xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
454
455 if( xParser.is() )
456 xParser->parseStrict( aURL );
457
458 xDispatcher = xDesktop->queryDispatch( aURL, OUString(), 0 );
459
460 if( xDispatcher.is() )
461 {
462 try
463 {
464 // We have to be listener to catch errors during dispatching URLs.
465 // Otherwise it would be possible to have an office running without an open
466 // window!!
467 Sequence < PropertyValue > aArgs2{ comphelper::makePropertyValue(u"SynchronMode"_ustr,
468 true) };
469 Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY );
470 if ( xDisp.is() )
471 xDisp->dispatchWithNotification( aURL, aArgs2, this );
472 else
473 xDispatcher->dispatch( aURL, aArgs2 );
474 }
475 catch (const css::uno::Exception&)
476 {
477 TOOLS_WARN_EXCEPTION(
478 "desktop.app",
479 "Desktop::OpenDefault() ignoring Exception while calling XNotifyingDispatch");
480 }
481 }
482 }
483 else
484 {
485 INetURLObject aObj( aName );
486 if ( aObj.GetProtocol() == INetProtocol::PrivSoffice )
487 aTarget = "_default";
488
489 // Set "AsTemplate" argument according to request type
490 if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW ||
491 aDispatchRequest.aRequestType == REQUEST_FORCEOPEN )
492 {
493 aArgs.emplace_back("AsTemplate", 0,
494 Any(aDispatchRequest.aRequestType == REQUEST_FORCENEW),
495 PropertyState_DIRECT_VALUE);
496 }
497
498 // if we are called in viewmode, open document read-only
499 if(aDispatchRequest.aRequestType == REQUEST_VIEW) {
500 aArgs.emplace_back("ReadOnly", 0, Any(true), PropertyState_DIRECT_VALUE);
501 }
502
503 // if we are called with --show set Start in mediadescriptor
504 if(aDispatchRequest.aRequestType == REQUEST_START) {
505 const sal_Int32 nStartingSlide = aDispatchRequest.aParam.toInt32();
506 const sal_uInt16 nSlide = nStartingSlide > 0 ? nStartingSlide : 1;
507 aArgs.emplace_back("StartPresentation", 0, Any(nSlide), PropertyState_DIRECT_VALUE);
508 }
509
510 // Force input filter, if possible
511 if( bSetInputFilter )
512 {
513 sal_Int32 nFilterOptionsIndex = 0;
514 aArgs.emplace_back("FilterName", 0,
515 Any(aForcedInputFilter.getToken(0, ':', nFilterOptionsIndex)),
516 PropertyState_DIRECT_VALUE);
517
518 if (0 < nFilterOptionsIndex)
519 {
520 aArgs.emplace_back("FilterOptions", 0,
521 Any(aForcedInputFilter.copy(nFilterOptionsIndex)),
522 PropertyState_DIRECT_VALUE);
523 }
524 }
525
526 // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism.
527 try
528 {
529 xDoc.set(comphelper::SynchronousDispatch::dispatch(
530 xDesktop, aName, aTarget, comphelper::containerToSequence(aArgs)),
531 UNO_QUERY);
532 }
533 catch (const css::lang::IllegalArgumentException&)
534 {
535 TOOLS_WARN_EXCEPTION(
536 "desktop.app",
537 "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL");
538 }
539 catch (const css::io::IOException&)
540 {
541 TOOLS_WARN_EXCEPTION(
542 "desktop.app",
543 "Dispatchwatcher IOException while calling loadComponentFromURL");
544 }
545 if ( aDispatchRequest.aRequestType == REQUEST_OPEN ||
546 aDispatchRequest.aRequestType == REQUEST_VIEW ||
547 aDispatchRequest.aRequestType == REQUEST_START ||
548 aDispatchRequest.aRequestType == REQUEST_FORCEOPEN ||
549 aDispatchRequest.aRequestType == REQUEST_FORCENEW )
550 {
551 // request is completed
552 RequestHandler::RequestsCompleted();
553 }
554 else if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
555 aDispatchRequest.aRequestType == REQUEST_PRINTTO ||
556 aDispatchRequest.aRequestType == REQUEST_BATCHPRINT ||
557 aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
558 aDispatchRequest.aRequestType == REQUEST_CAT ||
559 aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT )
560 {
561 if ( xDoc.is() )
562 {
563 // Do we need to save the document in a different format?
564 if ( aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
565 aDispatchRequest.aRequestType == REQUEST_CAT )
566 {
567 // FIXME: factor out into a method ...
568 Reference< XStorable > xStorable( xDoc, UNO_QUERY );
569 if ( xStorable.is() ) {
570 const OUString& aParam = aDispatchRequest.aParam;
571 sal_Int32 nPathIndex = aParam.lastIndexOf( ';' );
572 sal_Int32 nFilterIndex = aParam.indexOf( ':' );
573 sal_Int32 nImgFilterIndex = aParam.lastIndexOf( '|' );
574 if( nPathIndex < nFilterIndex )
575 nFilterIndex = -1;
576
577 OUString aFilterOut;
578 OUString aImgOut;
579 OUString aFilter;
580 OUString aFilterExt;
581 bool bGuess = false;
582
583 if( nFilterIndex >= 0 )
584 {
585 aFilter = aParam.copy( nFilterIndex+1, nPathIndex-nFilterIndex-1 );
586 aFilterExt = aParam.copy( 0, nFilterIndex );
587 }
588 else
589 {
590 // Guess
591 bGuess = true;
592 aFilterExt = aParam.copy( 0, nPathIndex );
593 }
594
595 if( nImgFilterIndex >= 0 )
596 {
597 aImgOut = aParam.copy( nImgFilterIndex+1 );
598 aFilterOut = aParam.copy( nPathIndex+1, nImgFilterIndex-nPathIndex-1 );
599 }
600 else
601 aFilterOut = aParam.copy( nPathIndex+1 );
602
603 FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
604 INetURLObject aOutFilename(aFilterOut);
605 aOutFilename.Append(aObj.getName(INetURLObject::LAST_SEGMENT, true,
606 INetURLObject::DecodeMechanism::NONE));
607 aOutFilename.SetExtension(aFilterExt);
608 OUString aOutFile
609 = aOutFilename.GetMainURL(INetURLObject::DecodeMechanism::NONE);
610
611 std::unique_ptr<utl::TempFileNamed> fileForCat;
612 if( aDispatchRequest.aRequestType == REQUEST_CAT )
613 {
614 fileForCat = std::make_unique<utl::TempFileNamed>();
615 if (fileForCat->IsValid())
616 fileForCat->EnableKillingFile();
617 else
618 std::cerr << "Error: Cannot create temporary file..." << std::endl ;
619 aOutFile = fileForCat->GetURL();
620 }
621
622 if ( bGuess )
623 {
624 OUString aDocService;
625 Reference< XModel > xModel( xDoc, UNO_QUERY );
626 if ( xModel.is() )
627 {
628 utl::MediaDescriptor aMediaDesc( xModel->getArgs() );
629 aDocService = aMediaDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, OUString() );
630 }
631 aFilter = impl_GuessFilter( aOutFile, aDocService );
632 }
633
634 bool bMultiFileTarget = false;
635
636 if (aFilter.isEmpty())
637 {
638 std::cerr << "Error: no export filter" << std::endl;
639 }
640 else
641 {
642 sal_Int32 nFilterOptionsIndex = aFilter.indexOf(':');
643 sal_Int32 nProps = ( 0 < nFilterOptionsIndex ) ? 4 : 3;
644
645 if ( !aImgOut.isEmpty() )
646 nProps +=1;
647 Sequence<PropertyValue> conversionProperties( nProps );
648 auto pconversionProperties = conversionProperties.getArray();
649 pconversionProperties[0].Name = "ConversionRequestOrigin";
650 pconversionProperties[0].Value <<= u"CommandLine"_ustr;
651 pconversionProperties[1].Name = "Overwrite";
652 pconversionProperties[1].Value <<= true;
653
654 pconversionProperties[2].Name = "FilterName";
655 if( 0 < nFilterOptionsIndex )
656 {
657 OUString sFilterName = aFilter.copy(0, nFilterOptionsIndex);
658 OUString sFilterOptions = aFilter.copy(nFilterOptionsIndex + 1);
659
660 if (sFilterName == "Text - txt - csv (StarCalc)")
661 {
662 sal_Int32 nIdx(0);
663 // If the 11th token is '-1' then we export a file
664 // per sheet where the file name is based on the suggested
665 // output filename concatenated with the sheet name, so adjust
666 // the output and overwrite messages
667 // If the 11th token is not present or numeric 0 then the
668 // default sheet is exported with the output filename. If it
669 // is numeric >0 then that sheet (1-based) with the output
670 // filename concatenated with the sheet name. So even if
671 // that is a single file, the multi file target mechanism is
672 // used.
673 const OUString aTok(sFilterOptions.getToken(11, ',', nIdx));
674 // Actual validity is checked in Calc, here just check for
675 // presence of numeric value at start.
676 bMultiFileTarget = (!aTok.isEmpty() && aTok.toInt32() != 0);
677 }
678
679 pconversionProperties[2].Value <<= sFilterName;
680
681 pconversionProperties[3].Name = "FilterOptions";
682 pconversionProperties[3].Value <<= sFilterOptions;
683 }
684 else
685 {
686 pconversionProperties[2].Value <<= aFilter;
687 }
688
689 if ( !aImgOut.isEmpty() )
690 {
691 assert(conversionProperties[nProps-1].Name.isEmpty());
692 pconversionProperties[nProps-1].Name = "ImageFilter";
693 pconversionProperties[nProps-1].Value <<= aImgOut;
694 }
695
696 OUString aTempName;
697 FileBase::getSystemPathFromFileURL(aName, aTempName);
698 OString aSource8 = OUStringToOString(aTempName, osl_getThreadTextEncoding());
699 FileBase::getSystemPathFromFileURL(aOutFile, aTempName);
700 OString aTargetURL8 = OUStringToOString(aTempName, osl_getThreadTextEncoding());
701 if (aDispatchRequest.aRequestType != REQUEST_CAT)
702 {
703 OUString name=getName(xDoc);
704 std::cout << "convert " << aSource8;
705 if (!name.isEmpty())
706 std::cout << " as a " << name <<" document";
707 if (!bMultiFileTarget)
708 std::cout << " -> " << aTargetURL8;
709 std::cout << " using filter : " << OUStringToOString(aFilter, osl_getThreadTextEncoding()) << std::endl;
710 if (!bMultiFileTarget && FStatHelper::IsDocument(aOutFile))
711 std::cout << "Overwriting: " << OUStringToOString(aTempName, osl_getThreadTextEncoding()) << std::endl ;
712 }
713 try
714 {
715 xStorable->storeToURL(aOutFile, conversionProperties);
716 }
717 catch (const Exception& rException)
718 {
719 std::cerr << "Error: Please verify input parameters...";
720 if (!rException.Message.isEmpty())
721 std::cerr << " (" << rException.Message << ")";
722 std::cerr << std::endl;
723 }
724
725 if (fileForCat && fileForCat->IsValid())
726 {
727 SvStream* aStream = fileForCat->GetStream(StreamMode::STD_READ);
728 while (aStream->good())
729 {
730 OString aStr;
731 aStream->ReadLine(aStr, SAL_MAX_INT32);
732 for (sal_Int32 i = 0; i < aStr.getLength(); ++i)
733 {
734 std::cout << aStr[i];
735 }
736 std::cout << std::endl;
737 }
738 }
739 }
740 }
741 }
742 else if ( aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT )
743 {
744 Reference< XModel > xModel( xDoc, UNO_QUERY );
745 if( xModel.is() )
746 scriptCat( xModel );
747 }
748 else if ( aDispatchRequest.aRequestType == REQUEST_BATCHPRINT )
749 {
750 batchPrint(aDispatchRequest.aParam, xDoc, aObj, aName);
751 }
752 else
753 {
754 if ( aDispatchRequest.aRequestType == REQUEST_PRINTTO )
755 {
756 // create the printer
757 Sequence < PropertyValue > aPrinterArgs{ comphelper::makePropertyValue(
758 u"Name"_ustr, aDispatchRequest.aParam) };
759 xDoc->setPrinter( aPrinterArgs );
760 }
761
762 // print ( also without user interaction )
763 Sequence < PropertyValue > aPrinterArgs{ comphelper::makePropertyValue(u"Wait"_ustr,
764 true) };
765 xDoc->print( aPrinterArgs );
766 }
767 }
768 else
769 {
770 std::cerr << "Error: source file could not be loaded" << std::endl;
771 }
772
773 // remove the document
774 try
775 {
776 Reference < XCloseable > xClose( xDoc, UNO_QUERY );
777 if ( xClose.is() )
778 xClose->close( true );
779 else
780 {
781 Reference < XComponent > xComp( xDoc, UNO_QUERY );
782 if ( xComp.is() )
783 xComp->dispose();
784 }
785 }
786 catch (const css::util::CloseVetoException&)
787 {
788 }
789
790 // request is completed
791 RequestHandler::RequestsCompleted();
792 }
793 }
794 }
795
796 if ( !aDispatches.empty() )
797 {
798 // Execute all asynchronous dispatches now after we placed them into our request container!
799 Sequence < PropertyValue > aArgs{
800 comphelper::makePropertyValue(u"Referer"_ustr, u"private:OpenEvent"_ustr),
801 comphelper::makePropertyValue(u"SynchronMode"_ustr, true)
802 };
803
804 for (const DispatchHolder & aDispatche : aDispatches)
805 {
806 Reference< XDispatch > xDispatch = aDispatche.xDispatch;
807 Reference < XNotifyingDispatch > xDisp( xDispatch, UNO_QUERY );
808 if ( xDisp.is() )
809 xDisp->dispatchWithNotification( aDispatche.aURL, aArgs, this );
810 else
811 {
812 m_nRequestCount--;
813 xDispatch->dispatch( aDispatche.aURL, aArgs );
814 }
815 }
816 }
817
818 bool bEmpty = (m_nRequestCount == 0);
819
820 // No more asynchronous requests?
821 // The requests are removed from the request container after they called back to this
822 // implementation via statusChanged!!
823 if ( bEmpty && !bNoTerminate /*m_aRequestContainer.empty()*/ )
824 {
825 // We have to check if we have an open task otherwise we have to shutdown the office.
826 Reference< XElementAccess > xList = xDesktop->getFrames();
827
828 if ( !xList->hasElements() )
829 {
830 // We don't have any task open so we have to shutdown ourself!!
831 return xDesktop->terminate();
832 }
833 }
834
835 return false;
836 }
837
838
disposing(const css::lang::EventObject &)839 void SAL_CALL DispatchWatcher::disposing( const css::lang::EventObject& )
840 {
841 }
842
843
dispatchFinished(const DispatchResultEvent &)844 void SAL_CALL DispatchWatcher::dispatchFinished( const DispatchResultEvent& )
845 {
846 int nCount = --m_nRequestCount;
847 RequestHandler::RequestsCompleted();
848 if ( !nCount && !RequestHandler::AreRequestsPending() )
849 {
850 // We have to check if we have an open task otherwise we have to shutdown the office.
851 Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
852 Reference< XElementAccess > xList = xDesktop->getFrames();
853
854 if ( !xList->hasElements() )
855 {
856 // We don't have any task open so we have to shutdown ourself!!
857 xDesktop->terminate();
858 }
859 }
860 }
861
862 } // namespace desktop
863
864 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
865