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/docpasswordhelper.hxx>
21 #include <unotools/mediadescriptor.hxx>
22 #include <unotools/securityoptions.hxx>
23 #include <comphelper/namedvaluecollection.hxx>
24 #include <comphelper/stillreadwriteinteraction.hxx>
26 #include <com/sun/star/ucb/XContent.hpp>
27 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
28 #include <com/sun/star/task/XInteractionHandler.hpp>
29 #include <com/sun/star/io/XStream.hpp>
30 #include <com/sun/star/io/XActiveDataSink.hpp>
31 #include <com/sun/star/io/XSeekable.hpp>
32 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
33 #include <com/sun/star/lang/IllegalArgumentException.hpp>
34 #include <com/sun/star/util/XURLTransformer.hpp>
35 #include <com/sun/star/ucb/InteractiveIOException.hpp>
36 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
37 #include <com/sun/star/ucb/CommandFailedException.hpp>
38 #include <com/sun/star/task/XInteractionAbort.hpp>
39 #include <com/sun/star/uri/UriReferenceFactory.hpp>
40 #include <com/sun/star/uri/XUriReference.hpp>
41 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
42 #include <com/sun/star/container/XNameAccess.hpp>
43 #include <officecfg/Office/Common.hxx>
44 #include <ucbhelper/interceptedinteraction.hxx>
45 #include <ucbhelper/content.hxx>
46 #include <ucbhelper/commandenvironment.hxx>
47 #include <ucbhelper/activedatasink.hxx>
48 #include <comphelper/processfactory.hxx>
54 OUString
removeFragment(OUString
const & uri
) {
55 css::uno::Reference
< css::uri::XUriReference
> ref(
56 css::uri::UriReferenceFactory::create(
57 comphelper::getProcessComponentContext())->
61 return ref
->getUriReference();
63 SAL_WARN("unotools.misc", "cannot parse <" << uri
<< ">");
70 const OUString
& MediaDescriptor::PROP_ABORTED()
72 static const OUString
sProp("Aborted");
76 const OUString
& MediaDescriptor::PROP_ASTEMPLATE()
78 static const OUString
sProp("AsTemplate");
82 const OUString
& MediaDescriptor::PROP_COMPONENTDATA()
84 static const OUString
sProp("ComponentData");
88 const OUString
& MediaDescriptor::PROP_DOCUMENTSERVICE()
90 static const OUString
sProp("DocumentService");
94 const OUString
& MediaDescriptor::PROP_ENCRYPTIONDATA()
96 static const OUString
sProp("EncryptionData");
100 const OUString
& MediaDescriptor::PROP_FILENAME()
102 static const OUString
sProp("FileName");
106 const OUString
& MediaDescriptor::PROP_FILTERNAME()
108 static const OUString
sProp("FilterName");
112 const OUString
& MediaDescriptor::PROP_FILTERPROVIDER()
114 static const OUString
aProp("FilterProvider");
118 const OUString
& MediaDescriptor::PROP_FILTEROPTIONS()
120 static const OUString
sProp("FilterOptions");
124 const OUString
& MediaDescriptor::PROP_FRAME()
126 static const OUString
sProp("Frame");
130 const OUString
& MediaDescriptor::PROP_FRAMENAME()
132 static const OUString
sProp("FrameName");
136 const OUString
& MediaDescriptor::PROP_HIDDEN()
138 static const OUString
sProp("Hidden");
142 const OUString
& MediaDescriptor::PROP_INPUTSTREAM()
144 static const OUString
sProp("InputStream");
148 const OUString
& MediaDescriptor::PROP_INTERACTIONHANDLER()
150 static const OUString
sProp("InteractionHandler");
154 const OUString
& MediaDescriptor::PROP_JUMPMARK()
156 static const OUString
sProp("JumpMark");
160 const OUString
& MediaDescriptor::PROP_MACROEXECUTIONMODE()
162 static const OUString
sProp("MacroExecutionMode");
166 const OUString
& MediaDescriptor::PROP_MEDIATYPE()
168 static const OUString
sProp("MediaType");
172 const OUString
& MediaDescriptor::PROP_MINIMIZED()
174 static const OUString
sProp("Minimized");
178 const OUString
& MediaDescriptor::PROP_NOAUTOSAVE()
180 static const OUString
sProp("NoAutoSave");
184 const OUString
& MediaDescriptor::PROP_OPENNEWVIEW()
186 static const OUString
sProp("OpenNewView");
190 const OUString
& MediaDescriptor::PROP_OUTPUTSTREAM()
192 static const OUString
sProp("OutputStream");
196 const OUString
& MediaDescriptor::PROP_POSTDATA()
198 static const OUString
sProp("PostData");
202 const OUString
& MediaDescriptor::PROP_PREVIEW()
204 static const OUString
sProp("Preview");
208 const OUString
& MediaDescriptor::PROP_READONLY()
210 static const OUString
sProp("ReadOnly");
214 const OUString
& MediaDescriptor::PROP_REFERRER()
216 static const OUString
sProp("Referer");
220 const OUString
& MediaDescriptor::PROP_STATUSINDICATOR()
222 static const OUString
sProp("StatusIndicator");
226 const OUString
& MediaDescriptor::PROP_STREAM()
228 static const OUString
sProp("Stream");
232 const OUString
& MediaDescriptor::PROP_STREAMFOROUTPUT()
234 static const OUString
sProp("StreamForOutput");
238 const OUString
& MediaDescriptor::PROP_TEMPLATENAME()
240 static const OUString
sProp("TemplateName");
244 const OUString
& MediaDescriptor::PROP_TYPENAME()
246 static const OUString
sProp("TypeName");
250 const OUString
& MediaDescriptor::PROP_UCBCONTENT()
252 static const OUString
sProp("UCBContent");
256 const OUString
& MediaDescriptor::PROP_UPDATEDOCMODE()
258 static const OUString
sProp("UpdateDocMode");
262 const OUString
& MediaDescriptor::PROP_URL()
264 static const OUString
sProp("URL");
268 const OUString
& MediaDescriptor::PROP_VERSION()
270 static const OUString
sProp("Version");
274 const OUString
& MediaDescriptor::PROP_DOCUMENTTITLE()
276 static const OUString
sProp("DocumentTitle");
280 const OUString
& MediaDescriptor::PROP_MODEL()
282 static const OUString
sProp("Model");
286 const OUString
& MediaDescriptor::PROP_PASSWORD()
288 static const OUString
sProp("Password");
292 const OUString
& MediaDescriptor::PROP_TITLE()
294 static const OUString
sProp("Title");
298 const OUString
& MediaDescriptor::PROP_SALVAGEDFILE()
300 static const OUString
sProp("SalvagedFile");
304 const OUString
& MediaDescriptor::PROP_VIEWONLY()
306 static const OUString
sProp("ViewOnly");
310 const OUString
& MediaDescriptor::PROP_DOCUMENTBASEURL()
312 static const OUString
sProp("DocumentBaseURL");
316 MediaDescriptor::MediaDescriptor()
317 : SequenceAsHashMap()
321 MediaDescriptor::MediaDescriptor(const css::uno::Sequence
< css::beans::PropertyValue
>& lSource
)
322 : SequenceAsHashMap(lSource
)
326 bool MediaDescriptor::isStreamReadOnly() const
328 static OUString
CONTENTSCHEME_FILE( "file" );
329 static OUString
CONTENTPROP_ISREADONLY( "IsReadOnly" );
330 static bool READONLY_FALLBACK
= false;
332 bool bReadOnly
= READONLY_FALLBACK
;
334 // check for explicit readonly state
335 const_iterator pIt
= find(MediaDescriptor::PROP_READONLY());
338 pIt
->second
>>= bReadOnly
;
342 // streams based on post data are readonly by definition
343 pIt
= find(MediaDescriptor::PROP_POSTDATA());
347 // A XStream capsulate XInputStream and XOutputStream ...
348 // If it exists - the file must be open in read/write mode!
349 pIt
= find(MediaDescriptor::PROP_STREAM());
353 // Only file system content provider is able to provide XStream
354 // so for this content impossibility to create XStream triggers
355 // switch to readonly mode.
358 css::uno::Reference
< css::ucb::XContent
> xContent
= getUnpackedValueOrDefault(MediaDescriptor::PROP_UCBCONTENT(), css::uno::Reference
< css::ucb::XContent
>());
361 css::uno::Reference
< css::ucb::XContentIdentifier
> xId(xContent
->getIdentifier(), css::uno::UNO_QUERY
);
364 aScheme
= xId
->getContentProviderScheme();
366 if (aScheme
.equalsIgnoreAsciiCase(CONTENTSCHEME_FILE
))
370 ::ucbhelper::Content
aContent(xContent
, css::uno::Reference
< css::ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext());
371 aContent
.getPropertyValue(CONTENTPROP_ISREADONLY
) >>= bReadOnly
;
375 catch(const css::uno::RuntimeException
& )
377 catch(const css::uno::Exception
&)
383 css::uno::Any
MediaDescriptor::getComponentDataEntry( const OUString
& rName
) const
385 comphelper::SequenceAsHashMap::const_iterator aPropertyIter
= find( PROP_COMPONENTDATA() );
386 if( aPropertyIter
!= end() )
387 return comphelper::NamedValueCollection( aPropertyIter
->second
).get( rName
);
388 return css::uno::Any();
391 void MediaDescriptor::setComponentDataEntry( const OUString
& rName
, const css::uno::Any
& rValue
)
393 if( rValue
.hasValue() )
395 // get or create the 'ComponentData' property entry
396 css::uno::Any
& rCompDataAny
= operator[]( PROP_COMPONENTDATA() );
397 // insert the value (retain sequence type, create NamedValue elements by default)
398 bool bHasNamedValues
= !rCompDataAny
.hasValue() || rCompDataAny
.has
< css::uno::Sequence
< css::beans::NamedValue
> >();
399 bool bHasPropValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::PropertyValue
> >();
400 OSL_ENSURE( bHasNamedValues
|| bHasPropValues
, "MediaDescriptor::setComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
401 if( bHasNamedValues
|| bHasPropValues
)
403 // insert or overwrite the passed value
404 comphelper::SequenceAsHashMap
aCompDataMap( rCompDataAny
);
405 aCompDataMap
[ rName
] = rValue
;
406 // write back the sequence (restore sequence with correct element type)
407 rCompDataAny
= aCompDataMap
.getAsConstAny( bHasPropValues
);
412 // if an empty Any is passed, clear the entry
413 clearComponentDataEntry( rName
);
417 void MediaDescriptor::clearComponentDataEntry( const OUString
& rName
)
419 comphelper::SequenceAsHashMap::iterator aPropertyIter
= find( PROP_COMPONENTDATA() );
420 if( aPropertyIter
!= end() )
422 css::uno::Any
& rCompDataAny
= aPropertyIter
->second
;
423 bool bHasNamedValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::NamedValue
> >();
424 bool bHasPropValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::PropertyValue
> >();
425 OSL_ENSURE( bHasNamedValues
|| bHasPropValues
, "MediaDescriptor::clearComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
426 if( bHasNamedValues
|| bHasPropValues
)
428 // remove the value with the passed name
429 comphelper::SequenceAsHashMap
aCompDataMap( rCompDataAny
);
430 aCompDataMap
.erase( rName
);
431 // write back the sequence, or remove it completely if it is empty
432 if( aCompDataMap
.empty() )
433 erase( aPropertyIter
);
435 rCompDataAny
= aCompDataMap
.getAsConstAny( bHasPropValues
);
440 ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> MediaDescriptor::requestAndVerifyDocPassword(
441 comphelper::IDocPasswordVerifier
& rVerifier
,
442 comphelper::DocPasswordRequestType eRequestType
,
443 const ::std::vector
< OUString
>* pDefaultPasswords
)
445 css::uno::Sequence
< css::beans::NamedValue
> aMediaEncData
= getUnpackedValueOrDefault(
446 PROP_ENCRYPTIONDATA(), css::uno::Sequence
< css::beans::NamedValue
>() );
447 OUString aMediaPassword
= getUnpackedValueOrDefault(
448 PROP_PASSWORD(), OUString() );
449 css::uno::Reference
< css::task::XInteractionHandler
> xInteractHandler
= getUnpackedValueOrDefault(
450 PROP_INTERACTIONHANDLER(), css::uno::Reference
< css::task::XInteractionHandler
>() );
451 OUString aDocumentName
= getUnpackedValueOrDefault(
452 PROP_URL(), OUString() );
454 bool bIsDefaultPassword
= false;
455 css::uno::Sequence
< css::beans::NamedValue
> aEncryptionData
= comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
456 rVerifier
, aMediaEncData
, aMediaPassword
, xInteractHandler
, aDocumentName
, eRequestType
, pDefaultPasswords
, &bIsDefaultPassword
);
458 erase( PROP_PASSWORD() );
459 erase( PROP_ENCRYPTIONDATA() );
461 // insert valid password into media descriptor (but not a default password)
462 if( (aEncryptionData
.getLength() > 0) && !bIsDefaultPassword
)
463 (*this)[ PROP_ENCRYPTIONDATA() ] <<= aEncryptionData
;
465 return aEncryptionData
;
468 bool MediaDescriptor::addInputStream()
470 return impl_addInputStream( true );
473 /*-----------------------------------------------*/
474 bool MediaDescriptor::addInputStreamOwnLock()
476 return impl_addInputStream(
477 officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get());
480 /*-----------------------------------------------*/
481 bool MediaDescriptor::impl_addInputStream( bool bLockFile
)
483 // check for an already existing stream item first
484 const_iterator pIt
= find(MediaDescriptor::PROP_INPUTSTREAM());
490 // No stream available - create a new one
491 // a) data comes as PostData ...
492 pIt
= find(MediaDescriptor::PROP_POSTDATA());
495 const css::uno::Any
& rPostData
= pIt
->second
;
496 css::uno::Reference
< css::io::XInputStream
> xPostData
;
497 rPostData
>>= xPostData
;
499 return impl_openStreamWithPostData( xPostData
);
502 // b) ... or we must get it from the given URL
503 OUString sURL
= getUnpackedValueOrDefault(MediaDescriptor::PROP_URL(), OUString());
505 throw css::uno::Exception("Found no URL.",
506 css::uno::Reference
< css::uno::XInterface
>());
508 return impl_openStreamWithURL( removeFragment(sURL
), bLockFile
);
510 catch(const css::uno::Exception
& ex
)
514 "invalid MediaDescriptor detected: " << ex
.Message
);
519 bool MediaDescriptor::impl_openStreamWithPostData( const css::uno::Reference
< css::io::XInputStream
>& _rxPostData
)
520 throw(::com::sun::star::uno::RuntimeException
)
522 if ( !_rxPostData
.is() )
523 throw css::lang::IllegalArgumentException("Found invalid PostData.",
524 css::uno::Reference
< css::uno::XInterface
>(), 1);
526 // PostData can't be used in read/write mode!
527 (*this)[MediaDescriptor::PROP_READONLY()] <<= sal_True
;
529 // prepare the environment
530 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction
= getUnpackedValueOrDefault(
531 MediaDescriptor::PROP_INTERACTIONHANDLER(),
532 css::uno::Reference
< css::task::XInteractionHandler
>());
533 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
534 ::ucbhelper::CommandEnvironment
* pCommandEnv
= new ::ucbhelper::CommandEnvironment(xInteraction
, xProgress
);
535 css::uno::Reference
< css::ucb::XCommandEnvironment
> xCommandEnv(static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
538 OUString sMediaType
= getUnpackedValueOrDefault(MediaDescriptor::PROP_MEDIATYPE(), OUString());
539 if (sMediaType
.isEmpty())
541 sMediaType
= "application/x-www-form-urlencoded";
542 (*this)[MediaDescriptor::PROP_MEDIATYPE()] <<= sMediaType
;
546 OUString
sURL( getUnpackedValueOrDefault( PROP_URL(), OUString() ) );
548 css::uno::Reference
< css::io::XInputStream
> xResultStream
;
551 // seek PostData stream to the beginning
552 css::uno::Reference
< css::io::XSeekable
> xSeek( _rxPostData
, css::uno::UNO_QUERY
);
556 // a content for the URL
557 ::ucbhelper::Content
aContent( sURL
, xCommandEnv
, comphelper::getProcessComponentContext() );
560 css::ucb::PostCommandArgument2 aPostArgument
;
561 aPostArgument
.Source
= _rxPostData
;
562 css::uno::Reference
< css::io::XActiveDataSink
> xSink( new ucbhelper::ActiveDataSink
);
563 aPostArgument
.Sink
= xSink
;
564 aPostArgument
.MediaType
= sMediaType
;
565 aPostArgument
.Referer
= getUnpackedValueOrDefault( PROP_REFERRER(), OUString() );
567 OUString
sCommandName( "post" );
568 aContent
.executeCommand( sCommandName
, css::uno::makeAny( aPostArgument
) );
571 xResultStream
= xSink
->getInputStream();
573 catch( const css::uno::Exception
& )
578 if ( !xResultStream
.is() )
580 OSL_FAIL( "no valid reply to the HTTP-Post" );
584 (*this)[MediaDescriptor::PROP_INPUTSTREAM()] <<= xResultStream
;
588 /*-----------------------------------------------*/
589 bool MediaDescriptor::impl_openStreamWithURL( const OUString
& sURL
, bool bLockFile
)
590 throw(::com::sun::star::uno::RuntimeException
)
592 OUString
referer(getUnpackedValueOrDefault(PROP_REFERRER(), OUString()));
593 if (SvtSecurityOptions().isUntrustedReferer(referer
)) {
597 // prepare the environment
598 css::uno::Reference
< css::task::XInteractionHandler
> xOrgInteraction
= getUnpackedValueOrDefault(
599 MediaDescriptor::PROP_INTERACTIONHANDLER(),
600 css::uno::Reference
< css::task::XInteractionHandler
>());
602 comphelper::StillReadWriteInteraction
* pInteraction
= new comphelper::StillReadWriteInteraction(xOrgInteraction
);
603 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction(static_cast< css::task::XInteractionHandler
* >(pInteraction
), css::uno::UNO_QUERY
);
605 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
606 ::ucbhelper::CommandEnvironment
* pCommandEnv
= new ::ucbhelper::CommandEnvironment(xInteraction
, xProgress
);
607 css::uno::Reference
< css::ucb::XCommandEnvironment
> xCommandEnv(static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
609 // try to create the content
610 // no content -> no stream => return immediately with FALSE
611 ::ucbhelper::Content aContent
;
612 css::uno::Reference
< css::ucb::XContent
> xContent
;
615 aContent
= ::ucbhelper::Content(sURL
, xCommandEnv
, comphelper::getProcessComponentContext());
616 xContent
= aContent
.get();
618 catch(const css::uno::RuntimeException
&)
620 catch(const css::ucb::ContentCreationException
& e
)
624 "caught ContentCreationException \"" << e
.Message
625 << "\" while opening <" << sURL
<< ">");
626 return false; // TODO error handling
628 catch(const css::uno::Exception
& e
)
632 "caught Exception \"" << e
.Message
<< "\" while opening <"
634 return false; // TODO error handling
637 // try to open the file in read/write mode
638 // (if its allowed to do so).
639 // But handle errors in a "hidden mode". Because
640 // we try it readonly later - if read/write isnt an option.
641 css::uno::Reference
< css::io::XStream
> xStream
;
642 css::uno::Reference
< css::io::XInputStream
> xInputStream
;
644 bool bReadOnly
= false;
645 bool bModeRequestedExplicitly
= false;
646 const_iterator pIt
= find(MediaDescriptor::PROP_READONLY());
649 pIt
->second
>>= bReadOnly
;
650 bModeRequestedExplicitly
= true;
653 if ( !bReadOnly
&& bLockFile
)
657 // TODO: use "special" still interaction to supress error messages
658 xStream
= aContent
.openWriteableStream();
660 xInputStream
= xStream
->getInputStream();
662 catch(const css::uno::RuntimeException
&)
664 catch(const css::uno::Exception
& e
)
666 // ignore exception, if reason was problem reasoned on
667 // open it in WRITEABLE mode! Then we try it READONLY
668 // later a second time.
669 // All other errors must be handled as real error an
670 // break this method.
671 if (!pInteraction
->wasWriteError() || bModeRequestedExplicitly
)
675 "caught Exception \"" << e
.Message
676 << "\" while opening <" << sURL
<< ">");
680 xInputStream
.clear();
684 // If opening of the stream in read/write mode wasnt allowed
685 // or failed by an error - we must try it in readonly mode.
686 if (!xInputStream
.is())
692 css::uno::Reference
< css::ucb::XContentIdentifier
> xContId(
693 aContent
.get().is() ? aContent
.get()->getIdentifier() : 0 );
696 aScheme
= xContId
->getContentProviderScheme();
698 // Only file system content provider is able to provide XStream
699 // so for this content impossibility to create XStream triggers
700 // switch to readonly mode in case of opening with locking on
701 if( bLockFile
&& aScheme
.equalsIgnoreAsciiCase("file") )
705 bool bRequestReadOnly
= bReadOnly
;
706 aContent
.getPropertyValue("IsReadOnly") >>= bReadOnly
;
707 if ( bReadOnly
&& !bRequestReadOnly
&& bModeRequestedExplicitly
)
708 return false; // the document is explicitly requested with WRITEABLE mode
711 catch(const css::uno::RuntimeException
&)
713 catch(const css::uno::Exception
&)
714 { /* no error handling if IsReadOnly property does not exist for UCP */ }
717 (*this)[MediaDescriptor::PROP_READONLY()] <<= bReadOnly
;
719 pInteraction
->resetInterceptions();
720 pInteraction
->resetErrorStates();
723 // all the contents except file-URLs should be opened as usual
724 if ( bLockFile
|| !aScheme
.equalsIgnoreAsciiCase("file") )
725 xInputStream
= aContent
.openStream();
727 xInputStream
= aContent
.openStreamNoLock();
729 catch(const css::uno::RuntimeException
&)
731 catch(const css::uno::Exception
& e
)
735 "caught Exception \"" << e
.Message
<< "\" while opening <"
741 // add streams to the descriptor
743 (*this)[MediaDescriptor::PROP_UCBCONTENT()] <<= xContent
;
745 (*this)[MediaDescriptor::PROP_STREAM()] <<= xStream
;
746 if (xInputStream
.is())
747 (*this)[MediaDescriptor::PROP_INPUTSTREAM()] <<= xInputStream
;
749 // At least we need an input stream. The r/w stream is optional ...
750 return xInputStream
.is();
753 } // namespace comphelper
755 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */