1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <comphelper/mediadescriptor.hxx>
21 #include <comphelper/namedvaluecollection.hxx>
22 #include <comphelper/stillreadwriteinteraction.hxx>
24 #include <com/sun/star/ucb/XContent.hpp>
25 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
26 #include <com/sun/star/task/XInteractionHandler.hpp>
27 #include <com/sun/star/io/XStream.hpp>
28 #include <com/sun/star/io/XActiveDataSink.hpp>
29 #include <com/sun/star/io/XSeekable.hpp>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/lang/IllegalArgumentException.hpp>
32 #include <com/sun/star/util/XURLTransformer.hpp>
33 #include <com/sun/star/ucb/InteractiveIOException.hpp>
34 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
35 #include <com/sun/star/ucb/CommandFailedException.hpp>
36 #include <com/sun/star/task/XInteractionAbort.hpp>
37 #include <com/sun/star/uri/UriReferenceFactory.hpp>
38 #include <com/sun/star/uri/XUriReference.hpp>
39 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <officecfg/Office/Common.hxx>
42 #include <ucbhelper/interceptedinteraction.hxx>
43 #include <ucbhelper/content.hxx>
44 #include <ucbhelper/commandenvironment.hxx>
45 #include <ucbhelper/activedatasink.hxx>
46 #include <comphelper/processfactory.hxx>
48 #include <rtl/ustrbuf.hxx>
53 const OUString
& MediaDescriptor::PROP_ABORTED()
55 static const OUString
sProp("Aborted");
59 const OUString
& MediaDescriptor::PROP_ASTEMPLATE()
61 static const OUString
sProp("AsTemplate");
65 const OUString
& MediaDescriptor::PROP_COMPONENTDATA()
67 static const OUString
sProp("ComponentData");
71 const OUString
& MediaDescriptor::PROP_DOCUMENTSERVICE()
73 static const OUString
sProp("DocumentService");
77 const OUString
& MediaDescriptor::PROP_ENCRYPTIONDATA()
79 static const OUString
sProp("EncryptionData");
83 const OUString
& MediaDescriptor::PROP_FILENAME()
85 static const OUString
sProp("FileName");
89 const OUString
& MediaDescriptor::PROP_FILTERNAME()
91 static const OUString
sProp("FilterName");
95 const OUString
& MediaDescriptor::PROP_FILTERPROVIDER()
97 static const OUString
aProp("FilterProvider");
101 const OUString
& MediaDescriptor::PROP_FILTEROPTIONS()
103 static const OUString
sProp("FilterOptions");
107 const OUString
& MediaDescriptor::PROP_FRAME()
109 static const OUString
sProp("Frame");
113 const OUString
& MediaDescriptor::PROP_FRAMENAME()
115 static const OUString
sProp("FrameName");
119 const OUString
& MediaDescriptor::PROP_HIDDEN()
121 static const OUString
sProp("Hidden");
125 const OUString
& MediaDescriptor::PROP_INPUTSTREAM()
127 static const OUString
sProp("InputStream");
131 const OUString
& MediaDescriptor::PROP_INTERACTIONHANDLER()
133 static const OUString
sProp("InteractionHandler");
137 const OUString
& MediaDescriptor::PROP_JUMPMARK()
139 static const OUString
sProp("JumpMark");
143 const OUString
& MediaDescriptor::PROP_MACROEXECUTIONMODE()
145 static const OUString
sProp("MacroExecutionMode");
149 const OUString
& MediaDescriptor::PROP_MEDIATYPE()
151 static const OUString
sProp("MediaType");
155 const OUString
& MediaDescriptor::PROP_MINIMIZED()
157 static const OUString
sProp("Minimized");
161 const OUString
& MediaDescriptor::PROP_NOAUTOSAVE()
163 static const OUString
sProp("NoAutoSave");
167 const OUString
& MediaDescriptor::PROP_OPENNEWVIEW()
169 static const OUString
sProp("OpenNewView");
173 const OUString
& MediaDescriptor::PROP_OUTPUTSTREAM()
175 static const OUString
sProp("OutputStream");
179 const OUString
& MediaDescriptor::PROP_POSTDATA()
181 static const OUString
sProp("PostData");
185 const OUString
& MediaDescriptor::PROP_PREVIEW()
187 static const OUString
sProp("Preview");
191 const OUString
& MediaDescriptor::PROP_READONLY()
193 static const OUString
sProp("ReadOnly");
197 const OUString
& MediaDescriptor::PROP_REFERRER()
199 static const OUString
sProp("Referer");
203 const OUString
& MediaDescriptor::PROP_STATUSINDICATOR()
205 static const OUString
sProp("StatusIndicator");
209 const OUString
& MediaDescriptor::PROP_STREAM()
211 static const OUString
sProp("Stream");
215 const OUString
& MediaDescriptor::PROP_STREAMFOROUTPUT()
217 static const OUString
sProp("StreamForOutput");
221 const OUString
& MediaDescriptor::PROP_TEMPLATENAME()
223 static const OUString
sProp("TemplateName");
227 const OUString
& MediaDescriptor::PROP_TYPENAME()
229 static const OUString
sProp("TypeName");
233 const OUString
& MediaDescriptor::PROP_UCBCONTENT()
235 static const OUString
sProp("UCBContent");
239 const OUString
& MediaDescriptor::PROP_UPDATEDOCMODE()
241 static const OUString
sProp("UpdateDocMode");
245 const OUString
& MediaDescriptor::PROP_URL()
247 static const OUString
sProp("URL");
251 const OUString
& MediaDescriptor::PROP_VERSION()
253 static const OUString
sProp("Version");
257 const OUString
& MediaDescriptor::PROP_DOCUMENTTITLE()
259 static const OUString
sProp("DocumentTitle");
263 const OUString
& MediaDescriptor::PROP_MODEL()
265 static const OUString
sProp("Model");
269 const OUString
& MediaDescriptor::PROP_PASSWORD()
271 static const OUString
sProp("Password");
275 const OUString
& MediaDescriptor::PROP_TITLE()
277 static const OUString
sProp("Title");
281 const OUString
& MediaDescriptor::PROP_SALVAGEDFILE()
283 static const OUString
sProp("SalvagedFile");
287 const OUString
& MediaDescriptor::PROP_VIEWONLY()
289 static const OUString
sProp("ViewOnly");
293 const OUString
& MediaDescriptor::PROP_DOCUMENTBASEURL()
295 static const OUString
sProp("DocumentBaseURL");
299 const OUString
& MediaDescriptor::PROP_DEEPDETECTION()
301 static const OUString
aProp("DeepDetection");
305 MediaDescriptor::MediaDescriptor()
306 : SequenceAsHashMap()
310 MediaDescriptor::MediaDescriptor(const css::uno::Sequence
< css::beans::PropertyValue
>& lSource
)
311 : SequenceAsHashMap(lSource
)
315 sal_Bool
MediaDescriptor::isStreamReadOnly() const
317 static OUString
CONTENTSCHEME_FILE( "file" );
318 static OUString
CONTENTPROP_ISREADONLY( "IsReadOnly" );
319 static sal_Bool READONLY_FALLBACK
= sal_False
;
321 sal_Bool bReadOnly
= READONLY_FALLBACK
;
323 // check for explicit readonly state
324 const_iterator pIt
= find(MediaDescriptor::PROP_READONLY());
327 pIt
->second
>>= bReadOnly
;
331 // streams based on post data are readonly by definition
332 pIt
= find(MediaDescriptor::PROP_POSTDATA());
336 // A XStream capsulate XInputStream and XOutputStream ...
337 // If it exists - the file must be open in read/write mode!
338 pIt
= find(MediaDescriptor::PROP_STREAM());
342 // Only file system content provider is able to provide XStream
343 // so for this content impossibility to create XStream triggers
344 // switch to readonly mode.
347 css::uno::Reference
< css::ucb::XContent
> xContent
= getUnpackedValueOrDefault(MediaDescriptor::PROP_UCBCONTENT(), css::uno::Reference
< css::ucb::XContent
>());
350 css::uno::Reference
< css::ucb::XContentIdentifier
> xId(xContent
->getIdentifier(), css::uno::UNO_QUERY
);
353 aScheme
= xId
->getContentProviderScheme();
355 if (aScheme
.equalsIgnoreAsciiCase(CONTENTSCHEME_FILE
))
356 bReadOnly
= sal_True
;
359 ::ucbhelper::Content
aContent(xContent
, css::uno::Reference
< css::ucb::XCommandEnvironment
>(), getProcessComponentContext());
360 aContent
.getPropertyValue(CONTENTPROP_ISREADONLY
) >>= bReadOnly
;
364 catch(const css::uno::RuntimeException
& )
366 catch(const css::uno::Exception
&)
372 // ----------------------------------------------------------------------------
374 css::uno::Any
MediaDescriptor::getComponentDataEntry( const OUString
& rName
) const
376 css::uno::Any aEntry
;
377 SequenceAsHashMap::const_iterator aPropertyIter
= find( PROP_COMPONENTDATA() );
378 if( aPropertyIter
!= end() )
379 return NamedValueCollection( aPropertyIter
->second
).get( rName
);
380 return css::uno::Any();
383 void MediaDescriptor::setComponentDataEntry( const OUString
& rName
, const css::uno::Any
& rValue
)
385 if( rValue
.hasValue() )
387 // get or create the 'ComponentData' property entry
388 css::uno::Any
& rCompDataAny
= operator[]( PROP_COMPONENTDATA() );
389 // insert the value (retain sequence type, create NamedValue elements by default)
390 bool bHasNamedValues
= !rCompDataAny
.hasValue() || rCompDataAny
.has
< css::uno::Sequence
< css::beans::NamedValue
> >();
391 bool bHasPropValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::PropertyValue
> >();
392 OSL_ENSURE( bHasNamedValues
|| bHasPropValues
, "MediaDescriptor::setComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
393 if( bHasNamedValues
|| bHasPropValues
)
395 // insert or overwrite the passed value
396 SequenceAsHashMap
aCompDataMap( rCompDataAny
);
397 aCompDataMap
[ rName
] = rValue
;
398 // write back the sequence (restore sequence with correct element type)
399 rCompDataAny
= aCompDataMap
.getAsConstAny( bHasPropValues
);
404 // if an empty Any is passed, clear the entry
405 clearComponentDataEntry( rName
);
409 void MediaDescriptor::clearComponentDataEntry( const OUString
& rName
)
411 SequenceAsHashMap::iterator aPropertyIter
= find( PROP_COMPONENTDATA() );
412 if( aPropertyIter
!= end() )
414 css::uno::Any
& rCompDataAny
= aPropertyIter
->second
;
415 bool bHasNamedValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::NamedValue
> >();
416 bool bHasPropValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::PropertyValue
> >();
417 OSL_ENSURE( bHasNamedValues
|| bHasPropValues
, "MediaDescriptor::clearComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
418 if( bHasNamedValues
|| bHasPropValues
)
420 // remove the value with the passed name
421 SequenceAsHashMap
aCompDataMap( rCompDataAny
);
422 aCompDataMap
.erase( rName
);
423 // write back the sequence, or remove it completely if it is empty
424 if( aCompDataMap
.empty() )
425 erase( aPropertyIter
);
427 rCompDataAny
= aCompDataMap
.getAsConstAny( bHasPropValues
);
432 sal_Bool
MediaDescriptor::addInputStream()
434 return impl_addInputStream( sal_True
);
437 /*-----------------------------------------------*/
438 sal_Bool
MediaDescriptor::addInputStreamOwnLock()
440 return impl_addInputStream(
441 officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get());
444 /*-----------------------------------------------*/
445 sal_Bool
MediaDescriptor::impl_addInputStream( sal_Bool bLockFile
)
447 // check for an already existing stream item first
448 const_iterator pIt
= find(MediaDescriptor::PROP_INPUTSTREAM());
454 // No stream available - create a new one
455 // a) data comes as PostData ...
456 pIt
= find(MediaDescriptor::PROP_POSTDATA());
459 const css::uno::Any
& rPostData
= pIt
->second
;
460 css::uno::Reference
< css::io::XInputStream
> xPostData
;
461 rPostData
>>= xPostData
;
463 return impl_openStreamWithPostData( xPostData
);
466 // b) ... or we must get it from the given URL
467 OUString sURL
= getUnpackedValueOrDefault(MediaDescriptor::PROP_URL(), OUString());
469 throw css::uno::Exception( OUString( "Found no URL." ),
470 css::uno::Reference
< css::uno::XInterface
>());
472 // Parse URL! Only the main part has to be used further. E.g. a jumpmark can make trouble
473 OUString sNormalizedURL
= impl_normalizeURL( sURL
);
474 return impl_openStreamWithURL( sNormalizedURL
, bLockFile
);
476 #if OSL_DEBUG_LEVEL > 0
477 catch(const css::uno::Exception
& ex
)
479 OUStringBuffer
sMsg(256);
480 sMsg
.appendAscii("Invalid MediaDescriptor detected:\n");
481 sMsg
.append (ex
.Message
);
482 OSL_FAIL(OUStringToOString(sMsg
.makeStringAndClear(), RTL_TEXTENCODING_UTF8
).getStr());
485 catch(const css::uno::Exception
&)
492 sal_Bool
MediaDescriptor::impl_openStreamWithPostData( const css::uno::Reference
< css::io::XInputStream
>& _rxPostData
)
493 throw(::com::sun::star::uno::RuntimeException
)
495 if ( !_rxPostData
.is() )
496 throw css::lang::IllegalArgumentException( OUString( "Found invalid PostData." ),
497 css::uno::Reference
< css::uno::XInterface
>(), 1);
499 // PostData can't be used in read/write mode!
500 (*this)[MediaDescriptor::PROP_READONLY()] <<= sal_True
;
502 // prepare the environment
503 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction
= getUnpackedValueOrDefault(
504 MediaDescriptor::PROP_INTERACTIONHANDLER(),
505 css::uno::Reference
< css::task::XInteractionHandler
>());
506 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
507 ::ucbhelper::CommandEnvironment
* pCommandEnv
= new ::ucbhelper::CommandEnvironment(xInteraction
, xProgress
);
508 css::uno::Reference
< css::ucb::XCommandEnvironment
> xCommandEnv(static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
511 OUString sMediaType
= getUnpackedValueOrDefault(MediaDescriptor::PROP_MEDIATYPE(), OUString());
512 if (sMediaType
.isEmpty())
514 sMediaType
= "application/x-www-form-urlencoded";
515 (*this)[MediaDescriptor::PROP_MEDIATYPE()] <<= sMediaType
;
519 OUString
sURL( getUnpackedValueOrDefault( PROP_URL(), OUString() ) );
521 css::uno::Reference
< css::io::XInputStream
> xResultStream
;
524 // seek PostData stream to the beginning
525 css::uno::Reference
< css::io::XSeekable
> xSeek( _rxPostData
, css::uno::UNO_QUERY
);
529 // a content for the URL
530 ::ucbhelper::Content
aContent( sURL
, xCommandEnv
, getProcessComponentContext() );
533 css::ucb::PostCommandArgument2 aPostArgument
;
534 aPostArgument
.Source
= _rxPostData
;
535 css::uno::Reference
< css::io::XActiveDataSink
> xSink( new ucbhelper::ActiveDataSink
);
536 aPostArgument
.Sink
= xSink
;
537 aPostArgument
.MediaType
= sMediaType
;
538 aPostArgument
.Referer
= getUnpackedValueOrDefault( PROP_REFERRER(), OUString() );
540 OUString
sCommandName( "post" );
541 aContent
.executeCommand( sCommandName
, css::uno::makeAny( aPostArgument
) );
544 xResultStream
= xSink
->getInputStream();
546 catch( const css::uno::Exception
& )
551 if ( !xResultStream
.is() )
553 OSL_FAIL( "no valid reply to the HTTP-Post" );
557 (*this)[MediaDescriptor::PROP_INPUTSTREAM()] <<= xResultStream
;
561 /*-----------------------------------------------*/
562 sal_Bool
MediaDescriptor::impl_openStreamWithURL( const OUString
& sURL
, sal_Bool bLockFile
)
563 throw(::com::sun::star::uno::RuntimeException
)
565 // prepare the environment
566 css::uno::Reference
< css::task::XInteractionHandler
> xOrgInteraction
= getUnpackedValueOrDefault(
567 MediaDescriptor::PROP_INTERACTIONHANDLER(),
568 css::uno::Reference
< css::task::XInteractionHandler
>());
570 StillReadWriteInteraction
* pInteraction
= new StillReadWriteInteraction(xOrgInteraction
);
571 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction(static_cast< css::task::XInteractionHandler
* >(pInteraction
), css::uno::UNO_QUERY
);
573 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
574 ::ucbhelper::CommandEnvironment
* pCommandEnv
= new ::ucbhelper::CommandEnvironment(xInteraction
, xProgress
);
575 css::uno::Reference
< css::ucb::XCommandEnvironment
> xCommandEnv(static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
577 // try to create the content
578 // no content -> no stream => return immediately with FALSE
579 ::ucbhelper::Content aContent
;
580 css::uno::Reference
< css::ucb::XContent
> xContent
;
583 aContent
= ::ucbhelper::Content(sURL
, xCommandEnv
, getProcessComponentContext());
584 xContent
= aContent
.get();
586 catch(const css::uno::RuntimeException
&)
588 catch(const css::ucb::ContentCreationException
&)
589 { return sal_False
; } // TODO error handling
590 catch(const css::uno::Exception
&)
591 { return sal_False
; } // TODO error handling
593 // try to open the file in read/write mode
594 // (if its allowed to do so).
595 // But handle errors in a "hidden mode". Because
596 // we try it readonly later - if read/write isnt an option.
597 css::uno::Reference
< css::io::XStream
> xStream
;
598 css::uno::Reference
< css::io::XInputStream
> xInputStream
;
600 sal_Bool bReadOnly
= sal_False
;
601 sal_Bool bModeRequestedExplicitly
= sal_False
;
602 const_iterator pIt
= find(MediaDescriptor::PROP_READONLY());
605 pIt
->second
>>= bReadOnly
;
606 bModeRequestedExplicitly
= sal_True
;
609 if ( !bReadOnly
&& bLockFile
)
613 // TODO: use "special" still interaction to supress error messages
614 xStream
= aContent
.openWriteableStream();
616 xInputStream
= xStream
->getInputStream();
618 catch(const css::uno::RuntimeException
&)
620 catch(const css::uno::Exception
&)
622 // ignore exception, if reason was problem reasoned on
623 // open it in WRITEABLE mode! Then we try it READONLY
624 // later a second time.
625 // All other errors must be handled as real error an
626 // break this method.
627 if (!pInteraction
->wasWriteError() || bModeRequestedExplicitly
)
630 xInputStream
.clear();
634 // If opening of the stream in read/write mode wasnt allowed
635 // or failed by an error - we must try it in readonly mode.
636 if (!xInputStream
.is())
642 css::uno::Reference
< css::ucb::XContentIdentifier
> xContId(
643 aContent
.get().is() ? aContent
.get()->getIdentifier() : 0 );
646 aScheme
= xContId
->getContentProviderScheme();
648 // Only file system content provider is able to provide XStream
649 // so for this content impossibility to create XStream triggers
650 // switch to readonly mode in case of opening with locking on
651 if( bLockFile
&& aScheme
.equalsIgnoreAsciiCase("file") )
652 bReadOnly
= sal_True
;
655 sal_Bool bRequestReadOnly
= bReadOnly
;
656 aContent
.getPropertyValue( OUString( "IsReadOnly" ) ) >>= bReadOnly
;
657 if ( bReadOnly
&& !bRequestReadOnly
&& bModeRequestedExplicitly
)
658 return sal_False
; // the document is explicitly requested with WRITEABLE mode
661 catch(const css::uno::RuntimeException
&)
663 catch(const css::uno::Exception
&)
664 { /* no error handling if IsReadOnly property does not exist for UCP */ }
667 (*this)[MediaDescriptor::PROP_READONLY()] <<= bReadOnly
;
669 pInteraction
->resetInterceptions();
670 pInteraction
->resetErrorStates();
673 // all the contents except file-URLs should be opened as usual
674 if ( bLockFile
|| !aScheme
.equalsIgnoreAsciiCase("file") )
675 xInputStream
= aContent
.openStream();
677 xInputStream
= aContent
.openStreamNoLock();
679 catch(const css::uno::RuntimeException
&)
681 catch(const css::uno::Exception
&)
682 { return sal_False
; }
685 // add streams to the descriptor
687 (*this)[MediaDescriptor::PROP_UCBCONTENT()] <<= xContent
;
689 (*this)[MediaDescriptor::PROP_STREAM()] <<= xStream
;
690 if (xInputStream
.is())
691 (*this)[MediaDescriptor::PROP_INPUTSTREAM()] <<= xInputStream
;
693 // At least we need an input stream. The r/w stream is optional ...
694 return xInputStream
.is();
697 OUString
MediaDescriptor::impl_normalizeURL(const OUString
& sURL
)
699 /* Remove Jumpmarks (fragments) of an URL only here.
700 They are not part of any URL and as a result may be
701 no ucb content can be created then.
702 On the other side arguments must exists ... because
703 they are part of an URL.
705 Do not use the URLTransformer service here. Because
706 it parses the URL in another way. It's main part isnt enough
707 and it's complete part contains the jumpmark (fragment) parameter ...
712 css::uno::Reference
< css::uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
713 css::uno::Reference
< css::uri::XUriReferenceFactory
> xUriFactory
= css::uri::UriReferenceFactory::create(xContext
);;
714 css::uno::Reference
< css::uri::XUriReference
> xUriRef
= xUriFactory
->parse(sURL
);
717 xUriRef
->clearFragment();
718 return xUriRef
->getUriReference();
721 catch(const css::uno::RuntimeException
&)
723 catch(const css::uno::Exception
&)
726 // If an error ocurred ... return the original URL.
731 } // namespace comphelper
733 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */