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 <sal/log.hxx>
22 #include <unotools/mediadescriptor.hxx>
23 #include <unotools/securityoptions.hxx>
24 #include <comphelper/namedvaluecollection.hxx>
25 #include <comphelper/stillreadwriteinteraction.hxx>
27 #include <com/sun/star/ucb/XContent.hpp>
28 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
29 #include <com/sun/star/task/XInteractionHandler.hpp>
30 #include <com/sun/star/io/XStream.hpp>
31 #include <com/sun/star/io/XActiveDataSink.hpp>
32 #include <com/sun/star/io/XSeekable.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/lang/IllegalArgumentException.hpp>
35 #include <com/sun/star/util/XURLTransformer.hpp>
36 #include <com/sun/star/ucb/InteractiveIOException.hpp>
37 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
38 #include <com/sun/star/ucb/CommandFailedException.hpp>
39 #include <com/sun/star/task/XInteractionAbort.hpp>
40 #include <com/sun/star/uri/UriReferenceFactory.hpp>
41 #include <com/sun/star/uri/XUriReference.hpp>
42 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
43 #include <com/sun/star/container/XNameAccess.hpp>
44 #include <officecfg/Office/Common.hxx>
45 #include <ucbhelper/interceptedinteraction.hxx>
46 #include <ucbhelper/content.hxx>
47 #include <ucbhelper/commandenvironment.hxx>
48 #include <ucbhelper/activedatasink.hxx>
49 #include <comphelper/processfactory.hxx>
50 #include <osl/diagnose.h>
56 OUString
removeFragment(OUString
const & uri
) {
57 css::uno::Reference
< css::uri::XUriReference
> ref(
58 css::uri::UriReferenceFactory::create(
59 comphelper::getProcessComponentContext())->
63 return ref
->getUriReference();
65 SAL_WARN("unotools.misc", "cannot parse <" << uri
<< ">");
72 const OUString
& MediaDescriptor::PROP_ABORTED()
74 static const OUString
sProp("Aborted");
78 const OUString
& MediaDescriptor::PROP_ASTEMPLATE()
80 static const OUString
sProp("AsTemplate");
84 const OUString
& MediaDescriptor::PROP_COMPONENTDATA()
86 static const OUString
sProp("ComponentData");
90 const OUString
& MediaDescriptor::PROP_DOCUMENTSERVICE()
92 static const OUString
sProp("DocumentService");
96 const OUString
& MediaDescriptor::PROP_ENCRYPTIONDATA()
98 static const OUString
sProp("EncryptionData");
102 const OUString
& MediaDescriptor::PROP_FILENAME()
104 static const OUString
sProp("FileName");
108 const OUString
& MediaDescriptor::PROP_FILTERNAME()
110 static const OUString
sProp("FilterName");
114 const OUString
& MediaDescriptor::PROP_FILTERPROVIDER()
116 static const OUString
aProp("FilterProvider");
120 const OUString
& MediaDescriptor::PROP_FILTEROPTIONS()
122 static const OUString
sProp("FilterOptions");
126 const OUString
& MediaDescriptor::PROP_FRAME()
128 static const OUString
sProp("Frame");
132 const OUString
& MediaDescriptor::PROP_FRAMENAME()
134 static const OUString
sProp("FrameName");
138 const OUString
& MediaDescriptor::PROP_HIDDEN()
140 static const OUString
sProp("Hidden");
144 const OUString
& MediaDescriptor::PROP_INPUTSTREAM()
146 static const OUString
sProp("InputStream");
150 const OUString
& MediaDescriptor::PROP_INTERACTIONHANDLER()
152 static const OUString
sProp("InteractionHandler");
156 const OUString
& MediaDescriptor::PROP_JUMPMARK()
158 static const OUString
sProp("JumpMark");
162 const OUString
& MediaDescriptor::PROP_MACROEXECUTIONMODE()
164 static const OUString
sProp("MacroExecutionMode");
168 const OUString
& MediaDescriptor::PROP_MEDIATYPE()
170 static const OUString
sProp("MediaType");
174 const OUString
& MediaDescriptor::PROP_MINIMIZED()
176 static const OUString
sProp("Minimized");
180 const OUString
& MediaDescriptor::PROP_NOAUTOSAVE()
182 static const OUString
sProp("NoAutoSave");
186 const OUString
& MediaDescriptor::PROP_OPENNEWVIEW()
188 static const OUString
sProp("OpenNewView");
192 const OUString
& MediaDescriptor::PROP_OUTPUTSTREAM()
194 static const OUString
sProp("OutputStream");
198 const OUString
& MediaDescriptor::PROP_POSTDATA()
200 static const OUString
sProp("PostData");
204 const OUString
& MediaDescriptor::PROP_PREVIEW()
206 static const OUString
sProp("Preview");
210 const OUString
& MediaDescriptor::PROP_READONLY()
212 static const OUString
sProp("ReadOnly");
216 const OUString
& MediaDescriptor::PROP_REFERRER()
218 static const OUString
sProp("Referer");
222 const OUString
& MediaDescriptor::PROP_STATUSINDICATOR()
224 static const OUString
sProp("StatusIndicator");
228 const OUString
& MediaDescriptor::PROP_STREAM()
230 static const OUString
sProp("Stream");
234 const OUString
& MediaDescriptor::PROP_STREAMFOROUTPUT()
236 static const OUString
sProp("StreamForOutput");
240 const OUString
& MediaDescriptor::PROP_TEMPLATENAME()
242 static const OUString
sProp("TemplateName");
246 const OUString
& MediaDescriptor::PROP_TYPENAME()
248 static const OUString
sProp("TypeName");
252 const OUString
& MediaDescriptor::PROP_UCBCONTENT()
254 static const OUString
sProp("UCBContent");
258 const OUString
& MediaDescriptor::PROP_UPDATEDOCMODE()
260 static const OUString
sProp("UpdateDocMode");
264 const OUString
& MediaDescriptor::PROP_URL()
266 static const OUString
sProp("URL");
270 const OUString
& MediaDescriptor::PROP_VERSION()
272 static const OUString
sProp("Version");
276 const OUString
& MediaDescriptor::PROP_DOCUMENTTITLE()
278 static const OUString
sProp("DocumentTitle");
282 const OUString
& MediaDescriptor::PROP_MODEL()
284 static const OUString
sProp("Model");
288 const OUString
& MediaDescriptor::PROP_PASSWORD()
290 static const OUString
sProp("Password");
294 const OUString
& MediaDescriptor::PROP_TITLE()
296 static const OUString
sProp("Title");
300 const OUString
& MediaDescriptor::PROP_SALVAGEDFILE()
302 static const OUString
sProp("SalvagedFile");
306 const OUString
& MediaDescriptor::PROP_VIEWONLY()
308 static const OUString
sProp("ViewOnly");
312 const OUString
& MediaDescriptor::PROP_DOCUMENTBASEURL()
314 static const OUString
sProp("DocumentBaseURL");
318 MediaDescriptor::MediaDescriptor()
319 : SequenceAsHashMap()
323 MediaDescriptor::MediaDescriptor(const css::uno::Sequence
< css::beans::PropertyValue
>& lSource
)
324 : SequenceAsHashMap(lSource
)
328 bool MediaDescriptor::isStreamReadOnly() const
330 static const char CONTENTSCHEME_FILE
[] = "file";
331 static const char CONTENTPROP_ISREADONLY
[] = "IsReadOnly";
332 static bool READONLY_FALLBACK
= false;
334 bool bReadOnly
= READONLY_FALLBACK
;
336 // check for explicit readonly state
337 const_iterator pIt
= find(MediaDescriptor::PROP_READONLY());
340 pIt
->second
>>= bReadOnly
;
344 // streams based on post data are readonly by definition
345 pIt
= find(MediaDescriptor::PROP_POSTDATA());
349 // A XStream capsulate XInputStream and XOutputStream ...
350 // If it exists - the file must be open in read/write mode!
351 pIt
= find(MediaDescriptor::PROP_STREAM());
355 // Only file system content provider is able to provide XStream
356 // so for this content impossibility to create XStream triggers
357 // switch to readonly mode.
360 css::uno::Reference
< css::ucb::XContent
> xContent
= getUnpackedValueOrDefault(MediaDescriptor::PROP_UCBCONTENT(), css::uno::Reference
< css::ucb::XContent
>());
363 css::uno::Reference
< css::ucb::XContentIdentifier
> xId(xContent
->getIdentifier(), css::uno::UNO_QUERY
);
366 aScheme
= xId
->getContentProviderScheme();
368 if (aScheme
.equalsIgnoreAsciiCase(CONTENTSCHEME_FILE
))
372 ::ucbhelper::Content
aContent(xContent
, css::uno::Reference
< css::ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext());
373 aContent
.getPropertyValue(CONTENTPROP_ISREADONLY
) >>= bReadOnly
;
377 catch(const css::uno::RuntimeException
& )
379 catch(const css::uno::Exception
&)
385 css::uno::Any
MediaDescriptor::getComponentDataEntry( const OUString
& rName
) const
387 comphelper::SequenceAsHashMap::const_iterator aPropertyIter
= find( PROP_COMPONENTDATA() );
388 if( aPropertyIter
!= end() )
389 return comphelper::NamedValueCollection( aPropertyIter
->second
).get( rName
);
390 return css::uno::Any();
393 void MediaDescriptor::setComponentDataEntry( const OUString
& rName
, const css::uno::Any
& rValue
)
395 if( rValue
.hasValue() )
397 // get or create the 'ComponentData' property entry
398 css::uno::Any
& rCompDataAny
= operator[]( PROP_COMPONENTDATA() );
399 // insert the value (retain sequence type, create NamedValue elements by default)
400 bool bHasNamedValues
= !rCompDataAny
.hasValue() || rCompDataAny
.has
< css::uno::Sequence
< css::beans::NamedValue
> >();
401 bool bHasPropValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::PropertyValue
> >();
402 OSL_ENSURE( bHasNamedValues
|| bHasPropValues
, "MediaDescriptor::setComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
403 if( bHasNamedValues
|| bHasPropValues
)
405 // insert or overwrite the passed value
406 comphelper::SequenceAsHashMap
aCompDataMap( rCompDataAny
);
407 aCompDataMap
[ rName
] = rValue
;
408 // write back the sequence (restore sequence with correct element type)
409 rCompDataAny
= aCompDataMap
.getAsConstAny( bHasPropValues
);
414 // if an empty Any is passed, clear the entry
415 clearComponentDataEntry( rName
);
419 void MediaDescriptor::clearComponentDataEntry( const OUString
& rName
)
421 comphelper::SequenceAsHashMap::iterator aPropertyIter
= find( PROP_COMPONENTDATA() );
422 if( aPropertyIter
!= end() )
424 css::uno::Any
& rCompDataAny
= aPropertyIter
->second
;
425 bool bHasNamedValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::NamedValue
> >();
426 bool bHasPropValues
= rCompDataAny
.has
< css::uno::Sequence
< css::beans::PropertyValue
> >();
427 OSL_ENSURE( bHasNamedValues
|| bHasPropValues
, "MediaDescriptor::clearComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
428 if( bHasNamedValues
|| bHasPropValues
)
430 // remove the value with the passed name
431 comphelper::SequenceAsHashMap
aCompDataMap( rCompDataAny
);
432 aCompDataMap
.erase( rName
);
433 // write back the sequence, or remove it completely if it is empty
434 if( aCompDataMap
.empty() )
435 erase( aPropertyIter
);
437 rCompDataAny
= aCompDataMap
.getAsConstAny( bHasPropValues
);
442 ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> MediaDescriptor::requestAndVerifyDocPassword(
443 comphelper::IDocPasswordVerifier
& rVerifier
,
444 comphelper::DocPasswordRequestType eRequestType
,
445 const ::std::vector
< OUString
>* pDefaultPasswords
)
447 css::uno::Sequence
< css::beans::NamedValue
> aMediaEncData
= getUnpackedValueOrDefault(
448 PROP_ENCRYPTIONDATA(), css::uno::Sequence
< css::beans::NamedValue
>() );
449 OUString aMediaPassword
= getUnpackedValueOrDefault(
450 PROP_PASSWORD(), OUString() );
451 css::uno::Reference
< css::task::XInteractionHandler
> xInteractHandler
= getUnpackedValueOrDefault(
452 PROP_INTERACTIONHANDLER(), css::uno::Reference
< css::task::XInteractionHandler
>() );
453 OUString aDocumentName
= getUnpackedValueOrDefault(
454 PROP_URL(), OUString() );
456 bool bIsDefaultPassword
= false;
457 css::uno::Sequence
< css::beans::NamedValue
> aEncryptionData
= comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
458 rVerifier
, aMediaEncData
, aMediaPassword
, xInteractHandler
, aDocumentName
, eRequestType
, pDefaultPasswords
, &bIsDefaultPassword
);
460 erase( PROP_PASSWORD() );
461 erase( PROP_ENCRYPTIONDATA() );
463 // insert valid password into media descriptor (but not a default password)
464 if( (aEncryptionData
.getLength() > 0) && !bIsDefaultPassword
)
465 (*this)[ PROP_ENCRYPTIONDATA() ] <<= aEncryptionData
;
467 return aEncryptionData
;
470 bool MediaDescriptor::addInputStream()
472 return impl_addInputStream( true );
475 /*-----------------------------------------------*/
476 bool MediaDescriptor::addInputStreamOwnLock()
478 return impl_addInputStream(
479 officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get());
482 /*-----------------------------------------------*/
483 bool MediaDescriptor::impl_addInputStream( bool bLockFile
)
485 // check for an already existing stream item first
486 const_iterator pIt
= find(MediaDescriptor::PROP_INPUTSTREAM());
492 // No stream available - create a new one
493 // a) data comes as PostData ...
494 pIt
= find(MediaDescriptor::PROP_POSTDATA());
497 const css::uno::Any
& rPostData
= pIt
->second
;
498 css::uno::Reference
< css::io::XInputStream
> xPostData
;
499 rPostData
>>= xPostData
;
501 return impl_openStreamWithPostData( xPostData
);
504 // b) ... or we must get it from the given URL
505 OUString sURL
= getUnpackedValueOrDefault(MediaDescriptor::PROP_URL(), OUString());
507 throw css::uno::Exception("Found no URL.",
508 css::uno::Reference
< css::uno::XInterface
>());
510 return impl_openStreamWithURL( removeFragment(sURL
), bLockFile
);
512 catch(const css::uno::Exception
& ex
)
516 "invalid MediaDescriptor detected: " << ex
.Message
);
521 bool MediaDescriptor::impl_openStreamWithPostData( const css::uno::Reference
< css::io::XInputStream
>& _rxPostData
)
522 throw(::com::sun::star::uno::RuntimeException
)
524 if ( !_rxPostData
.is() )
525 throw css::lang::IllegalArgumentException("Found invalid PostData.",
526 css::uno::Reference
< css::uno::XInterface
>(), 1);
528 // PostData can't be used in read/write mode!
529 (*this)[MediaDescriptor::PROP_READONLY()] <<= sal_True
;
531 // prepare the environment
532 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction
= getUnpackedValueOrDefault(
533 MediaDescriptor::PROP_INTERACTIONHANDLER(),
534 css::uno::Reference
< css::task::XInteractionHandler
>());
535 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
536 ::ucbhelper::CommandEnvironment
* pCommandEnv
= new ::ucbhelper::CommandEnvironment(xInteraction
, xProgress
);
537 css::uno::Reference
< css::ucb::XCommandEnvironment
> xCommandEnv(static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
540 OUString sMediaType
= getUnpackedValueOrDefault(MediaDescriptor::PROP_MEDIATYPE(), OUString());
541 if (sMediaType
.isEmpty())
543 sMediaType
= "application/x-www-form-urlencoded";
544 (*this)[MediaDescriptor::PROP_MEDIATYPE()] <<= sMediaType
;
548 OUString
sURL( getUnpackedValueOrDefault( PROP_URL(), OUString() ) );
550 css::uno::Reference
< css::io::XInputStream
> xResultStream
;
553 // seek PostData stream to the beginning
554 css::uno::Reference
< css::io::XSeekable
> xSeek( _rxPostData
, css::uno::UNO_QUERY
);
558 // a content for the URL
559 ::ucbhelper::Content
aContent( sURL
, xCommandEnv
, comphelper::getProcessComponentContext() );
562 css::ucb::PostCommandArgument2 aPostArgument
;
563 aPostArgument
.Source
= _rxPostData
;
564 css::uno::Reference
< css::io::XActiveDataSink
> xSink( new ucbhelper::ActiveDataSink
);
565 aPostArgument
.Sink
= xSink
;
566 aPostArgument
.MediaType
= sMediaType
;
567 aPostArgument
.Referer
= getUnpackedValueOrDefault( PROP_REFERRER(), OUString() );
569 OUString
sCommandName( "post" );
570 aContent
.executeCommand( sCommandName
, css::uno::makeAny( aPostArgument
) );
573 xResultStream
= xSink
->getInputStream();
575 catch( const css::uno::Exception
& )
580 if ( !xResultStream
.is() )
582 OSL_FAIL( "no valid reply to the HTTP-Post" );
586 (*this)[MediaDescriptor::PROP_INPUTSTREAM()] <<= xResultStream
;
590 /*-----------------------------------------------*/
591 bool MediaDescriptor::impl_openStreamWithURL( const OUString
& sURL
, bool bLockFile
)
592 throw(::com::sun::star::uno::RuntimeException
)
594 OUString
referer(getUnpackedValueOrDefault(PROP_REFERRER(), OUString()));
595 if (SvtSecurityOptions().isUntrustedReferer(referer
)) {
599 // prepare the environment
600 css::uno::Reference
< css::task::XInteractionHandler
> xOrgInteraction
= getUnpackedValueOrDefault(
601 MediaDescriptor::PROP_INTERACTIONHANDLER(),
602 css::uno::Reference
< css::task::XInteractionHandler
>());
604 comphelper::StillReadWriteInteraction
* pInteraction
= new comphelper::StillReadWriteInteraction(xOrgInteraction
);
605 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction(static_cast< css::task::XInteractionHandler
* >(pInteraction
), css::uno::UNO_QUERY
);
607 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
608 ::ucbhelper::CommandEnvironment
* pCommandEnv
= new ::ucbhelper::CommandEnvironment(xInteraction
, xProgress
);
609 css::uno::Reference
< css::ucb::XCommandEnvironment
> xCommandEnv(static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
611 // try to create the content
612 // no content -> no stream => return immediately with FALSE
613 ::ucbhelper::Content aContent
;
614 css::uno::Reference
< css::ucb::XContent
> xContent
;
617 aContent
= ::ucbhelper::Content(sURL
, xCommandEnv
, comphelper::getProcessComponentContext());
618 xContent
= aContent
.get();
620 catch(const css::uno::RuntimeException
&)
622 catch(const css::ucb::ContentCreationException
& e
)
626 "caught ContentCreationException \"" << e
.Message
627 << "\" while opening <" << sURL
<< ">");
628 return false; // TODO error handling
630 catch(const css::uno::Exception
& e
)
634 "caught Exception \"" << e
.Message
<< "\" while opening <"
636 return false; // TODO error handling
639 // try to open the file in read/write mode
640 // (if its allowed to do so).
641 // But handle errors in a "hidden mode". Because
642 // we try it readonly later - if read/write is not an option.
643 css::uno::Reference
< css::io::XStream
> xStream
;
644 css::uno::Reference
< css::io::XInputStream
> xInputStream
;
646 bool bReadOnly
= false;
647 bool bModeRequestedExplicitly
= false;
648 const_iterator pIt
= find(MediaDescriptor::PROP_READONLY());
651 pIt
->second
>>= bReadOnly
;
652 bModeRequestedExplicitly
= true;
655 if ( !bReadOnly
&& bLockFile
)
659 // TODO: use "special" still interaction to suppress error messages
660 xStream
= aContent
.openWriteableStream();
662 xInputStream
= xStream
->getInputStream();
664 catch(const css::uno::RuntimeException
&)
666 catch(const css::uno::Exception
& e
)
668 // ignore exception, if reason was problem reasoned on
669 // open it in WRITEABLE mode! Then we try it READONLY
670 // later a second time.
671 // All other errors must be handled as real error an
672 // break this method.
673 if (!pInteraction
->wasWriteError() || bModeRequestedExplicitly
)
677 "caught Exception \"" << e
.Message
678 << "\" while opening <" << sURL
<< ">");
682 xInputStream
.clear();
686 // If opening of the stream in read/write mode was not allowed
687 // or failed by an error - we must try it in readonly mode.
688 if (!xInputStream
.is())
694 css::uno::Reference
< css::ucb::XContentIdentifier
> xContId(
695 aContent
.get().is() ? aContent
.get()->getIdentifier() : 0 );
698 aScheme
= xContId
->getContentProviderScheme();
700 // Only file system content provider is able to provide XStream
701 // so for this content impossibility to create XStream triggers
702 // switch to readonly mode in case of opening with locking on
703 if( bLockFile
&& aScheme
.equalsIgnoreAsciiCase("file") )
707 bool bRequestReadOnly
= bReadOnly
;
708 aContent
.getPropertyValue("IsReadOnly") >>= bReadOnly
;
709 if ( bReadOnly
&& !bRequestReadOnly
&& bModeRequestedExplicitly
)
710 return false; // the document is explicitly requested with WRITEABLE mode
713 catch(const css::uno::RuntimeException
&)
715 catch(const css::uno::Exception
&)
716 { /* no error handling if IsReadOnly property does not exist for UCP */ }
719 (*this)[MediaDescriptor::PROP_READONLY()] <<= bReadOnly
;
721 pInteraction
->resetInterceptions();
722 pInteraction
->resetErrorStates();
725 // all the contents except file-URLs should be opened as usual
726 if ( bLockFile
|| !aScheme
.equalsIgnoreAsciiCase("file") )
727 xInputStream
= aContent
.openStream();
729 xInputStream
= aContent
.openStreamNoLock();
731 catch(const css::uno::RuntimeException
&)
733 catch(const css::uno::Exception
& e
)
737 "caught Exception \"" << e
.Message
<< "\" while opening <"
743 // add streams to the descriptor
745 (*this)[MediaDescriptor::PROP_UCBCONTENT()] <<= xContent
;
747 (*this)[MediaDescriptor::PROP_STREAM()] <<= xStream
;
748 if (xInputStream
.is())
749 (*this)[MediaDescriptor::PROP_INPUTSTREAM()] <<= xInputStream
;
751 // At least we need an input stream. The r/w stream is optional ...
752 return xInputStream
.is();
755 } // namespace comphelper
757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */