Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / doc / objstor.cxx
blob29ca6d9a0fede9c6a853495f04133ec4c0ad818a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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 <config_features.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
25 #include <cassert>
27 #include <svl/eitem.hxx>
28 #include <svl/stritem.hxx>
29 #include <svl/intitem.hxx>
30 #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
31 #include <com/sun/star/frame/XModel.hpp>
32 #include <com/sun/star/frame/XModule.hpp>
33 #include <com/sun/star/document/XFilter.hpp>
34 #include <com/sun/star/document/XImporter.hpp>
35 #include <com/sun/star/document/XExporter.hpp>
36 #include <com/sun/star/packages/zip/ZipIOException.hpp>
37 #include <com/sun/star/task/XInteractionHandler.hpp>
38 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
39 #include <com/sun/star/document/MacroExecMode.hpp>
40 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
41 #include <com/sun/star/beans/XPropertySetInfo.hpp>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/beans/PropertyValue.hpp>
44 #include <com/sun/star/beans/XPropertySet.hpp>
45 #include <com/sun/star/container/XNameAccess.hpp>
46 #include <com/sun/star/embed/ElementModes.hpp>
47 #include <com/sun/star/embed/EmbedStates.hpp>
48 #include <com/sun/star/embed/XTransactedObject.hpp>
49 #include <com/sun/star/embed/XEmbeddedObject.hpp>
50 #include <com/sun/star/embed/XEmbedPersist.hpp>
51 #include <com/sun/star/embed/XOptimizedStorage.hpp>
52 #include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
53 #include <com/sun/star/io/WrongFormatException.hpp>
54 #include <com/sun/star/io/XTruncate.hpp>
55 #include <com/sun/star/util/XModifiable.hpp>
56 #include <com/sun/star/util/RevisionTag.hpp>
57 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
58 #include <com/sun/star/text/XTextRange.hpp>
59 #include <com/sun/star/xml/crypto/CipherID.hpp>
60 #include <com/sun/star/xml/crypto/DigestID.hpp>
62 #include <com/sun/star/document/XDocumentProperties.hpp>
63 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
64 #include <comphelper/fileformat.h>
65 #include <comphelper/processfactory.hxx>
66 #include <svtools/langtab.hxx>
67 #include <svtools/sfxecode.hxx>
68 #include <unotools/configmgr.hxx>
69 #include <unotools/streamwrap.hxx>
71 #include <unotools/saveopt.hxx>
72 #include <unotools/useroptions.hxx>
73 #include <unotools/securityoptions.hxx>
74 #include <tools/urlobj.hxx>
75 #include <tools/diagnose_ex.h>
76 #include <unotools/ucbhelper.hxx>
77 #include <unotools/tempfile.hxx>
78 #include <unotools/docinfohelper.hxx>
79 #include <ucbhelper/content.hxx>
80 #include <sot/storage.hxx>
81 #include <sot/exchange.hxx>
82 #include <sot/formats.hxx>
83 #include <comphelper/storagehelper.hxx>
84 #include <comphelper/documentconstants.hxx>
85 #include <comphelper/string.hxx>
86 #include <vcl/errinf.hxx>
87 #include <vcl/svapp.hxx>
88 #include <vcl/weld.hxx>
89 #include <basic/modsizeexceeded.hxx>
90 #include <officecfg/Office/Common.hxx>
91 #include <osl/file.hxx>
92 #include <comphelper/scopeguard.hxx>
93 #include <comphelper/lok.hxx>
95 #include <sfx2/signaturestate.hxx>
96 #include <sfx2/app.hxx>
97 #include <sfx2/objsh.hxx>
98 #include <sfx2/sfxresid.hxx>
99 #include <sfx2/docfile.hxx>
100 #include <sfx2/fcontnr.hxx>
101 #include <sfx2/docfilt.hxx>
102 #include <sfx2/docfac.hxx>
103 #include <appopen.hxx>
104 #include <objshimp.hxx>
105 #include <sfx2/strings.hrc>
106 #include <sfx2/sfxsids.hrc>
107 #include <sfx2/dispatch.hxx>
108 #include <sfx2/sfxuno.hxx>
109 #include <sfx2/event.hxx>
110 #include <fltoptint.hxx>
111 #include <sfx2/viewfrm.hxx>
112 #include "graphhelp.hxx"
113 #include <appbaslib.hxx>
114 #include "objstor.hxx"
115 #include "exoticfileloadexception.hxx"
117 using namespace ::com::sun::star;
118 using namespace ::com::sun::star::container;
119 using namespace ::com::sun::star::lang;
120 using namespace ::com::sun::star::ui::dialogs;
121 using namespace ::com::sun::star::uno;
122 using namespace ::com::sun::star::beans;
123 using namespace ::com::sun::star::ucb;
124 using namespace ::com::sun::star::task;
125 using namespace ::com::sun::star::document;
126 using namespace ::cppu;
129 void impl_addToModelCollection(const css::uno::Reference< css::frame::XModel >& xModel)
131 if (!xModel.is())
132 return;
134 css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
135 css::uno::Reference< css::frame::XGlobalEventBroadcaster > xModelCollection =
136 css::frame::theGlobalEventBroadcaster::get(xContext);
139 xModelCollection->insert(css::uno::makeAny(xModel));
141 catch ( uno::Exception& )
143 SAL_WARN( "sfx.doc", "The document seems to be in the collection already!" );
148 bool SfxObjectShell::Save()
150 SaveChildren();
151 return true;
155 bool SfxObjectShell::SaveAs( SfxMedium& rMedium )
157 return SaveAsChildren( rMedium );
161 bool SfxObjectShell::QuerySlotExecutable( sal_uInt16 /*nSlotId*/ )
163 return true;
167 bool GetEncryptionData_Impl( const SfxItemSet* pSet, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
169 bool bResult = false;
170 if ( pSet )
172 const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_ENCRYPTIONDATA, false);
173 if ( pEncryptionDataItem )
175 pEncryptionDataItem->GetValue() >>= o_rEncryptionData;
176 bResult = true;
178 else
180 const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_PASSWORD, false);
181 if ( pPasswordItem )
183 o_rEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( pPasswordItem->GetValue() );
184 bResult = true;
189 return bResult;
193 bool SfxObjectShell::PutURLContentsToVersionStream_Impl(
194 const OUString& aURL,
195 const uno::Reference< embed::XStorage >& xDocStorage,
196 const OUString& aStreamName )
198 bool bResult = false;
201 uno::Reference< embed::XStorage > xVersion = xDocStorage->openStorageElement(
202 "Versions",
203 embed::ElementModes::READWRITE );
205 DBG_ASSERT( xVersion.is(),
206 "The method must throw an exception if the storage can not be opened!" );
207 if ( !xVersion.is() )
208 throw uno::RuntimeException();
210 uno::Reference< io::XStream > xVerStream = xVersion->openStreamElement(
211 aStreamName,
212 embed::ElementModes::READWRITE );
213 DBG_ASSERT( xVerStream.is(), "The method must throw an exception if the storage can not be opened!" );
214 if ( !xVerStream.is() )
215 throw uno::RuntimeException();
217 uno::Reference< io::XOutputStream > xOutStream = xVerStream->getOutputStream();
218 uno::Reference< io::XTruncate > xTrunc( xOutStream, uno::UNO_QUERY_THROW );
220 uno::Reference< io::XInputStream > xTmpInStream =
221 ::comphelper::OStorageHelper::GetInputStreamFromURL(
222 aURL, comphelper::getProcessComponentContext() );
223 assert( xTmpInStream.is() );
225 xTrunc->truncate();
226 ::comphelper::OStorageHelper::CopyInputToOutput( xTmpInStream, xOutStream );
227 xOutStream->closeOutput();
229 uno::Reference< embed::XTransactedObject > xTransact( xVersion, uno::UNO_QUERY );
230 DBG_ASSERT( xTransact.is(), "The storage must implement XTransacted interface!\n" );
231 if ( xTransact.is() )
232 xTransact->commit();
234 bResult = true;
236 catch( uno::Exception& )
238 // TODO/LATER: handle the error depending on exception
239 SetError(ERRCODE_IO_GENERAL);
242 return bResult;
246 OUString SfxObjectShell::CreateTempCopyOfStorage_Impl( const uno::Reference< embed::XStorage >& xStorage )
248 OUString aTempURL = ::utl::TempFile().GetURL();
250 DBG_ASSERT( !aTempURL.isEmpty(), "Can't create a temporary file!\n" );
251 if ( !aTempURL.isEmpty() )
255 uno::Reference< embed::XStorage > xTempStorage =
256 ::comphelper::OStorageHelper::GetStorageFromURL( aTempURL, embed::ElementModes::READWRITE );
258 // the password will be transferred from the xStorage to xTempStorage by storage implementation
259 xStorage->copyToStorage( xTempStorage );
261 // the temporary storage was committed by the previous method and it will die by refcount
263 catch ( uno::Exception& )
265 SAL_WARN( "sfx.doc", "Creation of a storage copy is failed!" );
266 ::utl::UCBContentHelper::Kill( aTempURL );
268 aTempURL.clear();
270 // TODO/LATER: may need error code setting based on exception
271 SetError(ERRCODE_IO_GENERAL);
275 return aTempURL;
279 SvGlobalName const & SfxObjectShell::GetClassName() const
281 return GetFactory().GetClassId();
285 void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xStorage,
286 sal_Int32 nVersion, bool bTemplate ) const
288 uno::Reference< beans::XPropertySet > xProps( xStorage, uno::UNO_QUERY );
290 if ( !xProps.is() )
291 return;
293 SotClipboardFormatId nClipFormat = SotClipboardFormatId::NONE;
295 SvGlobalName aName;
296 OUString aFullTypeName;
297 FillClass( &aName, &nClipFormat, &aFullTypeName, nVersion, bTemplate );
299 if ( nClipFormat == SotClipboardFormatId::NONE )
300 return;
302 // basic doesn't have a ClipFormat
303 // without MediaType the storage is not really usable, but currently the BasicIDE still
304 // is an SfxObjectShell and so we can't take this as an error
305 datatransfer::DataFlavor aDataFlavor;
306 SotExchange::GetFormatDataFlavor( nClipFormat, aDataFlavor );
307 if ( aDataFlavor.MimeType.isEmpty() )
308 return;
312 xProps->setPropertyValue("MediaType", uno::makeAny( aDataFlavor.MimeType ) );
314 catch( uno::Exception& )
316 const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
319 SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013;
320 if (!utl::ConfigManager::IsFuzzing())
322 SvtSaveOptions aSaveOpt;
323 nDefVersion = aSaveOpt.GetODFSaneDefaultVersion();
326 // the default values, that should be used for ODF1.1 and older formats
327 uno::Sequence< beans::NamedValue > aEncryptionAlgs
329 { "StartKeyGenerationAlgorithm", css::uno::makeAny(xml::crypto::DigestID::SHA1) },
330 { "EncryptionAlgorithm", css::uno::makeAny(xml::crypto::CipherID::BLOWFISH_CFB_8) },
331 { "ChecksumAlgorithm", css::uno::makeAny(xml::crypto::DigestID::SHA1_1K) }
334 if (nDefVersion >= SvtSaveOptions::ODFSVER_012)
338 // older versions can not have this property set, it exists only starting from ODF1.2
339 uno::Reference<frame::XModule> const xModule(GetModel(), uno::UNO_QUERY);
340 bool const isBaseForm(xModule.is() &&
341 xModule->getIdentifier() == "com.sun.star.sdb.FormDesign");
342 SAL_INFO_IF(isBaseForm, "sfx.doc", "tdf#138209 force form export to ODF 1.2");
343 if (!isBaseForm && SvtSaveOptions::ODFSVER_013 <= nDefVersion)
345 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_013_TEXT));
347 else
349 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_012_TEXT));
352 catch( uno::Exception& )
356 aEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256;
357 aEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
358 aEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
363 // set the encryption algorithms accordingly;
364 // the setting does not trigger encryption,
365 // it just provides the format for the case that contents should be encrypted
366 uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xStorage, uno::UNO_QUERY_THROW );
367 xEncr->setEncryptionAlgorithms( aEncryptionAlgs );
369 catch( uno::Exception& )
371 const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
376 void SfxObjectShell::PrepareSecondTryLoad_Impl()
378 // only for internal use
379 pImpl->m_xDocStorage.clear();
380 pImpl->m_bIsInit = false;
381 ResetError();
385 bool SfxObjectShell::GeneralInit_Impl( const uno::Reference< embed::XStorage >& xStorage,
386 bool bTypeMustBeSetAlready )
388 if ( pImpl->m_bIsInit )
389 return false;
391 pImpl->m_bIsInit = true;
392 if ( xStorage.is() )
394 // no notification is required the storage is set the first time
395 pImpl->m_xDocStorage = xStorage;
397 try {
398 uno::Reference < beans::XPropertySet > xPropSet( xStorage, uno::UNO_QUERY_THROW );
399 Any a = xPropSet->getPropertyValue("MediaType");
400 OUString aMediaType;
401 if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
403 if ( bTypeMustBeSetAlready )
405 SetError(ERRCODE_IO_BROKENPACKAGE);
406 return false;
409 SetupStorage( xStorage, SOFFICE_FILEFORMAT_CURRENT, false );
412 catch ( uno::Exception& )
414 SAL_WARN( "sfx.doc", "Can't check storage's mediatype!" );
417 else
418 pImpl->m_bCreateTempStor = true;
420 return true;
424 bool SfxObjectShell::InitNew( const uno::Reference< embed::XStorage >& xStorage )
426 return GeneralInit_Impl( xStorage, false );
430 bool SfxObjectShell::Load( SfxMedium& rMedium )
432 return GeneralInit_Impl(rMedium.GetStorage(), !tools::isEmptyFileUrl(rMedium.GetName()));
435 void SfxObjectShell::DoInitUnitTest()
437 pMedium = new SfxMedium;
440 bool SfxObjectShell::DoInitNew( SfxMedium* pMed )
441 /* [Description]
443 This from SvPersist inherited virtual method is called to initialize
444 the SfxObjectShell instance from a storage (PStore! = 0) or (PStore == 0)
446 Like with all Do...-methods there is a from a control, the actual
447 implementation is done by the virtual method in which also the
448 InitNew(SvStorate *) from the SfxObjectShell-Subclass is implemented.
450 For pStore == 0 the SfxObjectShell-instance is connected to an empty
451 SfxMedium, otherwise a SfxMedium, which refers to the SotStorage
452 passed as a parameter.
454 The object is only initialized correctly after InitNew() or Load().
456 [Return value]
457 true The object has been initialized.
458 false The object could not be initialized
462 ModifyBlocker_Impl aBlock( this );
463 pMedium = pMed;
464 if ( !pMedium )
466 pMedium = new SfxMedium;
469 pMedium->CanDisposeStorage_Impl( true );
471 if ( InitNew( pMed ? pMed->GetStorage() : uno::Reference < embed::XStorage >() ) )
473 // empty documents always get their macros from the user, so there is no reason to restrict access
474 pImpl->aMacroMode.allowMacroExecution();
475 if ( SfxObjectCreateMode::EMBEDDED == eCreateMode )
476 SetTitle(SfxResId(STR_NONAME));
478 uno::Reference< frame::XModel > xModel = GetModel();
479 if ( xModel.is() )
481 SfxItemSet *pSet = GetMedium()->GetItemSet();
482 uno::Sequence< beans::PropertyValue > aArgs;
483 TransformItems( SID_OPENDOC, *pSet, aArgs );
484 sal_Int32 nLength = aArgs.getLength();
485 aArgs.realloc( nLength + 1 );
486 aArgs[nLength].Name = "Title";
487 aArgs[nLength].Value <<= GetTitle( SFX_TITLE_DETECT );
488 xModel->attachResource( OUString(), aArgs );
489 if (!utl::ConfigManager::IsFuzzing())
490 impl_addToModelCollection(xModel);
493 SetInitialized_Impl( true );
494 return true;
497 return false;
500 bool SfxObjectShell::ImportFromGeneratedStream_Impl(
501 const uno::Reference< io::XStream >& xStream,
502 const uno::Sequence< beans::PropertyValue >& rMediaDescr )
504 if ( !xStream.is() )
505 return false;
507 if ( pMedium && pMedium->HasStorage_Impl() )
508 pMedium->CloseStorage();
510 bool bResult = false;
514 uno::Reference< embed::XStorage > xStorage =
515 ::comphelper::OStorageHelper::GetStorageFromStream( xStream );
517 if ( !xStorage.is() )
518 throw uno::RuntimeException();
520 if ( !pMedium )
521 pMedium = new SfxMedium( xStorage, OUString() );
522 else
523 pMedium->SetStorage_Impl( xStorage );
525 SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
526 TransformParameters( SID_OPENDOC, rMediaDescr, aSet );
527 pMedium->GetItemSet()->Put( aSet );
528 pMedium->CanDisposeStorage_Impl( false );
529 uno::Reference<text::XTextRange> xInsertTextRange;
530 for (const auto& rProp : rMediaDescr)
532 if (rProp.Name == "TextInsertModeRange")
534 rProp.Value >>= xInsertTextRange;
538 if (xInsertTextRange.is())
540 bResult = InsertGeneratedStream(*pMedium, xInsertTextRange);
542 else
545 // allow the subfilter to reinit the model
546 if ( pImpl->m_bIsInit )
547 pImpl->m_bIsInit = false;
549 if ( LoadOwnFormat( *pMedium ) )
551 bHasName = true;
552 if ( !IsReadOnly() && IsLoadReadonly() )
553 SetReadOnlyUI();
555 bResult = true;
556 OSL_ENSURE( pImpl->m_xDocStorage == xStorage, "Wrong storage is used!" );
560 // now the medium can be disconnected from the storage
561 // the medium is not allowed to dispose the storage so CloseStorage() can be used
562 pMedium->CloseStorage();
564 catch( uno::Exception& )
568 return bResult;
572 bool SfxObjectShell::DoLoad( SfxMedium *pMed )
574 ModifyBlocker_Impl aBlock( this );
576 pMedium = pMed;
577 pMedium->CanDisposeStorage_Impl( true );
579 bool bOk = false;
580 std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter();
581 SfxItemSet* pSet = pMedium->GetItemSet();
582 if( pImpl->nEventId == SfxEventHintId::NONE )
584 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pSet, SID_TEMPLATE, false);
585 SetActivateEvent_Impl(
586 ( pTemplateItem && pTemplateItem->GetValue() )
587 ? SfxEventHintId::CreateDoc : SfxEventHintId::OpenDoc );
590 const SfxStringItem* pBaseItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_BASEURL, false);
591 OUString aBaseURL;
592 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
593 if( pBaseItem )
594 aBaseURL = pBaseItem->GetValue();
595 else
597 if ( pSalvageItem )
599 osl::FileBase::getFileURLFromSystemPath( pMed->GetPhysicalName(), aBaseURL );
601 else
602 aBaseURL = pMed->GetBaseURL();
604 pMed->GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, aBaseURL ) );
606 pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
607 pImpl->bModelInitialized = false;
609 if (pFilter && !pFilter->IsEnabled())
611 SetError( ERRCODE_IO_FILTERDISABLED );
614 if ( pFilter && pFilter->IsExoticFormat() && !QueryAllowExoticFormat_Impl( getInteractionHandler(), aBaseURL, pMed->GetFilter()->GetUIName() ) )
616 SetError( ERRCODE_IO_ABORT );
619 // initialize static language table so language-related extensions are learned before the document loads
620 (void)SvtLanguageTable::GetLanguageEntryCount();
622 //TODO/LATER: make a clear strategy how to handle "UsesStorage" etc.
623 bool bOwnStorageFormat = IsOwnStorageFormat( *pMedium );
624 bool bHasStorage = IsPackageStorageFormat_Impl( *pMedium );
625 if ( pMedium->GetFilter() )
627 ErrCode nError = HandleFilter( pMedium, this );
628 if ( nError != ERRCODE_NONE )
629 SetError(nError);
631 if (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARTPRESENTATION)
632 pSet->Put( SfxBoolItem( SID_DOC_STARTPRESENTATION, true) );
635 EnableSetModified( false );
637 pMedium->LockOrigFileOnDemand( true, false );
638 if ( GetError() == ERRCODE_NONE && bOwnStorageFormat && ( !pFilter || !( pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) ) )
640 uno::Reference< embed::XStorage > xStorage;
641 if ( pMedium->GetError() == ERRCODE_NONE )
642 xStorage = pMedium->GetStorage();
644 if( xStorage.is() && pMedium->GetLastStorageCreationState() == ERRCODE_NONE )
646 DBG_ASSERT( pFilter, "No filter for storage found!" );
650 bool bWarnMediaTypeFallback = false;
651 const SfxBoolItem* pRepairPackageItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_REPAIRPACKAGE, false);
653 // treat the package as broken if the mediatype was retrieved as a fallback
654 uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
655 xStorProps->getPropertyValue("MediaTypeFallbackUsed")
656 >>= bWarnMediaTypeFallback;
658 if ( pRepairPackageItem && pRepairPackageItem->GetValue() )
660 // the macros in repaired documents should be disabled
661 pMedium->GetItemSet()->Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::NEVER_EXECUTE ) );
663 // the mediatype was retrieved by using fallback solution but this is a repairing mode
664 // so it is acceptable to open the document if there is no contents that required manifest.xml
665 bWarnMediaTypeFallback = false;
668 if (bWarnMediaTypeFallback
669 || (!tools::isEmptyFileUrl(pMedium->GetName())
670 && !xStorage->getElementNames().hasElements()))
671 SetError(ERRCODE_IO_BROKENPACKAGE);
673 catch( uno::Exception& )
675 // TODO/LATER: may need error code setting based on exception
676 SetError(ERRCODE_IO_GENERAL);
679 // Load
680 if ( !GetError() )
682 pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
683 pImpl->bModelInitialized = false;
684 bOk = xStorage.is() && LoadOwnFormat( *pMed );
685 if ( bOk )
687 // the document loaded from template has no name
688 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
689 if ( !pTemplateItem || !pTemplateItem->GetValue() )
690 bHasName = true;
692 else
693 SetError(ERRCODE_ABORT);
696 else
697 SetError(pMed->GetLastStorageCreationState());
699 else if ( GetError() == ERRCODE_NONE && InitNew(nullptr) )
701 // set name before ConvertFrom, so that GetSbxObject() already works
702 bHasName = true;
703 SetName( SfxResId(STR_NONAME) );
705 if( !bHasStorage )
706 pMedium->GetInStream();
707 else
708 pMedium->GetStorage();
710 if ( GetError() == ERRCODE_NONE )
712 // Experimental PDF importing using PDFium. This is currently enabled for LOK only and
713 // we handle it not via XmlFilterAdaptor but a new SdPdfFiler.
714 #if !HAVE_FEATURE_POPPLER
715 constexpr bool bUsePdfium = true;
716 #else
717 const bool bUsePdfium
718 = comphelper::LibreOfficeKit::isActive() || getenv("LO_IMPORT_USE_PDFIUM");
719 #endif
720 const bool bPdfiumImport
721 = bUsePdfium && pMedium->GetFilter()
722 && (pMedium->GetFilter()->GetFilterName() == "draw_pdf_import");
724 pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
725 pImpl->bModelInitialized = false;
726 if (pMedium->GetFilter()
727 && (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER)
728 && !bPdfiumImport)
730 uno::Reference < beans::XPropertySet > xSet( GetModel(), uno::UNO_QUERY );
731 const OUString sLockUpdates("LockUpdates");
732 bool bSetProperty = true;
735 xSet->setPropertyValue( sLockUpdates, makeAny( true ) );
737 catch(const beans::UnknownPropertyException& )
739 bSetProperty = false;
741 bOk = ImportFrom(*pMedium, nullptr);
742 if(bSetProperty)
746 xSet->setPropertyValue( sLockUpdates, makeAny( false ) );
748 catch(const beans::UnknownPropertyException& )
751 UpdateLinks();
752 FinishedLoading();
754 else
756 bOk = ConvertFrom(*pMedium);
757 InitOwnModel_Impl();
762 if ( bOk )
764 if ( IsReadOnlyMedium() || IsLoadReadonly() )
765 SetReadOnlyUI();
769 ::ucbhelper::Content aContent( pMedium->GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
770 css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties();
771 if ( xProps.is() )
773 const OUString aAuthor( "Author" );
774 const OUString aKeywords( "Keywords" );
775 const OUString aSubject( "Subject" );
776 Any aAny;
777 OUString aValue;
778 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
779 GetModel(), uno::UNO_QUERY_THROW);
780 uno::Reference<document::XDocumentProperties> xDocProps
781 = xDPS->getDocumentProperties();
782 if ( xProps->hasPropertyByName( aAuthor ) )
784 aAny = aContent.getPropertyValue( aAuthor );
785 if ( aAny >>= aValue )
786 xDocProps->setAuthor(aValue);
788 if ( xProps->hasPropertyByName( aKeywords ) )
790 aAny = aContent.getPropertyValue( aKeywords );
791 if ( aAny >>= aValue )
792 xDocProps->setKeywords(
793 ::comphelper::string::convertCommaSeparated(aValue));
796 if ( xProps->hasPropertyByName( aSubject ) )
798 aAny = aContent.getPropertyValue( aSubject );
799 if ( aAny >>= aValue ) {
800 xDocProps->setSubject(aValue);
805 catch( Exception& )
809 // If not loaded asynchronously call FinishedLoading
810 if ( !( pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) &&
811 ( !pMedium->GetFilter() || pMedium->GetFilter()->UsesStorage() )
813 FinishedLoading( SfxLoadedFlags::MAINDOCUMENT );
815 Broadcast( SfxHint(SfxHintId::NameChanged) );
817 if ( SfxObjectCreateMode::EMBEDDED != eCreateMode )
819 const SfxBoolItem* pAsTempItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
820 const SfxBoolItem* pPreviewItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_PREVIEW, false);
821 const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_HIDDEN, false);
822 if( bOk && !pMedium->GetOrigURL().isEmpty()
823 && !( pAsTempItem && pAsTempItem->GetValue() )
824 && !( pPreviewItem && pPreviewItem->GetValue() )
825 && !( pHiddenItem && pHiddenItem->GetValue() ) )
827 AddToRecentlyUsedList();
831 const SfxBoolItem* pDdeReconnectItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_DDE_RECONNECT_ONLOAD, false);
833 bool bReconnectDde = true; // by default, we try to auto-connect DDE connections.
834 if (pDdeReconnectItem)
835 bReconnectDde = pDdeReconnectItem->GetValue();
837 if (bReconnectDde)
838 ReconnectDdeLinks(*this);
841 return bOk;
844 bool SfxObjectShell::DoLoadExternal( SfxMedium *pMed )
846 pMedium = pMed;
847 return LoadExternal(*pMedium);
850 ErrCode SfxObjectShell::HandleFilter( SfxMedium* pMedium, SfxObjectShell const * pDoc )
852 ErrCode nError = ERRCODE_NONE;
853 SfxItemSet* pSet = pMedium->GetItemSet();
854 const SfxStringItem* pOptions = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_FILE_FILTEROPTIONS, false);
855 const SfxUnoAnyItem* pData = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_FILTER_DATA, false);
856 if ( !pData && !pOptions )
858 css::uno::Reference< XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
859 css::uno::Reference< XNameAccess > xFilterCFG;
860 if( xServiceManager.is() )
862 xFilterCFG.set( xServiceManager->createInstance("com.sun.star.document.FilterFactory"),
863 UNO_QUERY );
866 if( xFilterCFG.is() )
868 try {
869 bool bAbort = false;
870 std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter();
871 Sequence < PropertyValue > aProps;
872 Any aAny = xFilterCFG->getByName( pFilter->GetName() );
873 if ( aAny >>= aProps )
875 auto pProp = std::find_if(aProps.begin(), aProps.end(),
876 [](const PropertyValue& rProp) { return rProp.Name == "UIComponent"; });
877 if (pProp != aProps.end())
879 OUString aServiceName;
880 pProp->Value >>= aServiceName;
881 if( !aServiceName.isEmpty() )
883 css::uno::Reference< XInteractionHandler > rHandler = pMedium->GetInteractionHandler();
884 if( rHandler.is() )
886 // we need some properties in the media descriptor, so we have to make sure that they are in
887 Any aStreamAny;
888 aStreamAny <<= pMedium->GetInputStream();
889 if ( pSet->GetItemState( SID_INPUTSTREAM ) < SfxItemState::SET )
890 pSet->Put( SfxUnoAnyItem( SID_INPUTSTREAM, aStreamAny ) );
891 if ( pSet->GetItemState( SID_FILE_NAME ) < SfxItemState::SET )
892 pSet->Put( SfxStringItem( SID_FILE_NAME, pMedium->GetName() ) );
893 if ( pSet->GetItemState( SID_FILTER_NAME ) < SfxItemState::SET )
894 pSet->Put( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
896 Sequence< PropertyValue > rProperties;
897 TransformItems( SID_OPENDOC, *pSet, rProperties );
898 RequestFilterOptions* pFORequest = new RequestFilterOptions( pDoc->GetModel(), rProperties );
900 css::uno::Reference< XInteractionRequest > rRequest( pFORequest );
901 rHandler->handle( rRequest );
903 if ( !pFORequest->isAbort() )
905 SfxAllItemSet aNewParams( pDoc->GetPool() );
906 TransformParameters( SID_OPENDOC,
907 pFORequest->getFilterOptions(),
908 aNewParams );
910 const SfxStringItem* pFilterOptions = aNewParams.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
911 if ( pFilterOptions )
912 pSet->Put( *pFilterOptions );
914 const SfxUnoAnyItem* pFilterData = aNewParams.GetItem<SfxUnoAnyItem>(SID_FILTER_DATA, false);
915 if ( pFilterData )
916 pSet->Put( *pFilterData );
918 else
919 bAbort = true;
925 if( bAbort )
927 // filter options were not entered
928 nError = ERRCODE_ABORT;
931 catch( NoSuchElementException& )
933 // the filter name is unknown
934 nError = ERRCODE_IO_INVALIDPARAMETER;
936 catch( Exception& )
938 nError = ERRCODE_ABORT;
943 return nError;
947 bool SfxObjectShell::IsOwnStorageFormat(const SfxMedium &rMedium)
949 return !rMedium.GetFilter() || // Embedded
950 ( rMedium.GetFilter()->IsOwnFormat() &&
951 rMedium.GetFilter()->UsesStorage() &&
952 rMedium.GetFilter()->GetVersion() >= SOFFICE_FILEFORMAT_60 );
956 bool SfxObjectShell::IsPackageStorageFormat_Impl(const SfxMedium &rMedium)
958 return !rMedium.GetFilter() || // Embedded
959 ( rMedium.GetFilter()->UsesStorage() &&
960 rMedium.GetFilter()->GetVersion() >= SOFFICE_FILEFORMAT_60 );
964 bool SfxObjectShell::DoSave()
965 // DoSave is only invoked for OLE. Save your own documents in the SFX through
966 // DoSave_Impl order to allow for the creation of backups.
967 // Save in your own format again.
969 bool bOk = false ;
971 ModifyBlocker_Impl aBlock( this );
973 pImpl->bIsSaving = true;
975 if (IsOwnStorageFormat(*GetMedium()))
977 SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013;
978 if (!utl::ConfigManager::IsFuzzing())
980 SvtSaveOptions aSaveOpt;
981 nDefVersion = aSaveOpt.GetODFSaneDefaultVersion();
983 uno::Reference<beans::XPropertySet> const xProps(GetMedium()->GetStorage(), uno::UNO_QUERY);
984 assert(xProps.is());
985 if (nDefVersion >= SvtSaveOptions::ODFSVER_012) // property exists only since ODF 1.2
987 try // tdf#134582 set Version on embedded objects as they
988 { // could have been loaded with a different/old version
989 uno::Reference<frame::XModule> const xModule(GetModel(), uno::UNO_QUERY);
990 bool const isBaseForm(xModule.is() &&
991 xModule->getIdentifier() == "com.sun.star.sdb.FormDesign");
992 SAL_INFO_IF(isBaseForm, "sfx.doc", "tdf#138209 force form export to ODF 1.2");
993 if (!isBaseForm && SvtSaveOptions::ODFSVER_013 <= nDefVersion)
995 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_013_TEXT));
997 else
999 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_012_TEXT));
1002 catch (uno::Exception&)
1004 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::DoSave");
1009 uno::Sequence< beans::NamedValue > aEncryptionData;
1010 if ( IsPackageStorageFormat_Impl( *GetMedium() ) )
1012 if ( GetEncryptionData_Impl( GetMedium()->GetItemSet(), aEncryptionData ) )
1016 //TODO/MBA: GetOutputStorage?! Special mode, because it's "Save"?!
1017 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( GetMedium()->GetStorage(), aEncryptionData );
1018 bOk = true;
1020 catch( uno::Exception& )
1022 SetError(ERRCODE_IO_GENERAL);
1025 DBG_ASSERT( bOk, "The root storage must allow to set common password!\n" );
1027 else
1028 bOk = true;
1029 #if HAVE_FEATURE_SCRIPTING
1030 if ( HasBasic() )
1034 // The basic and dialogs related contents are still not able to proceed with save operation ( saveTo only )
1035 // so since the document storage is locked a workaround has to be used
1037 uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
1038 DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" );
1039 if ( !xTmpStorage.is() )
1040 throw uno::RuntimeException();
1042 const OUString aBasicStorageName( "Basic" );
1043 const OUString aDialogsStorageName( "Dialogs" );
1044 if ( GetMedium()->GetStorage()->hasByName( aBasicStorageName ) )
1045 GetMedium()->GetStorage()->copyElementTo( aBasicStorageName, xTmpStorage, aBasicStorageName );
1046 if ( GetMedium()->GetStorage()->hasByName( aDialogsStorageName ) )
1047 GetMedium()->GetStorage()->copyElementTo( aDialogsStorageName, xTmpStorage, aDialogsStorageName );
1049 GetBasicManager();
1051 // disconnect from the current storage
1052 pImpl->aBasicManager.setStorage( xTmpStorage );
1054 // store to the current storage
1055 pImpl->aBasicManager.storeLibrariesToStorage( GetMedium()->GetStorage() );
1057 // connect to the current storage back
1058 pImpl->aBasicManager.setStorage( GetMedium()->GetStorage() );
1060 catch( uno::Exception& )
1062 SetError(ERRCODE_IO_GENERAL);
1063 bOk = false;
1066 #endif
1069 if (bOk)
1070 bOk = Save();
1072 if (bOk)
1073 bOk = pMedium->Commit();
1076 return bOk;
1079 namespace
1081 class LockUIGuard
1083 public:
1084 LockUIGuard(SfxObjectShell const* pDoc)
1085 : m_pDoc(pDoc)
1087 Lock_Impl();
1089 ~LockUIGuard() { Unlock(); }
1091 void Unlock()
1093 if (m_bUnlock)
1094 Lock_Impl();
1097 private:
1098 void Lock_Impl()
1100 SfxViewFrame* pFrame = SfxViewFrame::GetFirst(m_pDoc);
1101 while (pFrame)
1103 pFrame->GetDispatcher()->Lock(!m_bUnlock);
1104 pFrame->Enable(m_bUnlock);
1105 pFrame = SfxViewFrame::GetNext(*pFrame, m_pDoc);
1107 m_bUnlock = !m_bUnlock;
1109 SfxObjectShell const* m_pDoc;
1110 bool m_bUnlock = false;
1114 static OUString lcl_strip_template(const OUString &aString)
1116 static constexpr OUStringLiteral sPostfix(u"_template");
1117 OUString sRes(aString);
1118 if (sRes.endsWith(sPostfix))
1119 sRes = sRes.copy(0, sRes.getLength() - sPostfix.getLength());
1120 return sRes;
1123 bool SfxObjectShell::SaveTo_Impl
1125 SfxMedium &rMedium, // Medium, in which it will be stored
1126 const SfxItemSet* pSet
1129 /* [Description]
1131 Writes the current contents to the medium rMedium. If the target medium is
1132 no storage, then saving to a temporary storage, or directly if the medium
1133 is transacted, if we ourselves have opened it, and if we are a server
1134 either the container a transacted storage provides or created a
1135 temporary storage by one self.
1139 SAL_INFO( "sfx.doc", "saving \"" << rMedium.GetName() << "\"" );
1141 UpdateDocInfoForSave();
1143 ModifyBlocker_Impl aMod(this);
1144 // tdf#41063, tdf#135244: prevent jumping to cursor at any temporary modification
1145 auto aViewGuard(LockAllViews());
1147 std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
1148 if ( !pFilter )
1150 // if no filter was set, use the default filter
1151 // this should be changed in the feature, it should be an error!
1152 SAL_WARN( "sfx.doc","No filter set!");
1153 pFilter = GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT );
1154 rMedium.SetFilter(pFilter);
1157 bool bStorageBasedSource = IsPackageStorageFormat_Impl( *pMedium );
1158 bool bStorageBasedTarget = IsPackageStorageFormat_Impl( rMedium );
1159 bool bOwnSource = IsOwnStorageFormat( *pMedium );
1160 bool bOwnTarget = IsOwnStorageFormat( rMedium );
1162 // Examine target format to determine whether to query if any password
1163 // protected libraries exceed the size we can handler
1164 if ( bOwnTarget && !QuerySaveSizeExceededModules_Impl( rMedium.GetInteractionHandler() ) )
1166 SetError(ERRCODE_IO_ABORT);
1167 return false;
1170 bool bNeedsDisconnectionOnFail = false;
1172 bool bStoreToSameLocation = false;
1174 // the detection whether the script is changed should be done before saving
1175 bool bTryToPreserveScriptSignature = false;
1176 // no way to detect whether a filter is oasis format, have to wait for saving process
1177 bool bNoPreserveForOasis = false;
1178 if ( bOwnSource && bOwnTarget
1179 && ( pImpl->nScriptingSignatureState == SignatureState::OK
1180 || pImpl->nScriptingSignatureState == SignatureState::NOTVALIDATED
1181 || pImpl->nScriptingSignatureState == SignatureState::INVALID ) )
1183 // the checking of the library modified state iterates over the libraries, should be done only when required
1184 // currently the check is commented out since it is broken, we have to check the signature every time we save
1185 // TODO/LATER: let isAnyContainerModified() work!
1186 bTryToPreserveScriptSignature = true; // !pImpl->pBasicManager->isAnyContainerModified();
1187 if ( bTryToPreserveScriptSignature )
1189 // check that the storage format stays the same
1190 SvtSaveOptions aSaveOpt;
1191 SvtSaveOptions::ODFSaneDefaultVersion nVersion = aSaveOpt.GetODFSaneDefaultVersion();
1193 OUString aODFVersion;
1196 uno::Reference < beans::XPropertySet > xPropSet( GetStorage(), uno::UNO_QUERY_THROW );
1197 xPropSet->getPropertyValue("Version") >>= aODFVersion;
1199 catch( uno::Exception& )
1202 // preserve only if the same filter has been used
1203 // for templates, strip the _template from the filter name for comparison
1204 const OUString aMediumFilter = lcl_strip_template(pMedium->GetFilter()->GetFilterName());
1205 bTryToPreserveScriptSignature = pMedium->GetFilter() && pFilter && aMediumFilter == lcl_strip_template(pFilter->GetFilterName());
1207 // signatures were specified in ODF 1.2 but were used since much longer.
1208 // LO will still correctly validate an old style signature on an ODF 1.2
1209 // document, but technically this is not correct, so this prevents old
1210 // signatures to be copied over to a version 1.2 document
1211 bNoPreserveForOasis = (
1212 (0 <= aODFVersion.compareTo(ODFVER_012_TEXT) && nVersion < SvtSaveOptions::ODFSVER_012) ||
1213 (aODFVersion.isEmpty() && nVersion >= SvtSaveOptions::ODFSVER_012)
1218 // use UCB for case sensitive/insensitive file name comparison
1219 if ( !pMedium->GetName().equalsIgnoreAsciiCase("private:stream")
1220 && !rMedium.GetName().equalsIgnoreAsciiCase("private:stream")
1221 && ::utl::UCBContentHelper::EqualURLs( pMedium->GetName(), rMedium.GetName() ) )
1223 // Do not unlock the file during saving.
1224 // need to modify this for WebDAV if this method is called outside of
1225 // the process of saving a file
1226 pMedium->DisableUnlockWebDAV();
1227 bStoreToSameLocation = true;
1229 if ( pMedium->DocNeedsFileDateCheck() )
1230 rMedium.CheckFileDate( pMedium->GetInitFileDate( false ) );
1232 // before we overwrite the original file, we will make a backup if there is a demand for that
1233 // if the backup is not created here it will be created internally and will be removed in case of successful saving
1234 const bool bDoBackup = SvtSaveOptions().IsBackup();
1235 if ( bDoBackup )
1237 rMedium.DoBackup_Impl();
1238 if ( rMedium.GetError() )
1240 SetError(rMedium.GetErrorCode());
1241 rMedium.ResetError();
1245 if ( bStorageBasedSource && bStorageBasedTarget )
1247 // The active storage must be switched. The simple saving is not enough.
1248 // The problem is that the target medium contains target MediaDescriptor.
1250 // In future the switch of the persistence could be done on stream level:
1251 // a new wrapper service will be implemented that allows to exchange
1252 // persistence on the fly. So the real persistence will be set
1253 // to that stream only after successful commit of the storage.
1254 // TODO/LATER:
1255 // create wrapper stream based on the URL
1256 // create a new storage based on this stream
1257 // store to this new storage
1258 // commit the new storage
1259 // call saveCompleted based with this new storage ( get rid of old storage and "frees" URL )
1260 // commit the wrapper stream ( the stream will connect the URL only on commit, after that it will hold it )
1261 // if the last step is failed the stream should stay to be transacted and should be committed on any flush
1262 // so we can forget the stream in any way and the next storage commit will flush it
1264 bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
1265 *pMedium, rMedium );
1266 if ( bNeedsDisconnectionOnFail
1267 || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
1269 pMedium->CloseAndRelease();
1271 // TODO/LATER: for now the medium must be closed since it can already contain streams from old medium
1272 // in future those streams should not be copied in case a valid target url is provided,
1273 // if the url is not provided ( means the document is based on a stream ) this code is not
1274 // reachable.
1275 rMedium.CloseAndRelease();
1276 rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
1277 rMedium.GetOutputStorage();
1278 rMedium.SetHasEmbeddedObjects(false);
1281 else if ( !bStorageBasedSource && !bStorageBasedTarget )
1283 // the source and the target formats are alien
1284 // just disconnect the stream from the source format
1285 // so that the target medium can use it
1287 pMedium->CloseAndRelease();
1288 rMedium.CloseAndRelease();
1289 rMedium.CreateTempFileNoCopy();
1290 rMedium.GetOutStream();
1292 else if ( !bStorageBasedSource && bStorageBasedTarget )
1294 // the source format is an alien one but the target
1295 // format is an own one so just disconnect the source
1296 // medium
1298 pMedium->CloseAndRelease();
1299 rMedium.CloseAndRelease();
1300 rMedium.GetOutputStorage();
1302 else // means if ( bStorageBasedSource && !bStorageBasedTarget )
1304 // the source format is an own one but the target is
1305 // an alien format, just connect the source to temporary
1306 // storage
1308 bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
1309 *pMedium, rMedium );
1310 if ( bNeedsDisconnectionOnFail
1311 || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
1313 pMedium->CloseAndRelease();
1314 rMedium.CloseAndRelease();
1315 rMedium.CreateTempFileNoCopy();
1316 rMedium.GetOutStream();
1319 pMedium->DisableUnlockWebDAV(false);
1321 else
1323 // This is SaveAs or export action, prepare the target medium
1324 // the alien filters still might write directly to the file, that is of course a bug,
1325 // but for now the framework has to be ready for it
1326 // TODO/LATER: let the medium be prepared for alien formats as well
1328 rMedium.CloseAndRelease();
1329 if ( bStorageBasedTarget )
1331 rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
1332 rMedium.GetOutputStorage();
1333 rMedium.SetHasEmbeddedObjects(false);
1337 // TODO/LATER: error handling
1338 if( rMedium.GetErrorCode() || pMedium->GetErrorCode() || GetErrorCode() )
1340 SAL_WARN("sfx.doc", "SfxObjectShell::SaveTo_Impl: very early error return");
1341 return false;
1344 rMedium.LockOrigFileOnDemand( false, false );
1346 if ( bStorageBasedTarget )
1348 if ( rMedium.GetErrorCode() )
1349 return false;
1351 // If the filter is a "cross export" filter ( f.e. a filter for exporting an impress document from
1352 // a draw document ), the ClassId of the destination storage is different from the ClassId of this
1353 // document. It can be retrieved from the default filter for the desired target format
1354 SotClipboardFormatId nFormat = rMedium.GetFilter()->GetFormat();
1355 SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
1356 std::shared_ptr<const SfxFilter> pFilt = rMatcher.GetFilter4ClipBoardId( nFormat );
1357 if ( pFilt )
1359 if ( pFilt->GetServiceName() != rMedium.GetFilter()->GetServiceName() )
1361 datatransfer::DataFlavor aDataFlavor;
1362 SotExchange::GetFormatDataFlavor( nFormat, aDataFlavor );
1366 uno::Reference< beans::XPropertySet > xProps( rMedium.GetStorage(), uno::UNO_QUERY_THROW );
1367 xProps->setPropertyValue("MediaType",
1368 uno::makeAny( aDataFlavor.MimeType ) );
1370 catch( uno::Exception& )
1377 // TODO/LATER: error handling
1378 if( rMedium.GetErrorCode() || pMedium->GetErrorCode() || GetErrorCode() )
1379 return false;
1381 bool bOldStat = pImpl->bForbidReload;
1382 pImpl->bForbidReload = true;
1384 // lock user interface while saving the document
1385 LockUIGuard aLockUIGuard(this);
1387 bool bCopyTo = false;
1388 SfxItemSet *pMedSet = rMedium.GetItemSet();
1389 if( pMedSet )
1391 const SfxBoolItem* pSaveToItem = SfxItemSet::GetItem<SfxBoolItem>(pMedSet, SID_SAVETO, false);
1392 bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED ||
1393 (pSaveToItem && pSaveToItem->GetValue());
1396 bool bOk = false;
1397 // TODO/LATER: get rid of bOk
1398 if (bOwnTarget && pFilter && !(pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER))
1400 uno::Reference< embed::XStorage > xMedStorage = rMedium.GetStorage();
1401 if ( !xMedStorage.is() )
1403 // no saving without storage
1404 pImpl->bForbidReload = bOldStat;
1405 return false;
1408 // transfer password from the parameters to the storage
1409 uno::Sequence< beans::NamedValue > aEncryptionData;
1410 bool bPasswdProvided = false;
1411 if ( GetEncryptionData_Impl( rMedium.GetItemSet(), aEncryptionData ) )
1413 bPasswdProvided = true;
1414 try {
1415 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );
1416 bOk = true;
1418 catch( uno::Exception& )
1420 SAL_WARN( "sfx.doc", "Setting of common encryption key failed!" );
1421 SetError(ERRCODE_IO_GENERAL);
1424 else
1425 bOk = true;
1427 pFilter = rMedium.GetFilter();
1429 const SfxStringItem *pVersionItem = !rMedium.IsInCheckIn()? SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_COMMENTS, false): nullptr;
1430 OUString aTmpVersionURL;
1432 if ( bOk )
1434 bOk = false;
1435 // currently the case that the storage is the same should be impossible
1436 if ( xMedStorage == GetStorage() )
1438 OSL_ENSURE( !pVersionItem, "This scenario is impossible currently!" );
1439 // usual save procedure
1440 bOk = Save();
1442 else
1444 // save to target
1445 bOk = SaveAsOwnFormat( rMedium );
1446 if ( bOk && pVersionItem )
1448 aTmpVersionURL = CreateTempCopyOfStorage_Impl( xMedStorage );
1449 bOk = !aTmpVersionURL.isEmpty();
1454 //fdo#61320: only store thumbnail image if the corresponding option is enabled in the configuration
1455 if ( bOk && officecfg::Office::Common::Save::Document::GenerateThumbnail::get()
1456 && GetCreateMode() != SfxObjectCreateMode::EMBEDDED && !bPasswdProvided && IsUseThumbnailSave() )
1458 // store the thumbnail representation image
1459 // the thumbnail is not stored in case of encrypted document
1460 if ( !GenerateAndStoreThumbnail( bPasswdProvided, xMedStorage ) )
1462 // TODO: error handling
1463 SAL_WARN( "sfx.doc", "Couldn't store thumbnail representation!" );
1467 if ( bOk )
1469 if ( pImpl->bIsSaving || pImpl->bPreserveVersions )
1473 const Sequence < util::RevisionTag > aVersions = rMedium.GetVersionList();
1474 if ( aVersions.hasElements() )
1476 // copy the version streams
1477 const OUString aVersionsName( "Versions" );
1478 uno::Reference< embed::XStorage > xNewVerStor = xMedStorage->openStorageElement(
1479 aVersionsName,
1480 embed::ElementModes::READWRITE );
1481 uno::Reference< embed::XStorage > xOldVerStor = GetStorage()->openStorageElement(
1482 aVersionsName,
1483 embed::ElementModes::READ );
1484 if ( !xNewVerStor.is() || !xOldVerStor.is() )
1485 throw uno::RuntimeException();
1487 for ( const auto& rVersion : aVersions )
1489 if ( xOldVerStor->hasByName( rVersion.Identifier ) )
1490 xOldVerStor->copyElementTo( rVersion.Identifier, xNewVerStor, rVersion.Identifier );
1493 uno::Reference< embed::XTransactedObject > xTransact( xNewVerStor, uno::UNO_QUERY );
1494 if ( xTransact.is() )
1495 xTransact->commit();
1498 catch( uno::Exception& )
1500 SAL_WARN( "sfx.doc", "Couldn't copy versions!" );
1501 bOk = false;
1502 // TODO/LATER: a specific error could be set
1506 if ( bOk && pVersionItem && !rMedium.IsInCheckIn() )
1508 // store a version also
1509 const SfxStringItem *pAuthorItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_AUTHOR, false);
1511 // version comment
1512 util::RevisionTag aInfo;
1513 aInfo.Comment = pVersionItem->GetValue();
1515 // version author
1516 if ( pAuthorItem )
1517 aInfo.Author = pAuthorItem->GetValue();
1518 else
1519 // if not transferred as a parameter, get it from user settings
1520 aInfo.Author = SvtUserOptions().GetFullName();
1522 DateTime aTime( DateTime::SYSTEM );
1523 aInfo.TimeStamp.Day = aTime.GetDay();
1524 aInfo.TimeStamp.Month = aTime.GetMonth();
1525 aInfo.TimeStamp.Year = aTime.GetYear();
1526 aInfo.TimeStamp.Hours = aTime.GetHour();
1527 aInfo.TimeStamp.Minutes = aTime.GetMin();
1528 aInfo.TimeStamp.Seconds = aTime.GetSec();
1530 // add new version information into the versionlist and save the versionlist
1531 // the version list must have been transferred from the "old" medium before
1532 rMedium.AddVersion_Impl(aInfo);
1533 rMedium.SaveVersionList_Impl();
1534 bOk = PutURLContentsToVersionStream_Impl(aTmpVersionURL, xMedStorage,
1535 aInfo.Identifier);
1537 else if ( bOk && ( pImpl->bIsSaving || pImpl->bPreserveVersions ) )
1539 rMedium.SaveVersionList_Impl();
1543 if ( !aTmpVersionURL.isEmpty() )
1544 ::utl::UCBContentHelper::Kill( aTmpVersionURL );
1546 else
1548 // it's a "SaveAs" in an alien format
1549 if ( rMedium.GetFilter() && ( rMedium.GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) )
1550 bOk = ExportTo( rMedium );
1551 else
1552 bOk = ConvertTo( rMedium );
1554 // after saving the document, the temporary object storage must be updated
1555 // if the old object storage was not a temporary one, it will be updated also, because it will be used
1556 // as a source for copying the objects into the new temporary storage that will be created below
1557 // updating means: all child objects must be stored into it
1558 // ( same as on loading, where these objects are copied to the temporary storage )
1559 // but don't commit these changes, because in the case when the old object storage is not a temporary one,
1560 // all changes will be written into the original file !
1562 if( bOk && !bCopyTo )
1563 // we also don't touch any graphical replacements here
1564 SaveChildren( true );
1567 if ( bOk )
1569 // if ODF version of oasis format changes on saving the signature should not be preserved
1570 if ( bTryToPreserveScriptSignature && bNoPreserveForOasis )
1571 bTryToPreserveScriptSignature = ( SotStorage::GetVersion( rMedium.GetStorage() ) == SOFFICE_FILEFORMAT_60 );
1573 uno::Reference< security::XDocumentDigitalSignatures > xDDSigns;
1574 if (bTryToPreserveScriptSignature)
1576 // if the scripting code was not changed and it is signed the signature should be preserved
1577 // unfortunately at this point we have only information whether the basic code has changed or not
1578 // so the only way is to check the signature if the basic was not changed
1581 // get the ODF version of the new medium
1582 OUString aVersion;
1585 uno::Reference < beans::XPropertySet > xPropSet( rMedium.GetStorage(), uno::UNO_QUERY_THROW );
1586 xPropSet->getPropertyValue("Version") >>= aVersion;
1588 catch( uno::Exception& )
1592 xDDSigns = security::DocumentDigitalSignatures::createWithVersion(comphelper::getProcessComponentContext(), aVersion);
1594 const OUString aScriptSignName = xDDSigns->getScriptingContentSignatureDefaultStreamName();
1596 if ( !aScriptSignName.isEmpty() )
1598 // target medium is still not committed, it should not be closed
1599 // commit the package storage and close it, but leave the streams open
1600 rMedium.StorageCommit_Impl();
1601 rMedium.CloseStorage();
1603 uno::Reference< embed::XStorage > xReadOrig = pMedium->GetZipStorageToSign_Impl();
1604 if ( !xReadOrig.is() )
1605 throw uno::RuntimeException();
1606 uno::Reference< embed::XStorage > xMetaInf = xReadOrig->openStorageElement(
1607 "META-INF",
1608 embed::ElementModes::READ );
1610 uno::Reference< embed::XStorage > xTarget = rMedium.GetZipStorageToSign_Impl( false );
1611 if ( !xTarget.is() )
1612 throw uno::RuntimeException();
1613 uno::Reference< embed::XStorage > xTargetMetaInf = xTarget->openStorageElement(
1614 "META-INF",
1615 embed::ElementModes::READWRITE );
1617 if ( xMetaInf.is() && xTargetMetaInf.is() )
1619 xMetaInf->copyElementTo( aScriptSignName, xTargetMetaInf, aScriptSignName );
1621 uno::Reference< embed::XTransactedObject > xTransact( xTargetMetaInf, uno::UNO_QUERY );
1622 if ( xTransact.is() )
1623 xTransact->commit();
1625 xTargetMetaInf->dispose();
1627 // now check the copied signature
1628 uno::Sequence< security::DocumentSignatureInformation > aInfos =
1629 xDDSigns->verifyScriptingContentSignatures( xTarget,
1630 uno::Reference< io::XInputStream >() );
1631 SignatureState nState = DocumentSignatures::getSignatureState(aInfos);
1632 if ( nState == SignatureState::OK || nState == SignatureState::NOTVALIDATED
1633 || nState == SignatureState::PARTIAL_OK)
1635 rMedium.SetCachedSignatureState_Impl( nState );
1637 // commit the ZipStorage from target medium
1638 xTransact.set( xTarget, uno::UNO_QUERY );
1639 if ( xTransact.is() )
1640 xTransact->commit();
1642 else
1644 // it should not happen, the copies signature is invalid!
1645 // throw the changes away
1646 SAL_WARN( "sfx.doc", "An invalid signature was copied!" );
1651 catch( uno::Exception& )
1655 rMedium.CloseZipStorage_Impl();
1658 const OUString sName( rMedium.GetName( ) );
1659 bOk = rMedium.Commit();
1660 const OUString sNewName( rMedium.GetName( ) );
1662 if ( sName != sNewName )
1663 GetMedium( )->SwitchDocumentToFile( sNewName );
1665 if ( bOk )
1667 // if the target medium is an alien format and the "old" medium was an own format and the "old" medium
1668 // has a name, the object storage must be exchanged, because now we need a new temporary storage
1669 // as object storage
1670 if ( !bCopyTo && bStorageBasedSource && !bStorageBasedTarget )
1672 if ( bStoreToSameLocation )
1674 // if the old medium already disconnected from document storage, the storage still must
1675 // be switched if backup file is used
1676 if ( bNeedsDisconnectionOnFail )
1677 ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
1679 else if (!pMedium->GetName().isEmpty()
1680 || ( pMedium->HasStorage_Impl() && pMedium->WillDisposeStorageOnClose_Impl() ) )
1682 OSL_ENSURE(!pMedium->GetName().isEmpty(), "Fallback is used, the medium without name should not dispose the storage!");
1683 // copy storage of old medium to new temporary storage and take this over
1684 if( !ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
1686 SAL_WARN( "sfx.doc", "Process after storing has failed." );
1687 bOk = false;
1692 else
1694 SAL_WARN( "sfx.doc", "Storing has failed." );
1696 // in case the document storage was connected to backup temporarily it must be disconnected now
1697 if ( bNeedsDisconnectionOnFail )
1698 ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
1702 // unlock user interface
1703 aLockUIGuard.Unlock();
1704 pImpl->bForbidReload = bOldStat;
1706 if ( bOk )
1710 ::ucbhelper::Content aContent( rMedium.GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
1711 css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties();
1712 if ( xProps.is() )
1714 const OUString aAuthor( "Author" );
1715 const OUString aKeywords( "Keywords" );
1716 const OUString aSubject( "Subject" );
1718 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1719 GetModel(), uno::UNO_QUERY_THROW);
1720 uno::Reference<document::XDocumentProperties> xDocProps
1721 = xDPS->getDocumentProperties();
1723 if ( xProps->hasPropertyByName( aAuthor ) )
1725 aContent.setPropertyValue( aAuthor, Any(xDocProps->getAuthor()) );
1727 if ( xProps->hasPropertyByName( aKeywords ) )
1729 Any aAny;
1730 aAny <<= ::comphelper::string::convertCommaSeparated(
1731 xDocProps->getKeywords());
1732 aContent.setPropertyValue( aKeywords, aAny );
1734 if ( xProps->hasPropertyByName( aSubject ) )
1736 aContent.setPropertyValue( aSubject, Any(xDocProps->getSubject()) );
1740 catch( Exception& )
1745 return bOk;
1748 bool SfxObjectShell::DisconnectStorage_Impl( SfxMedium& rSrcMedium, SfxMedium& rTargetMedium )
1750 // this method disconnects the storage from source medium, and attaches it to the backup created by the target medium
1752 uno::Reference< embed::XStorage > xStorage = rSrcMedium.GetStorage();
1754 bool bResult = false;
1755 if ( xStorage == pImpl->m_xDocStorage )
1759 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
1760 const OUString aBackupURL = rTargetMedium.GetBackup_Impl();
1761 if ( aBackupURL.isEmpty() )
1763 // the backup could not be created, try to disconnect the storage and close the source SfxMedium
1764 // in this case the optimization is not possible, connect storage to a temporary file
1765 rTargetMedium.ResetError();
1766 xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
1767 rSrcMedium.CanDisposeStorage_Impl( false );
1768 rSrcMedium.Close();
1770 // now try to create the backup
1771 rTargetMedium.GetBackup_Impl();
1773 else
1775 // the following call will only compare stream sizes
1776 // TODO/LATER: this is a very risky part, since if the URL contents are different from the storage
1777 // contents, the storage will be broken
1778 xOptStorage->attachToURL( aBackupURL, true );
1780 // the storage is successfully attached to backup, thus it is owned by the document not by the medium
1781 rSrcMedium.CanDisposeStorage_Impl( false );
1782 bResult = true;
1785 catch ( uno::Exception& )
1788 return bResult;
1792 bool SfxObjectShell::ConnectTmpStorage_Impl(
1793 const uno::Reference< embed::XStorage >& xStorage,
1794 SfxMedium* pMediumArg )
1796 /* [Description]
1798 If the application operates on a temporary storage, then it may not take
1799 the temporary storage from the SaveCompleted. Therefore the new storage
1800 is connected already here in this case and SaveCompleted then does nothing.
1804 bool bResult = false;
1806 if ( xStorage.is() )
1810 // the empty argument means that the storage will create temporary stream itself
1811 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
1812 xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
1814 // the storage is successfully disconnected from the original sources, thus the medium must not dispose it
1815 if ( pMediumArg )
1816 pMediumArg->CanDisposeStorage_Impl( false );
1818 bResult = true;
1820 catch( uno::Exception& )
1824 // if switching of the storage does not work for any reason ( nonroot storage for example ) use the old method
1825 if ( !bResult ) try
1827 uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
1829 DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" );
1830 if ( !xTmpStorage.is() )
1831 throw uno::RuntimeException();
1833 // TODO/LATER: may be it should be done in SwitchPersistence also
1834 // TODO/LATER: find faster way to copy storage; perhaps sharing with backup?!
1835 xStorage->copyToStorage( xTmpStorage );
1836 bResult = SaveCompleted( xTmpStorage );
1838 if ( bResult )
1840 pImpl->aBasicManager.setStorage( xTmpStorage );
1842 // Get rid of this workaround after issue i113914 is fixed
1845 uno::Reference< script::XStorageBasedLibraryContainer > xBasicLibraries( pImpl->xBasicLibraries, uno::UNO_QUERY_THROW );
1846 xBasicLibraries->setRootStorage( xTmpStorage );
1848 catch( uno::Exception& )
1852 uno::Reference< script::XStorageBasedLibraryContainer > xDialogLibraries( pImpl->xDialogLibraries, uno::UNO_QUERY_THROW );
1853 xDialogLibraries->setRootStorage( xTmpStorage );
1855 catch( uno::Exception& )
1859 catch( uno::Exception& )
1862 if ( !bResult )
1864 // TODO/LATER: may need error code setting based on exception
1865 SetError(ERRCODE_IO_GENERAL);
1868 else if (!GetMedium()->GetFilter()->IsOwnFormat())
1869 bResult = true;
1871 return bResult;
1875 bool SfxObjectShell::DoSaveObjectAs( SfxMedium& rMedium, bool bCommit )
1877 bool bOk = false;
1879 ModifyBlocker_Impl aBlock( this );
1881 uno::Reference < embed::XStorage > xNewStor = rMedium.GetStorage();
1882 if ( !xNewStor.is() )
1883 return false;
1885 uno::Reference < beans::XPropertySet > xPropSet( xNewStor, uno::UNO_QUERY );
1886 if ( !xPropSet.is() )
1887 return false;
1889 Any a = xPropSet->getPropertyValue("MediaType");
1890 OUString aMediaType;
1891 if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
1893 SAL_WARN( "sfx.doc", "The mediatype must be set already!" );
1894 SetupStorage( xNewStor, SOFFICE_FILEFORMAT_CURRENT, false );
1897 pImpl->bIsSaving = false;
1898 bOk = SaveAsOwnFormat( rMedium );
1900 if ( bCommit )
1902 try {
1903 uno::Reference< embed::XTransactedObject > xTransact( xNewStor, uno::UNO_QUERY_THROW );
1904 xTransact->commit();
1906 catch( uno::Exception& )
1908 SAL_WARN( "sfx.doc", "The storage was not committed on DoSaveAs!" );
1912 return bOk;
1916 // TODO/LATER: may be the call must be removed completely
1917 bool SfxObjectShell::DoSaveAs( SfxMedium& rMedium )
1919 // here only root storages are included, which are stored via temp file
1920 rMedium.CreateTempFileNoCopy();
1921 SetError(rMedium.GetErrorCode());
1922 if ( GetError() )
1923 return false;
1925 // copy version list from "old" medium to target medium, so it can be used on saving
1926 if ( pImpl->bPreserveVersions )
1927 rMedium.TransferVersionList_Impl( *pMedium );
1929 bool bRet = SaveTo_Impl( rMedium, nullptr );
1930 if ( !bRet )
1931 SetError(rMedium.GetErrorCode());
1932 return bRet;
1936 bool SfxObjectShell::DoSaveCompleted( SfxMedium* pNewMed, bool bRegisterRecent )
1938 bool bOk = true;
1939 bool bMedChanged = pNewMed && pNewMed!=pMedium;
1941 DBG_ASSERT( !pNewMed || pNewMed->GetError() == ERRCODE_NONE, "DoSaveCompleted: Medium has error!" );
1943 // delete Medium (and Storage!) after all notifications
1944 SfxMedium* pOld = pMedium;
1945 if ( bMedChanged )
1947 pMedium = pNewMed;
1948 pMedium->CanDisposeStorage_Impl( true );
1951 std::shared_ptr<const SfxFilter> pFilter = pMedium ? pMedium->GetFilter() : nullptr;
1952 if ( pNewMed )
1954 if( bMedChanged )
1956 if (!pNewMed->GetName().isEmpty())
1957 bHasName = true;
1958 Broadcast( SfxHint(SfxHintId::NameChanged) );
1959 EnableSetModified(false);
1960 getDocProperties()->setGenerator(
1961 ::utl::DocInfoHelper::GetGeneratorString() );
1962 EnableSetModified();
1965 uno::Reference< embed::XStorage > xStorage;
1966 if ( !pFilter || IsPackageStorageFormat_Impl( *pMedium ) )
1968 uno::Reference < embed::XStorage > xOld = GetStorage();
1970 // when the package based medium is broken and has no storage or if the storage
1971 // is the same as the document storage the current document storage should be preserved
1972 xStorage = pMedium->GetStorage();
1973 bOk = SaveCompleted( xStorage );
1974 if ( bOk && xStorage.is() && xOld != xStorage
1975 && (!pOld || !pOld->HasStorage_Impl() || xOld != pOld->GetStorage() ) )
1977 // old own storage was not controlled by old Medium -> dispose it
1978 try {
1979 xOld->dispose();
1980 } catch( uno::Exception& )
1982 // the storage is disposed already
1983 // can happen during reload scenario when the medium has
1984 // disposed it during the closing
1985 // will be fixed in one of the next milestones
1989 else
1991 if (pImpl->m_bSavingForSigning && pFilter && pFilter->GetSupportsSigning())
1992 // So that pMedium->pImpl->xStream becomes a non-empty
1993 // reference, and at the end we attempt locking again in
1994 // SfxMedium::LockOrigFileOnDemand().
1995 pMedium->GetMedium_Impl();
1997 if( pMedium->GetOpenMode() & StreamMode::WRITE )
1998 pMedium->GetInStream();
1999 xStorage = GetStorage();
2002 // TODO/LATER: may be this code will be replaced, but not sure
2003 // Set storage in document library containers
2004 pImpl->aBasicManager.setStorage( xStorage );
2006 // Get rid of this workaround after issue i113914 is fixed
2009 uno::Reference< script::XStorageBasedLibraryContainer > xBasicLibraries( pImpl->xBasicLibraries, uno::UNO_QUERY_THROW );
2010 xBasicLibraries->setRootStorage( xStorage );
2012 catch( uno::Exception& )
2016 uno::Reference< script::XStorageBasedLibraryContainer > xDialogLibraries( pImpl->xDialogLibraries, uno::UNO_QUERY_THROW );
2017 xDialogLibraries->setRootStorage( xStorage );
2019 catch( uno::Exception& )
2022 else
2024 if( pMedium )
2026 if( pFilter && !IsPackageStorageFormat_Impl( *pMedium ) && (pMedium->GetOpenMode() & StreamMode::WRITE ))
2028 pMedium->ReOpen();
2029 bOk = SaveCompletedChildren();
2031 else
2032 bOk = SaveCompleted( nullptr );
2034 // either Save or ConvertTo
2035 else
2036 bOk = SaveCompleted( nullptr );
2039 if ( bOk && pNewMed )
2041 if( bMedChanged )
2043 delete pOld;
2045 uno::Reference< frame::XModel > xModel = GetModel();
2046 if ( xModel.is() )
2048 const OUString& aURL {pNewMed->GetOrigURL()};
2049 uno::Sequence< beans::PropertyValue > aMediaDescr;
2050 TransformItems( SID_OPENDOC, *pNewMed->GetItemSet(), aMediaDescr );
2053 xModel->attachResource( aURL, aMediaDescr );
2055 catch( uno::Exception& )
2059 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
2060 bool bTemplate = pTemplateItem && pTemplateItem->GetValue();
2062 // before the title regenerated the document must lose the signatures
2063 pImpl->nDocumentSignatureState = SignatureState::NOSIGNATURES;
2064 if (!bTemplate)
2066 pImpl->nScriptingSignatureState = pNewMed->GetCachedSignatureState_Impl();
2067 OSL_ENSURE( pImpl->nScriptingSignatureState != SignatureState::BROKEN, "The signature must not be broken at this place" );
2069 // TODO/LATER: in future the medium must control own signature state, not the document
2070 pNewMed->SetCachedSignatureState_Impl( SignatureState::NOSIGNATURES ); // set the default value back
2072 else
2073 pNewMed->SetCachedSignatureState_Impl( pImpl->nScriptingSignatureState );
2075 // Set new title
2076 if (!pNewMed->GetName().isEmpty() && SfxObjectCreateMode::EMBEDDED != eCreateMode)
2077 InvalidateName();
2078 SetModified(false); // reset only by set medium
2079 Broadcast( SfxHint(SfxHintId::ModeChanged) );
2081 // this is the end of the saving process, it is possible that
2082 // the file was changed
2083 // between medium commit and this step (attributes change and so on)
2084 // so get the file date again
2085 if ( pNewMed->DocNeedsFileDateCheck() )
2086 pNewMed->GetInitFileDate( true );
2090 pMedium->ClearBackup_Impl();
2091 pMedium->LockOrigFileOnDemand( true, false );
2093 if (bRegisterRecent)
2094 AddToRecentlyUsedList();
2096 return bOk;
2099 void SfxObjectShell::AddToRecentlyUsedList()
2101 INetURLObject aUrl( pMedium->GetOrigURL() );
2103 if ( aUrl.GetProtocol() == INetProtocol::File )
2105 std::shared_ptr<const SfxFilter> pOrgFilter = pMedium->GetFilter();
2106 Application::AddToRecentDocumentList( aUrl.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
2107 pOrgFilter ? pOrgFilter->GetMimeType() : OUString(),
2108 pOrgFilter ? pOrgFilter->GetServiceName() : OUString() );
2113 bool SfxObjectShell::ConvertFrom
2115 SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the source file
2116 (for example file name, <SfxFilter>,
2117 Open-Modi and so on) */
2120 /* [Description]
2122 This method is called for loading of documents over all filters which are
2123 not SfxFilterFlags::OWN or for which no clipboard format has been registered
2124 (thus no storage format that is used). In other words, with this method
2125 it is imported.
2127 Files which are to be opened here should be opened through 'rMedium'
2128 to guarantee the right open modes. Especially if the format is retained
2129 (only possible with SfxFilterFlags::SIMULATE or SfxFilterFlags::OWN) file which must
2130 be opened STREAM_SHARE_DENYWRITE.
2132 [Return value]
2134 bool true
2135 The document could be loaded.
2137 false
2138 The document could not be loaded, an error code
2139 received through <SvMedium::GetError()const>
2141 [Example]
2143 bool DocSh::ConvertFrom( SfxMedium &rMedium )
2145 SvStreamRef xStream = rMedium.GetInStream();
2146 if( xStream.is() )
2148 xStream->SetBufferSize(4096);
2149 *xStream >> ...;
2151 // Do not call 'rMedium.CloseInStream()'! Keep File locked!
2152 return ERRCODE_NONE == rMedium.GetError();
2155 return false;
2158 [Cross-references]
2160 <SfxObjectShell::ConvertTo(SfxMedium&)>
2161 <SfxFilterFlags::REGISTRATION>
2164 return false;
2167 bool SfxObjectShell::ImportFrom(SfxMedium& rMedium,
2168 css::uno::Reference<css::text::XTextRange> const& xInsertPosition)
2170 const OUString aFilterName( rMedium.GetFilter()->GetFilterName() );
2172 uno::Reference< lang::XMultiServiceFactory > xMan = ::comphelper::getProcessServiceFactory();
2173 uno::Reference < lang::XMultiServiceFactory > xFilterFact (
2174 xMan->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
2176 uno::Sequence < beans::PropertyValue > aProps;
2177 uno::Reference < container::XNameAccess > xFilters ( xFilterFact, uno::UNO_QUERY );
2178 if ( xFilters->hasByName( aFilterName ) )
2180 xFilters->getByName( aFilterName ) >>= aProps;
2181 rMedium.GetItemSet()->Put( SfxStringItem( SID_FILTER_NAME, aFilterName ) );
2184 OUString aFilterImplName;
2185 auto pProp = std::find_if(aProps.begin(), aProps.end(),
2186 [](const beans::PropertyValue& rFilterProp) { return rFilterProp.Name == "FilterService"; });
2187 if (pProp != aProps.end())
2188 pProp->Value >>= aFilterImplName;
2190 uno::Reference< document::XFilter > xLoader;
2191 if ( !aFilterImplName.isEmpty() )
2195 xLoader.set( xFilterFact->createInstanceWithArguments( aFilterName, uno::Sequence < uno::Any >() ), uno::UNO_QUERY );
2197 catch(const uno::Exception&)
2199 xLoader.clear();
2202 if ( xLoader.is() )
2204 // it happens that xLoader does not support xImporter!
2207 uno::Reference< lang::XComponent > xComp( GetModel(), uno::UNO_QUERY_THROW );
2208 uno::Reference< document::XImporter > xImporter( xLoader, uno::UNO_QUERY_THROW );
2209 xImporter->setTargetDocument( xComp );
2211 uno::Sequence < beans::PropertyValue > lDescriptor;
2212 rMedium.GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, rMedium.GetName() ) );
2213 TransformItems( SID_OPENDOC, *rMedium.GetItemSet(), lDescriptor );
2215 css::uno::Sequence < css::beans::PropertyValue > aArgs ( lDescriptor.getLength() );
2216 css::beans::PropertyValue * pNewValue = aArgs.getArray();
2217 const css::beans::PropertyValue * pOldValue = lDescriptor.getConstArray();
2218 const OUString sInputStream ( "InputStream" );
2220 bool bHasInputStream = false;
2221 bool bHasBaseURL = false;
2222 sal_Int32 nEnd = lDescriptor.getLength();
2224 for ( sal_Int32 i = 0; i < nEnd; i++ )
2226 pNewValue[i] = pOldValue[i];
2227 if ( pOldValue [i].Name == sInputStream )
2228 bHasInputStream = true;
2229 else if ( pOldValue[i].Name == "DocumentBaseURL" )
2230 bHasBaseURL = true;
2233 if ( !bHasInputStream )
2235 aArgs.realloc ( ++nEnd );
2236 aArgs[nEnd-1].Name = sInputStream;
2237 aArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XInputStream > ( new utl::OSeekableInputStreamWrapper ( *rMedium.GetInStream() ) );
2240 if ( !bHasBaseURL )
2242 aArgs.realloc ( ++nEnd );
2243 aArgs[nEnd-1].Name = "DocumentBaseURL";
2244 aArgs[nEnd-1].Value <<= rMedium.GetBaseURL();
2247 if (xInsertPosition.is()) {
2248 aArgs.realloc( ++nEnd );
2249 aArgs[nEnd-1].Name = "InsertMode";
2250 aArgs[nEnd-1].Value <<= true;
2251 aArgs.realloc( ++nEnd );
2252 aArgs[nEnd-1].Name = "TextInsertModeRange";
2253 aArgs[nEnd-1].Value <<= xInsertPosition;
2256 // #i119492# During loading, some OLE objects like chart will be set
2257 // modified flag, so needs to reset the flag to false after loading
2258 bool bRtn = true;
2259 if (!tools::isEmptyFileUrl(rMedium.GetName()))
2261 bRtn = xLoader->filter(aArgs);
2263 const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
2264 for ( const auto& rName : aNames )
2266 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
2267 OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
2268 if ( xObj.is() )
2270 sal_Int32 nState = xObj->getCurrentState();
2271 if ( nState == embed::EmbedStates::LOADED || nState == embed::EmbedStates::RUNNING ) // means that the object is not active
2273 uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
2274 if (xModifiable.is() && xModifiable->isModified())
2276 uno::Reference<embed::XEmbedPersist> const xPers(xObj, uno::UNO_QUERY);
2277 assert(xPers.is() && "Modified object without persistence!");
2278 // store it before resetting modified!
2279 xPers->storeOwn();
2280 xModifiable->setModified(false);
2286 // tdf#107690 import custom document property _MarkAsFinal as SecurityOptOpenReadonly
2287 // (before this fix, LibreOffice opened read-only OOXML documents as editable,
2288 // also saved and exported _MarkAsFinal=true silently, resulting unintended read-only
2289 // warning info bar in MSO)
2290 uno::Reference< document::XDocumentPropertiesSupplier > xPropSupplier(GetModel(), uno::UNO_QUERY_THROW);
2291 uno::Reference<document::XDocumentProperties> xDocProps = xPropSupplier->getDocumentProperties() ;
2292 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocProps->getUserDefinedProperties();
2293 if (xPropertyContainer.is())
2295 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
2296 if (xPropertySet.is())
2298 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
2299 if (xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName("_MarkAsFinal"))
2301 if (xPropertySet->getPropertyValue("_MarkAsFinal").get<bool>())
2303 uno::Reference< lang::XMultiServiceFactory > xFactory(GetModel(), uno::UNO_QUERY);
2304 uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
2305 xSettings->setPropertyValue("LoadReadonly", uno::makeAny(true));
2307 xPropertyContainer->removeProperty("_MarkAsFinal");
2312 return bRtn;
2314 catch (const packages::zip::ZipIOException&)
2316 SetError(ERRCODE_IO_BROKENPACKAGE);
2318 catch (const lang::WrappedTargetRuntimeException& rWrapped)
2320 io::WrongFormatException e;
2321 if (rWrapped.TargetException >>= e)
2323 SetError(*new StringErrorInfo(ERRCODE_SFX_FORMAT_ROWCOL,
2324 e.Message, DialogMask::ButtonsOk | DialogMask::MessageError ));
2327 catch (const css::io::IOException& e)
2329 SetError(*new StringErrorInfo(ERRCODE_SFX_FORMAT_ROWCOL,
2330 e.Message, DialogMask::ButtonsOk | DialogMask::MessageError ));
2332 catch (const std::exception& e)
2334 const char *msg = e.what();
2335 const OUString sError(msg, strlen(msg), RTL_TEXTENCODING_ASCII_US);
2336 SetError(*new StringErrorInfo(ERRCODE_SFX_DOLOADFAILED,
2337 sError, DialogMask::ButtonsOk | DialogMask::MessageError));
2339 catch (...)
2341 std::abort(); // cannot happen
2345 return false;
2348 bool SfxObjectShell::ExportTo( SfxMedium& rMedium )
2350 const OUString aFilterName( rMedium.GetFilter()->GetFilterName() );
2351 uno::Reference< document::XExporter > xExporter;
2354 uno::Reference< lang::XMultiServiceFactory > xMan = ::comphelper::getProcessServiceFactory();
2355 uno::Reference < lang::XMultiServiceFactory > xFilterFact (
2356 xMan->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
2358 uno::Sequence < beans::PropertyValue > aProps;
2359 uno::Reference < container::XNameAccess > xFilters ( xFilterFact, uno::UNO_QUERY );
2360 if ( xFilters->hasByName( aFilterName ) )
2361 xFilters->getByName( aFilterName ) >>= aProps;
2363 OUString aFilterImplName;
2364 auto pProp = std::find_if(aProps.begin(), aProps.end(),
2365 [](const beans::PropertyValue& rFilterProp) { return rFilterProp.Name == "FilterService"; });
2366 if (pProp != aProps.end())
2367 pProp->Value >>= aFilterImplName;
2369 if ( !aFilterImplName.isEmpty() )
2373 xExporter.set( xFilterFact->createInstanceWithArguments( aFilterName, uno::Sequence < uno::Any >() ), uno::UNO_QUERY );
2375 catch(const uno::Exception&)
2377 xExporter.clear();
2382 if ( xExporter.is() )
2384 try{
2385 uno::Reference< lang::XComponent > xComp( GetModel(), uno::UNO_QUERY_THROW );
2386 uno::Reference< document::XFilter > xFilter( xExporter, uno::UNO_QUERY_THROW );
2387 xExporter->setSourceDocument( xComp );
2389 css::uno::Sequence < css::beans::PropertyValue > aOldArgs;
2390 SfxItemSet* pItems = rMedium.GetItemSet();
2391 TransformItems( SID_SAVEASDOC, *pItems, aOldArgs );
2393 const css::beans::PropertyValue * pOldValue = aOldArgs.getConstArray();
2394 css::uno::Sequence < css::beans::PropertyValue > aArgs ( aOldArgs.getLength() );
2395 css::beans::PropertyValue * pNewValue = aArgs.getArray();
2397 // put in the REAL file name, and copy all PropertyValues
2398 const OUString sOutputStream ( "OutputStream" );
2399 const OUString sStream ( "StreamForOutput" );
2400 bool bHasOutputStream = false;
2401 bool bHasStream = false;
2402 bool bHasBaseURL = false;
2403 bool bHasFilterName = false;
2404 bool bIsRedactMode = false;
2405 sal_Int32 nEnd = aOldArgs.getLength();
2407 for ( sal_Int32 i = 0; i < nEnd; i++ )
2409 pNewValue[i] = pOldValue[i];
2410 if ( pOldValue[i].Name == "FileName" )
2411 pNewValue[i].Value <<= rMedium.GetName();
2412 else if ( pOldValue[i].Name == sOutputStream )
2413 bHasOutputStream = true;
2414 else if ( pOldValue[i].Name == sStream )
2415 bHasStream = true;
2416 else if ( pOldValue[i].Name == "DocumentBaseURL" )
2417 bHasBaseURL = true;
2418 else if( pOldValue[i].Name == "FilterName" )
2419 bHasFilterName = true;
2422 // FIXME: Handle this inside TransformItems()
2423 if (pItems->GetItemState(SID_IS_REDACT_MODE) == SfxItemState::SET)
2424 bIsRedactMode = true;
2426 if ( !bHasOutputStream )
2428 aArgs.realloc ( ++nEnd );
2429 aArgs[nEnd-1].Name = sOutputStream;
2430 aArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XOutputStream > ( new utl::OOutputStreamWrapper ( *rMedium.GetOutStream() ) );
2433 // add stream as well, for OOX export and maybe others
2434 if ( !bHasStream )
2436 aArgs.realloc ( ++nEnd );
2437 aArgs[nEnd-1].Name = sStream;
2438 aArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XStream > ( new utl::OStreamWrapper ( *rMedium.GetOutStream() ) );
2441 if ( !bHasBaseURL )
2443 aArgs.realloc ( ++nEnd );
2444 aArgs[nEnd-1].Name = "DocumentBaseURL";
2445 aArgs[nEnd-1].Value <<= rMedium.GetBaseURL( true );
2448 if( !bHasFilterName )
2450 aArgs.realloc( ++nEnd );
2451 aArgs[nEnd-1].Name = "FilterName";
2452 aArgs[nEnd-1].Value <<= aFilterName;
2455 if (bIsRedactMode)
2457 aArgs.realloc( ++nEnd );
2458 aArgs[nEnd-1].Name = "IsRedactMode";
2459 aArgs[nEnd-1].Value <<= bIsRedactMode;
2462 return xFilter->filter( aArgs );
2463 }catch(...)
2467 return false;
2471 bool SfxObjectShell::ConvertTo
2473 SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the target file
2474 (for example file name, <SfxFilter>,
2475 Open-Modi and so on) */
2478 /* [Description]
2480 This method is called for saving of documents over all filters which are
2481 not SfxFilterFlags::OWN or for which no clipboard format has been registered
2482 (thus no storage format that is used). In other words, with this method
2483 it is exported.
2485 Files which are to be opened here should be opened through 'rMedium'
2486 to guarantee the right open modes. Especially if the format is retained
2487 (only possible with SfxFilterFlags::SIMULATE or SfxFilterFlags::OWN) file which must
2488 be opened STREAM_SHARE_DENYWRITE.
2490 [Return value]
2492 bool true
2493 The document could be saved.
2495 false
2496 The document could not be saved, an error code is
2497 received by <SvMedium::GetError()const>
2500 [Example]
2502 bool DocSh::ConvertTo( SfxMedium &rMedium )
2504 SvStreamRef xStream = rMedium.GetOutStream();
2505 if ( xStream.is() )
2507 xStream->SetBufferSize(4096);
2508 *xStream << ...;
2510 rMedium.CloseOutStream(); // opens the InStream automatically
2511 return ERRCODE_NONE == rMedium.GetError();
2513 return false ;
2516 [Cross-references]
2518 <SfxObjectShell::ConvertFrom(SfxMedium&)>
2519 <SfxFilterFlags::REGISTRATION>
2523 return false;
2527 bool SfxObjectShell::DoSave_Impl( const SfxItemSet* pArgs )
2529 SfxMedium* pRetrMedium = GetMedium();
2530 std::shared_ptr<const SfxFilter> pFilter = pRetrMedium->GetFilter();
2532 // copy the original itemset, but remove the "version" item, because pMediumTmp
2533 // is a new medium "from scratch", so no version should be stored into it
2534 std::shared_ptr<SfxItemSet> pSet = std::make_shared<SfxAllItemSet>(*pRetrMedium->GetItemSet());
2535 pSet->ClearItem( SID_VERSION );
2536 pSet->ClearItem( SID_DOC_BASEURL );
2538 // copy the version comment and major items for the checkin only
2539 if ( pRetrMedium->IsInCheckIn( ) )
2541 const SfxPoolItem* pMajor = pArgs->GetItem( SID_DOCINFO_MAJOR );
2542 if ( pMajor )
2543 pSet->Put( *pMajor );
2545 const SfxPoolItem* pComments = pArgs->GetItem( SID_DOCINFO_COMMENTS );
2546 if ( pComments )
2547 pSet->Put( *pComments );
2550 // create a medium as a copy; this medium is only for writing, because it
2551 // uses the same name as the original one writing is done through a copy,
2552 // that will be transferred to the target (of course after calling HandsOff)
2553 SfxMedium* pMediumTmp = new SfxMedium( pRetrMedium->GetName(), pRetrMedium->GetOpenMode(), pFilter, std::move(pSet) );
2554 pMediumTmp->SetInCheckIn( pRetrMedium->IsInCheckIn( ) );
2555 pMediumTmp->SetLongName( pRetrMedium->GetLongName() );
2556 if ( pMediumTmp->GetErrorCode() != ERRCODE_NONE )
2558 SetError(pMediumTmp->GetError());
2559 delete pMediumTmp;
2560 return false;
2563 // copy version list from "old" medium to target medium, so it can be used on saving
2564 pMediumTmp->TransferVersionList_Impl( *pRetrMedium );
2566 // an interaction handler here can acquire only in case of GUI Saving
2567 // and should be removed after the saving is done
2568 css::uno::Reference< XInteractionHandler > xInteract;
2569 const SfxUnoAnyItem* pxInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pArgs, SID_INTERACTIONHANDLER, false);
2570 if ( pxInteractionItem && ( pxInteractionItem->GetValue() >>= xInteract ) && xInteract.is() )
2571 pMediumTmp->GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny( xInteract ) ) );
2573 const SfxBoolItem* pNoFileSync = pArgs->GetItem<SfxBoolItem>(SID_NO_FILE_SYNC, false);
2574 if (pNoFileSync && pNoFileSync->GetValue())
2575 pMediumTmp->DisableFileSync(true);
2577 bool bSaved = false;
2578 if( !GetError() && SaveTo_Impl( *pMediumTmp, pArgs ) )
2580 bSaved = true;
2582 if( pMediumTmp->GetItemSet() )
2584 pMediumTmp->GetItemSet()->ClearItem( SID_INTERACTIONHANDLER );
2585 pMediumTmp->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
2588 SetError(pMediumTmp->GetErrorCode());
2590 bool bOpen = DoSaveCompleted( pMediumTmp );
2592 DBG_ASSERT(bOpen,"Error handling for DoSaveCompleted not implemented");
2594 else
2596 // transfer error code from medium to objectshell
2597 SetError(pMediumTmp->GetError());
2599 // reconnect to object storage
2600 DoSaveCompleted();
2602 if( pRetrMedium->GetItemSet() )
2604 pRetrMedium->GetItemSet()->ClearItem( SID_INTERACTIONHANDLER );
2605 pRetrMedium->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
2608 delete pMediumTmp;
2611 SetModified( !bSaved );
2612 return bSaved;
2616 bool SfxObjectShell::Save_Impl( const SfxItemSet* pSet )
2618 if ( IsReadOnly() )
2620 SetError(ERRCODE_SFX_DOCUMENTREADONLY);
2621 return false;
2624 pImpl->bIsSaving = true;
2625 bool bSaved = false;
2626 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_DOC_SALVAGE, false);
2627 if ( pSalvageItem )
2629 const SfxStringItem* pFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_FILTER_NAME, false);
2630 std::shared_ptr<const SfxFilter> pFilter;
2631 if ( pFilterItem )
2632 pFilter = SfxFilterMatcher( GetFactory().GetFactoryName() ).GetFilter4FilterName( OUString() );
2634 SfxMedium *pMed = new SfxMedium(
2635 pSalvageItem->GetValue(), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, pFilter );
2637 const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_PASSWORD, false);
2638 if ( pPasswordItem )
2639 pMed->GetItemSet()->Put( *pPasswordItem );
2641 bSaved = DoSaveAs( *pMed );
2642 if ( bSaved )
2643 bSaved = DoSaveCompleted( pMed );
2644 else
2645 delete pMed;
2647 else
2648 bSaved = DoSave_Impl( pSet );
2649 return bSaved;
2652 bool SfxObjectShell::CommonSaveAs_Impl(const INetURLObject& aURL, const OUString& aFilterName,
2653 SfxItemSet& rItemSet,
2654 const uno::Sequence<beans::PropertyValue>& rArgs)
2656 if( aURL.HasError() )
2658 SetError(ERRCODE_IO_INVALIDPARAMETER);
2659 return false;
2662 if ( aURL != INetURLObject( OUString( "private:stream" ) ) )
2664 // Is there already a Document with this name?
2665 SfxObjectShell* pDoc = nullptr;
2666 for ( SfxObjectShell* pTmp = SfxObjectShell::GetFirst();
2667 pTmp && !pDoc;
2668 pTmp = SfxObjectShell::GetNext(*pTmp) )
2670 if( ( pTmp != this ) && pTmp->GetMedium() )
2672 INetURLObject aCompare( pTmp->GetMedium()->GetName() );
2673 if ( aCompare == aURL )
2674 pDoc = pTmp;
2677 if ( pDoc )
2679 // Then error message: "already opened"
2680 SetError(ERRCODE_SFX_ALREADYOPEN);
2681 return false;
2685 DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" );
2686 DBG_ASSERT( rItemSet.Count() != 0, "Incorrect Parameter");
2688 const SfxBoolItem* pSaveToItem = rItemSet.GetItem<SfxBoolItem>(SID_SAVETO, false);
2689 bool bSaveTo = pSaveToItem && pSaveToItem->GetValue();
2691 std::shared_ptr<const SfxFilter> pFilter = GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName );
2692 if ( !pFilter
2693 || !pFilter->CanExport()
2694 || (!bSaveTo && !pFilter->CanImport()) )
2696 SetError(ERRCODE_IO_INVALIDPARAMETER);
2697 return false;
2701 const SfxBoolItem* pCopyStreamItem = rItemSet.GetItem<SfxBoolItem>(SID_COPY_STREAM_IF_POSSIBLE, false);
2702 if ( bSaveTo && pCopyStreamItem && pCopyStreamItem->GetValue() && !IsModified() )
2704 if (pMedium->TryDirectTransfer(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), rItemSet))
2705 return true;
2707 rItemSet.ClearItem( SID_COPY_STREAM_IF_POSSIBLE );
2709 SfxMedium *pActMed = GetMedium();
2710 const INetURLObject aActName(pActMed->GetName());
2712 bool bWasReadonly = IsReadOnly();
2714 if ( aURL == aActName && aURL != INetURLObject( OUString("private:stream") )
2715 && IsReadOnly() )
2717 SetError(ERRCODE_SFX_DOCUMENTREADONLY);
2718 return false;
2721 if (SfxItemState::SET != rItemSet.GetItemState(SID_UNPACK) && officecfg::Office::Common::Save::Document::Unpacked::get())
2722 rItemSet.Put(SfxBoolItem(SID_UNPACK, false));
2724 OUString aTempFileURL;
2725 if ( IsDocShared() )
2726 aTempFileURL = pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
2728 if (PreDoSaveAs_Impl(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), aFilterName,
2729 rItemSet, rArgs))
2731 // Update Data on media
2732 SfxItemSet *pSet = GetMedium()->GetItemSet();
2733 pSet->ClearItem( SID_INTERACTIONHANDLER );
2734 pSet->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
2735 pSet->ClearItem( SID_STANDARD_DIR );
2736 pSet->ClearItem( SID_PATH );
2738 if ( !bSaveTo )
2740 pSet->ClearItem( SID_REFERER );
2741 pSet->ClearItem( SID_POSTDATA );
2742 pSet->ClearItem( SID_TEMPLATE );
2743 pSet->ClearItem( SID_DOC_READONLY );
2744 pSet->ClearItem( SID_CONTENTTYPE );
2745 pSet->ClearItem( SID_CHARSET );
2746 pSet->ClearItem( SID_FILTER_NAME );
2747 pSet->ClearItem( SID_OPTIONS );
2748 pSet->ClearItem( SID_VERSION );
2749 pSet->ClearItem( SID_EDITDOC );
2750 pSet->ClearItem( SID_OVERWRITE );
2751 pSet->ClearItem( SID_DEFAULTFILEPATH );
2752 pSet->ClearItem( SID_DEFAULTFILENAME );
2754 const SfxStringItem* pFilterItem = rItemSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
2755 if ( pFilterItem )
2756 pSet->Put( *pFilterItem );
2758 const SfxStringItem* pOptionsItem = rItemSet.GetItem<SfxStringItem>(SID_OPTIONS, false);
2759 if ( pOptionsItem )
2760 pSet->Put( *pOptionsItem );
2762 const SfxStringItem* pFilterOptItem = rItemSet.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
2763 if ( pFilterOptItem )
2764 pSet->Put( *pFilterOptItem );
2766 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
2767 if ( IsDocShared() && !aTempFileURL.isEmpty() )
2769 // this is a shared document that has to be disconnected from the old location
2770 FreeSharedFile( aTempFileURL );
2772 if ( pFilter->IsOwnFormat()
2773 && pFilter->UsesStorage()
2774 && pFilter->GetVersion() >= SOFFICE_FILEFORMAT_60 )
2776 // the target format is the own format
2777 // the target document must be shared
2778 SwitchToShared( true, false );
2781 #endif
2784 if ( bWasReadonly && !bSaveTo )
2785 Broadcast( SfxHint(SfxHintId::ModeChanged) );
2787 return true;
2789 else
2790 return false;
2793 bool SfxObjectShell::PreDoSaveAs_Impl(const OUString& rFileName, const OUString& aFilterName,
2794 SfxItemSet const& rItemSet,
2795 const uno::Sequence<beans::PropertyValue>& rArgs)
2797 // copy all items stored in the itemset of the current medium
2798 std::shared_ptr<SfxAllItemSet> xMergedParams = std::make_shared<SfxAllItemSet>( *pMedium->GetItemSet() );
2800 // in "SaveAs" title and password will be cleared ( maybe the new itemset contains new values, otherwise they will be empty )
2801 // #i119366# - As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both.
2802 // Also, ( maybe the new itemset contains new values, otherwise they will be empty )
2803 if (xMergedParams->HasItem(SID_ENCRYPTIONDATA))
2805 bool bPasswordProtected = true;
2806 const SfxUnoAnyItem* pEncryptionDataItem
2807 = xMergedParams->GetItem<SfxUnoAnyItem>(SID_ENCRYPTIONDATA, false);
2808 if (pEncryptionDataItem)
2810 uno::Sequence<beans::NamedValue> aEncryptionData;
2811 pEncryptionDataItem->GetValue() >>= aEncryptionData;
2812 for (const auto& rItem : std::as_const(aEncryptionData))
2814 if (rItem.Name == "CryptoType")
2816 OUString aValue;
2817 rItem.Value >>= aValue;
2818 if (aValue != "StrongEncryptionDataSpace")
2820 // This is not just a password protected document. Let's keep encryption data as is.
2821 bPasswordProtected = false;
2823 break;
2827 if (bPasswordProtected)
2829 // For password protected documents remove encryption data during "Save as..."
2830 xMergedParams->ClearItem(SID_PASSWORD);
2831 xMergedParams->ClearItem(SID_ENCRYPTIONDATA);
2835 xMergedParams->ClearItem( SID_DOCINFO_TITLE );
2837 xMergedParams->ClearItem( SID_INPUTSTREAM );
2838 xMergedParams->ClearItem( SID_STREAM );
2839 xMergedParams->ClearItem( SID_CONTENT );
2840 xMergedParams->ClearItem( SID_DOC_READONLY );
2841 xMergedParams->ClearItem( SID_DOC_BASEURL );
2843 xMergedParams->ClearItem( SID_REPAIRPACKAGE );
2845 // "SaveAs" will never store any version information - it's a complete new file !
2846 xMergedParams->ClearItem( SID_VERSION );
2848 // merge the new parameters into the copy
2849 // all values present in both itemsets will be overwritten by the new parameters
2850 xMergedParams->Put(rItemSet);
2852 SAL_WARN_IF( xMergedParams->GetItemState( SID_DOC_SALVAGE) >= SfxItemState::SET,
2853 "sfx.doc","Salvage item present in Itemset, check the parameters!");
2855 // should be unnecessary - too hot to handle!
2856 xMergedParams->ClearItem( SID_DOC_SALVAGE );
2858 // create a medium for the target URL
2859 SfxMedium *pNewFile = new SfxMedium( rFileName, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, nullptr, xMergedParams );
2860 pNewFile->SetArgs(rArgs);
2862 const SfxBoolItem* pNoFileSync = xMergedParams->GetItem<SfxBoolItem>(SID_NO_FILE_SYNC, false);
2863 if (pNoFileSync && pNoFileSync->GetValue())
2864 pNewFile->DisableFileSync(true);
2866 bool bUseThumbnailSave = IsUseThumbnailSave();
2867 comphelper::ScopeGuard aThumbnailGuard(
2868 [this, bUseThumbnailSave] { this->SetUseThumbnailSave(bUseThumbnailSave); });
2869 const SfxBoolItem* pNoThumbnail = xMergedParams->GetItem<SfxBoolItem>(SID_NO_THUMBNAIL, false);
2870 if (pNoThumbnail)
2871 // Thumbnail generation should be avoided just for this save.
2872 SetUseThumbnailSave(!pNoThumbnail->GetValue());
2873 else
2874 aThumbnailGuard.dismiss();
2876 // set filter; if no filter is given, take the default filter of the factory
2877 if ( !aFilterName.isEmpty() )
2879 pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName ) );
2881 if(aFilterName == "writer_pdf_Export" && pNewFile->GetItemSet())
2883 uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions(2);
2884 bool bRet = false;
2886 for(int i = 0 ; i< rArgs.getLength() ; ++i)
2888 auto aProp = rArgs[i];
2889 if(aProp.Name == "EncryptFile")
2891 aSaveToFilterDataOptions[0].Name = aProp.Name;
2892 aSaveToFilterDataOptions[0].Value = aProp.Value;
2893 bRet = true;
2895 if(aProp.Name == "DocumentOpenPassword")
2897 aSaveToFilterDataOptions[1].Name = aProp.Name;
2898 aSaveToFilterDataOptions[1].Value = aProp.Value;
2899 bRet = true;
2903 if( bRet )
2904 pNewFile->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA, uno::makeAny(aSaveToFilterDataOptions)));
2907 else
2908 pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT ) );
2910 if ( pNewFile->GetErrorCode() != ERRCODE_NONE )
2912 // creating temporary file failed ( f.e. floppy disk not inserted! )
2913 SetError(pNewFile->GetError());
2914 delete pNewFile;
2915 return false;
2918 if (comphelper::LibreOfficeKit::isActive())
2920 // Before saving, commit in-flight changes.
2921 TerminateEditing();
2924 // check if a "SaveTo" is wanted, no "SaveAs"
2925 const SfxBoolItem* pSaveToItem = xMergedParams->GetItem<SfxBoolItem>(SID_SAVETO, false);
2926 bool bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED || (pSaveToItem && pSaveToItem->GetValue());
2928 // distinguish between "Save" and "SaveAs"
2929 pImpl->bIsSaving = false;
2931 // copy version list from "old" medium to target medium, so it can be used on saving
2932 if ( pImpl->bPreserveVersions )
2933 pNewFile->TransferVersionList_Impl( *pMedium );
2935 // Save the document ( first as temporary file, then transfer to the target URL by committing the medium )
2936 bool bOk = false;
2937 if ( !pNewFile->GetErrorCode() && SaveTo_Impl( *pNewFile, nullptr ) )
2939 // transfer a possible error from the medium to the document
2940 SetError(pNewFile->GetErrorCode());
2942 // notify the document that saving was done successfully
2943 if ( !bCopyTo )
2945 bOk = DoSaveCompleted( pNewFile );
2947 else
2948 bOk = DoSaveCompleted();
2950 if( bOk )
2952 if( !bCopyTo )
2953 SetModified( false );
2955 else
2957 // TODO/LATER: the code below must be dead since the storage commit makes all the stuff
2958 // and the DoSaveCompleted call should not be able to fail in general
2960 DBG_ASSERT( !bCopyTo, "Error while reconnecting to medium, can't be handled!");
2961 SetError(pNewFile->GetErrorCode());
2963 if ( !bCopyTo )
2965 // reconnect to the old medium
2966 bool bRet = DoSaveCompleted( pMedium );
2967 DBG_ASSERT( bRet, "Error in DoSaveCompleted, can't be handled!");
2970 // TODO/LATER: disconnect the new file from the storage for the case when pure saving is done
2971 // if storing has corrupted the file, probably it must be restored either here or
2972 // by the storage
2973 delete pNewFile;
2974 pNewFile = nullptr;
2977 else
2979 SetError(pNewFile->GetErrorCode());
2981 // reconnect to the old storage
2982 DoSaveCompleted();
2984 delete pNewFile;
2985 pNewFile = nullptr;
2988 if ( bCopyTo )
2989 delete pNewFile;
2990 else if( !bOk )
2991 SetModified();
2993 return bOk;
2997 bool SfxObjectShell::LoadFrom( SfxMedium& /*rMedium*/ )
2999 SAL_WARN( "sfx.doc", "Base implementation, must not be called in general!" );
3000 return true;
3004 bool SfxObjectShell::CanReload_Impl()
3006 /* [Description]
3008 Internal method for determining whether a reload of the document
3009 (as RevertToSaved or last known version) is possible.
3013 return pMedium && HasName() && !IsInModalMode() && !pImpl->bForbidReload;
3017 HiddenInformation SfxObjectShell::GetHiddenInformationState( HiddenInformation nStates )
3019 HiddenInformation nState = HiddenInformation::NONE;
3020 if ( nStates & HiddenInformation::DOCUMENTVERSIONS )
3022 if ( GetMedium()->GetVersionList().hasElements() )
3023 nState |= HiddenInformation::DOCUMENTVERSIONS;
3026 return nState;
3029 sal_Int16 SfxObjectShell::QueryHiddenInformation(HiddenWarningFact eFact, weld::Window* pParent)
3031 sal_Int16 nRet = RET_YES;
3032 const char* pResId = nullptr;
3033 SvtSecurityOptions::EOption eOption = SvtSecurityOptions::EOption();
3035 switch ( eFact )
3037 case HiddenWarningFact::WhenSaving :
3039 pResId = STR_HIDDENINFO_CONTINUE_SAVING;
3040 eOption = SvtSecurityOptions::EOption::DocWarnSaveOrSend;
3041 break;
3043 case HiddenWarningFact::WhenPrinting :
3045 pResId = STR_HIDDENINFO_CONTINUE_PRINTING;
3046 eOption = SvtSecurityOptions::EOption::DocWarnPrint;
3047 break;
3049 case HiddenWarningFact::WhenSigning :
3051 pResId = STR_HIDDENINFO_CONTINUE_SIGNING;
3052 eOption = SvtSecurityOptions::EOption::DocWarnSigning;
3053 break;
3055 case HiddenWarningFact::WhenCreatingPDF :
3057 pResId = STR_HIDDENINFO_CONTINUE_CREATEPDF;
3058 eOption = SvtSecurityOptions::EOption::DocWarnCreatePdf;
3059 break;
3061 default:
3062 assert(false); // this cannot happen
3065 if ( SvtSecurityOptions().IsOptionSet( eOption ) )
3067 OUString sMessage( SfxResId(STR_HIDDENINFO_CONTAINS) );
3068 HiddenInformation nWantedStates = HiddenInformation::RECORDEDCHANGES | HiddenInformation::NOTES;
3069 if ( eFact != HiddenWarningFact::WhenPrinting )
3070 nWantedStates |= HiddenInformation::DOCUMENTVERSIONS;
3071 HiddenInformation nStates = GetHiddenInformationState( nWantedStates );
3072 bool bWarning = false;
3074 if ( nStates & HiddenInformation::RECORDEDCHANGES )
3076 sMessage += SfxResId(STR_HIDDENINFO_RECORDCHANGES) + "\n";
3077 bWarning = true;
3079 if ( nStates & HiddenInformation::NOTES )
3081 sMessage += SfxResId(STR_HIDDENINFO_NOTES) + "\n";
3082 bWarning = true;
3084 if ( nStates & HiddenInformation::DOCUMENTVERSIONS )
3086 sMessage += SfxResId(STR_HIDDENINFO_DOCVERSIONS) + "\n";
3087 bWarning = true;
3090 if ( bWarning )
3092 sMessage += "\n" + SfxResId(pResId);
3093 std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pParent,
3094 VclMessageType::Warning, VclButtonsType::YesNo, sMessage));
3095 xWarn->set_default_response(RET_NO);
3096 nRet = xWarn->run();
3100 return nRet;
3103 bool SfxObjectShell::IsSecurityOptOpenReadOnly() const
3105 return IsLoadReadonly();
3108 void SfxObjectShell::SetSecurityOptOpenReadOnly( bool _b )
3110 SetLoadReadonly( _b );
3113 bool SfxObjectShell::LoadOwnFormat( SfxMedium& rMedium )
3115 SAL_INFO( "sfx.doc", "loading \" " << rMedium.GetName() << "\"" );
3117 uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage();
3118 if ( xStorage.is() )
3120 // Password
3121 const SfxStringItem* pPasswdItem = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_PASSWORD, false);
3122 if ( pPasswdItem || ERRCODE_IO_ABORT != CheckPasswd_Impl( this, pMedium ) )
3124 uno::Sequence< beans::NamedValue > aEncryptionData;
3125 if ( GetEncryptionData_Impl(pMedium->GetItemSet(), aEncryptionData) )
3129 // the following code must throw an exception in case of failure
3130 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xStorage, aEncryptionData );
3132 catch( uno::Exception& )
3134 // TODO/LATER: handle the error code
3138 // load document
3139 return Load( rMedium );
3141 return false;
3143 else
3144 return false;
3147 bool SfxObjectShell::SaveAsOwnFormat( SfxMedium& rMedium )
3149 uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage();
3150 if( xStorage.is() )
3152 sal_Int32 nVersion = rMedium.GetFilter()->GetVersion();
3154 // OASIS templates have own mediatypes (SO7 also actually, but it is too late to use them here)
3155 const bool bTemplate = rMedium.GetFilter()->IsOwnTemplateFormat()
3156 && nVersion > SOFFICE_FILEFORMAT_60;
3158 SetupStorage( xStorage, nVersion, bTemplate );
3159 #if HAVE_FEATURE_SCRIPTING
3160 if ( HasBasic() )
3162 // Initialize Basic
3163 GetBasicManager();
3165 // Save dialog/script container
3166 pImpl->aBasicManager.storeLibrariesToStorage( xStorage );
3168 #endif
3169 return SaveAs( rMedium );
3171 else return false;
3174 uno::Reference< embed::XStorage > const & SfxObjectShell::GetStorage()
3176 if ( !pImpl->m_xDocStorage.is() )
3178 OSL_ENSURE( pImpl->m_bCreateTempStor, "The storage must exist already!" );
3179 try {
3180 // no notification is required the storage is set the first time
3181 pImpl->m_xDocStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
3182 OSL_ENSURE( pImpl->m_xDocStorage.is(), "The method must either return storage or throw exception!" );
3184 SetupStorage( pImpl->m_xDocStorage, SOFFICE_FILEFORMAT_CURRENT, false );
3185 pImpl->m_bCreateTempStor = false;
3186 if (!utl::ConfigManager::IsFuzzing())
3187 SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
3189 catch( uno::Exception& )
3191 // TODO/LATER: error handling?
3192 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::GetStorage");
3196 OSL_ENSURE( pImpl->m_xDocStorage.is(), "The document storage must be created!" );
3197 return pImpl->m_xDocStorage;
3201 void SfxObjectShell::SaveChildren( bool bObjectsOnly )
3203 if ( pImpl->mxObjectContainer )
3205 bool bOasis = ( SotStorage::GetVersion( GetStorage() ) > SOFFICE_FILEFORMAT_60 );
3206 GetEmbeddedObjectContainer().StoreChildren(bOasis,bObjectsOnly);
3210 bool SfxObjectShell::SaveAsChildren( SfxMedium& rMedium )
3212 uno::Reference < embed::XStorage > xStorage = rMedium.GetStorage();
3213 if ( !xStorage.is() )
3214 return false;
3216 if ( xStorage == GetStorage() )
3218 SaveChildren();
3219 return true;
3222 if ( pImpl->mxObjectContainer )
3224 bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
3225 GetEmbeddedObjectContainer().StoreAsChildren(bOasis,SfxObjectCreateMode::EMBEDDED == eCreateMode,xStorage);
3228 uno::Sequence<OUString> aExceptions;
3229 if (const SfxBoolItem* pNoEmbDS
3230 = SfxItemSet::GetItem(rMedium.GetItemSet(), SID_NO_EMBEDDED_DS, false))
3232 // Don't save data source in case a temporary is being saved for preview in MM wizard
3233 if (pNoEmbDS->GetValue())
3234 aExceptions = uno::Sequence<OUString>{ "EmbeddedDatabase" };
3237 return CopyStoragesOfUnknownMediaType(GetStorage(), xStorage, aExceptions);
3240 bool SfxObjectShell::SaveCompletedChildren()
3242 bool bResult = true;
3244 if ( pImpl->mxObjectContainer )
3246 const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
3247 for ( const auto& rName : aNames )
3249 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
3250 OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
3251 if ( xObj.is() )
3253 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
3254 if ( xPersist.is() )
3258 xPersist->saveCompleted( false/*bSuccess*/ );
3260 catch( uno::Exception& )
3262 // TODO/LATER: error handling
3263 bResult = false;
3264 break;
3271 return bResult;
3274 bool SfxObjectShell::SwitchChildrenPersistance( const uno::Reference< embed::XStorage >& xStorage,
3275 bool bForceNonModified )
3277 if ( !xStorage.is() )
3279 // TODO/LATER: error handling
3280 return false;
3283 if ( pImpl->mxObjectContainer )
3284 pImpl->mxObjectContainer->SetPersistentEntries(xStorage,bForceNonModified);
3286 return true;
3289 // Never call this method directly, always use the DoSaveCompleted call
3290 bool SfxObjectShell::SaveCompleted( const uno::Reference< embed::XStorage >& xStorage )
3292 bool bResult = false;
3293 bool bSendNotification = false;
3294 uno::Reference< embed::XStorage > xOldStorageHolder;
3296 // check for wrong creation of object container
3297 bool bHasContainer( pImpl->mxObjectContainer );
3299 if ( !xStorage.is() || xStorage == GetStorage() )
3301 // no persistence change
3302 bResult = SaveCompletedChildren();
3304 else
3306 if ( pImpl->mxObjectContainer )
3307 GetEmbeddedObjectContainer().SwitchPersistence( xStorage );
3309 bResult = SwitchChildrenPersistance( xStorage, true );
3312 if ( bResult )
3314 if ( xStorage.is() && pImpl->m_xDocStorage != xStorage )
3316 // make sure that until the storage is assigned the object
3317 // container is not created by accident!
3318 DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
3319 xOldStorageHolder = pImpl->m_xDocStorage;
3320 pImpl->m_xDocStorage = xStorage;
3321 bSendNotification = true;
3323 if ( IsEnableSetModified() )
3324 SetModified( false );
3327 else
3329 if ( pImpl->mxObjectContainer )
3330 GetEmbeddedObjectContainer().SwitchPersistence( pImpl->m_xDocStorage );
3332 // let already successfully connected objects be switched back
3333 SwitchChildrenPersistance( pImpl->m_xDocStorage, true );
3336 if ( bSendNotification )
3338 SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
3341 return bResult;
3344 static bool StoragesOfUnknownMediaTypeAreCopied_Impl( const uno::Reference< embed::XStorage >& xSource,
3345 const uno::Reference< embed::XStorage >& xTarget )
3347 OSL_ENSURE( xSource.is() && xTarget.is(), "Source and/or target storages are not available!" );
3348 if ( !xSource.is() || !xTarget.is() || xSource == xTarget )
3349 return true;
3353 const uno::Sequence< OUString > aSubElements = xSource->getElementNames();
3354 for ( const auto& rSubElement : aSubElements )
3356 if ( xSource->isStorageElement( rSubElement ) )
3358 OUString aMediaType;
3359 const OUString aMediaTypePropName( "MediaType" );
3360 bool bGotMediaType = false;
3364 uno::Reference< embed::XOptimizedStorage > xOptStorage( xSource, uno::UNO_QUERY_THROW );
3365 bGotMediaType =
3366 ( xOptStorage->getElementPropertyValue( rSubElement, aMediaTypePropName ) >>= aMediaType );
3368 catch( uno::Exception& )
3371 if ( !bGotMediaType )
3373 uno::Reference< embed::XStorage > xSubStorage;
3374 try {
3375 xSubStorage = xSource->openStorageElement( rSubElement, embed::ElementModes::READ );
3376 } catch( uno::Exception& )
3379 if ( !xSubStorage.is() )
3381 xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
3382 xSource->copyStorageElementLastCommitTo( rSubElement, xSubStorage );
3385 uno::Reference< beans::XPropertySet > xProps( xSubStorage, uno::UNO_QUERY_THROW );
3386 xProps->getPropertyValue( aMediaTypePropName ) >>= aMediaType;
3389 // TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist
3390 // probably it should be placed in the MimeType-ClassID table or in standalone table
3391 if ( !aMediaType.isEmpty()
3392 && aMediaType != "application/vnd.sun.star.oleobject" )
3394 css::datatransfer::DataFlavor aDataFlavor;
3395 aDataFlavor.MimeType = aMediaType;
3396 SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
3398 switch ( nFormat )
3400 case SotClipboardFormatId::STARWRITER_60 :
3401 case SotClipboardFormatId::STARWRITERWEB_60 :
3402 case SotClipboardFormatId::STARWRITERGLOB_60 :
3403 case SotClipboardFormatId::STARDRAW_60 :
3404 case SotClipboardFormatId::STARIMPRESS_60 :
3405 case SotClipboardFormatId::STARCALC_60 :
3406 case SotClipboardFormatId::STARCHART_60 :
3407 case SotClipboardFormatId::STARMATH_60 :
3408 case SotClipboardFormatId::STARWRITER_8:
3409 case SotClipboardFormatId::STARWRITERWEB_8:
3410 case SotClipboardFormatId::STARWRITERGLOB_8:
3411 case SotClipboardFormatId::STARDRAW_8:
3412 case SotClipboardFormatId::STARIMPRESS_8:
3413 case SotClipboardFormatId::STARCALC_8:
3414 case SotClipboardFormatId::STARCHART_8:
3415 case SotClipboardFormatId::STARMATH_8:
3416 break;
3418 default:
3420 if ( !xTarget->hasByName( rSubElement ) )
3421 return false;
3428 catch( uno::Exception& )
3430 SAL_WARN( "sfx.doc", "Can not check storage consistency!" );
3433 return true;
3436 bool SfxObjectShell::SwitchPersistance( const uno::Reference< embed::XStorage >& xStorage )
3438 bool bResult = false;
3439 // check for wrong creation of object container
3440 bool bHasContainer( pImpl->mxObjectContainer );
3441 if ( xStorage.is() )
3443 if ( pImpl->mxObjectContainer )
3444 GetEmbeddedObjectContainer().SwitchPersistence( xStorage );
3445 bResult = SwitchChildrenPersistance( xStorage );
3447 // TODO/LATER: substorages that have unknown mimetypes probably should be copied to the target storage here
3448 OSL_ENSURE( StoragesOfUnknownMediaTypeAreCopied_Impl( pImpl->m_xDocStorage, xStorage ),
3449 "Some of substorages with unknown mimetypes is lost!" );
3452 if ( bResult )
3454 // make sure that until the storage is assigned the object container is not created by accident!
3455 DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
3456 if ( pImpl->m_xDocStorage != xStorage )
3457 DoSaveCompleted( new SfxMedium( xStorage, GetMedium()->GetBaseURL() ) );
3459 if ( IsEnableSetModified() )
3460 SetModified(); // ???
3463 return bResult;
3466 bool SfxObjectShell::CopyStoragesOfUnknownMediaType(const uno::Reference< embed::XStorage >& xSource,
3467 const uno::Reference< embed::XStorage >& xTarget,
3468 const uno::Sequence<OUString>& rExceptions)
3470 // This method does not commit the target storage and should not do it
3471 bool bResult = true;
3475 const css::uno::Sequence<OUString> aSubElementNames = xSource->getElementNames();
3476 for (const OUString& rSubElement : aSubElementNames)
3478 if (std::find(rExceptions.begin(), rExceptions.end(), rSubElement) != rExceptions.end())
3479 continue;
3481 if (rSubElement == "Configurations")
3483 // The workaround for compatibility with SO7, "Configurations" substorage must be preserved
3484 if (xSource->isStorageElement(rSubElement))
3486 OSL_ENSURE(!xTarget->hasByName(rSubElement), "The target storage is an output "
3487 "storage, the element should not "
3488 "exist in the target!");
3490 xSource->copyElementTo(rSubElement, xTarget, rSubElement);
3493 else if (xSource->isStorageElement(rSubElement))
3495 OUString aMediaType;
3496 const OUString aMediaTypePropName( "MediaType" );
3497 bool bGotMediaType = false;
3501 uno::Reference< embed::XOptimizedStorage > xOptStorage( xSource, uno::UNO_QUERY_THROW );
3502 bGotMediaType = (xOptStorage->getElementPropertyValue(rSubElement, aMediaTypePropName)
3503 >>= aMediaType);
3505 catch( uno::Exception& )
3508 if ( !bGotMediaType )
3510 uno::Reference< embed::XStorage > xSubStorage;
3511 try {
3512 xSubStorage
3513 = xSource->openStorageElement(rSubElement, embed::ElementModes::READ);
3514 } catch( uno::Exception& )
3517 if ( !xSubStorage.is() )
3519 // TODO/LATER: as optimization in future a substorage of target storage could be used
3520 // instead of the temporary storage; this substorage should be removed later
3521 // if the MimeType is wrong
3522 xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
3523 xSource->copyStorageElementLastCommitTo(rSubElement, xSubStorage);
3526 uno::Reference< beans::XPropertySet > xProps( xSubStorage, uno::UNO_QUERY_THROW );
3527 xProps->getPropertyValue( aMediaTypePropName ) >>= aMediaType;
3530 // TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist
3531 // probably it should be placed in the MimeType-ClassID table or in standalone table
3532 if ( !aMediaType.isEmpty()
3533 && aMediaType != "application/vnd.sun.star.oleobject" )
3535 css::datatransfer::DataFlavor aDataFlavor;
3536 aDataFlavor.MimeType = aMediaType;
3537 SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
3539 switch ( nFormat )
3541 case SotClipboardFormatId::STARWRITER_60 :
3542 case SotClipboardFormatId::STARWRITERWEB_60 :
3543 case SotClipboardFormatId::STARWRITERGLOB_60 :
3544 case SotClipboardFormatId::STARDRAW_60 :
3545 case SotClipboardFormatId::STARIMPRESS_60 :
3546 case SotClipboardFormatId::STARCALC_60 :
3547 case SotClipboardFormatId::STARCHART_60 :
3548 case SotClipboardFormatId::STARMATH_60 :
3549 case SotClipboardFormatId::STARWRITER_8:
3550 case SotClipboardFormatId::STARWRITERWEB_8:
3551 case SotClipboardFormatId::STARWRITERGLOB_8:
3552 case SotClipboardFormatId::STARDRAW_8:
3553 case SotClipboardFormatId::STARIMPRESS_8:
3554 case SotClipboardFormatId::STARCALC_8:
3555 case SotClipboardFormatId::STARCHART_8:
3556 case SotClipboardFormatId::STARMATH_8:
3557 break;
3559 default:
3561 OSL_ENSURE(rSubElement == "Configurations2"
3562 || nFormat == SotClipboardFormatId::STARBASE_8
3563 || !xTarget->hasByName(rSubElement),
3564 "The target storage is an output storage, the element "
3565 "should not exist in the target!");
3567 if (!xTarget->hasByName(rSubElement))
3569 xSource->copyElementTo(rSubElement, xTarget, rSubElement);
3577 catch( uno::Exception& )
3579 bResult = false;
3580 // TODO/LATER: a specific error could be provided
3583 return bResult;
3586 bool SfxObjectShell::GenerateAndStoreThumbnail(bool bEncrypted, const uno::Reference<embed::XStorage>& xStorage)
3588 //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
3589 bIsInGenerateThumbnail = true;
3591 bool bResult = false;
3595 uno::Reference<embed::XStorage> xThumbnailStorage = xStorage->openStorageElement("Thumbnails", embed::ElementModes::READWRITE);
3597 if (xThumbnailStorage.is())
3599 uno::Reference<io::XStream> xStream = xThumbnailStorage->openStreamElement("thumbnail.png", embed::ElementModes::READWRITE);
3601 if (xStream.is() && WriteThumbnail(bEncrypted, xStream))
3603 uno::Reference<embed::XTransactedObject> xTransactedObject(xThumbnailStorage, uno::UNO_QUERY_THROW);
3604 xTransactedObject->commit();
3605 bResult = true;
3609 catch( uno::Exception& )
3613 //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
3614 bIsInGenerateThumbnail = false;
3616 return bResult;
3619 bool SfxObjectShell::WriteThumbnail(bool bEncrypted, const uno::Reference<io::XStream>& xStream)
3621 bool bResult = false;
3623 if (!xStream.is())
3624 return false;
3628 uno::Reference<io::XTruncate> xTruncate(xStream->getOutputStream(), uno::UNO_QUERY_THROW);
3629 xTruncate->truncate();
3631 uno::Reference <beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
3632 if (xSet.is())
3633 xSet->setPropertyValue("MediaType", uno::makeAny(OUString("image/png")));
3634 if (bEncrypted)
3636 const OUString sResID = GraphicHelper::getThumbnailReplacementIDByFactoryName_Impl(
3637 GetFactory().GetFactoryName());
3638 if (!sResID.isEmpty())
3639 bResult = GraphicHelper::getThumbnailReplacement_Impl(sResID, xStream);
3641 else
3643 std::shared_ptr<GDIMetaFile> xMetaFile = GetPreviewMetaFile();
3644 if (xMetaFile)
3646 bResult = GraphicHelper::getThumbnailFormatFromGDI_Impl(xMetaFile.get(), xStream);
3650 catch(uno::Exception&)
3653 return bResult;
3656 void SfxObjectShell::UpdateLinks()
3660 bool SfxObjectShell::LoadExternal( SfxMedium& )
3662 // Not implemented. It's an error if the code path ever comes here.
3663 assert(false);
3664 return false;
3667 bool SfxObjectShell::InsertGeneratedStream(SfxMedium&,
3668 uno::Reference<text::XTextRange> const&)
3670 // Not implemented. It's an error if the code path ever comes here.
3671 assert(false);
3672 return false;
3675 bool SfxObjectShell::IsConfigOptionsChecked() const
3677 return pImpl->m_bConfigOptionsChecked;
3680 void SfxObjectShell::SetConfigOptionsChecked( bool bChecked )
3682 pImpl->m_bConfigOptionsChecked = bChecked;
3685 void SfxObjectShell::SetMacroCallsSeenWhileLoading()
3687 pImpl->m_bMacroCallsSeenWhileLoading = true;
3690 bool SfxObjectShell::GetMacroCallsSeenWhileLoading() const
3692 if (officecfg::Office::Common::Security::Scripting::CheckDocumentEvents::get())
3693 return pImpl->m_bMacroCallsSeenWhileLoading;
3694 return false;
3697 bool SfxObjectShell::QuerySaveSizeExceededModules_Impl( const uno::Reference< task::XInteractionHandler >& xHandler )
3699 #if !HAVE_FEATURE_SCRIPTING
3700 (void) xHandler;
3701 #else
3702 if ( !HasBasic() )
3703 return true;
3705 if ( !pImpl->aBasicManager.isValid() )
3706 GetBasicManager();
3707 std::vector< OUString > sModules;
3708 if ( xHandler.is() )
3710 if( pImpl->aBasicManager.LegacyPsswdBinaryLimitExceeded( sModules ) )
3712 ModuleSizeExceeded* pReq = new ModuleSizeExceeded( sModules );
3713 uno::Reference< task::XInteractionRequest > xReq( pReq );
3714 xHandler->handle( xReq );
3715 return pReq->isApprove();
3718 #endif
3719 // No interaction handler, default is to continue to save
3720 return true;
3723 bool SfxObjectShell::QueryAllowExoticFormat_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& rURL, const OUString& rFilterUIName )
3725 if ( SvtSecurityOptions().isTrustedLocationUri( rURL ) )
3727 // Always load from trusted location
3728 return true;
3730 if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 0 )
3732 // Refuse loading without question
3733 return false;
3735 else if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 2 )
3737 // Always load without question
3738 return true;
3740 else if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 1 && xHandler.is() )
3742 // Display a warning and let the user decide
3743 rtl::Reference<ExoticFileLoadException> xException(new ExoticFileLoadException( rURL, rFilterUIName ));
3744 uno::Reference< task::XInteractionRequest > xReq( xException.get() );
3745 xHandler->handle( xReq );
3746 return xException->isApprove();
3748 // No interaction handler, default is to continue to load
3749 return true;
3752 uno::Reference< task::XInteractionHandler > SfxObjectShell::getInteractionHandler() const
3754 uno::Reference< task::XInteractionHandler > xRet;
3755 if ( GetMedium() )
3756 xRet = GetMedium()->GetInteractionHandler();
3757 return xRet;
3760 OUString SfxObjectShell::getDocumentBaseURL() const
3762 return GetMedium()->GetBaseURL();
3765 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */