LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / doc / objstor.cxx
blobc56efca7cf8ff1d18d66187c17a6f44dafca3563
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>
94 #include <i18nlangtag/languagetag.hxx>
96 #include <sfx2/signaturestate.hxx>
97 #include <sfx2/app.hxx>
98 #include <sfx2/objsh.hxx>
99 #include <sfx2/sfxresid.hxx>
100 #include <sfx2/docfile.hxx>
101 #include <sfx2/fcontnr.hxx>
102 #include <sfx2/docfilt.hxx>
103 #include <sfx2/docfac.hxx>
104 #include <appopen.hxx>
105 #include <objshimp.hxx>
106 #include <sfx2/lokhelper.hxx>
107 #include <sfx2/strings.hrc>
108 #include <sfx2/sfxsids.hrc>
109 #include <sfx2/dispatch.hxx>
110 #include <sfx2/sfxuno.hxx>
111 #include <sfx2/event.hxx>
112 #include <fltoptint.hxx>
113 #include <sfx2/viewfrm.hxx>
114 #include "graphhelp.hxx"
115 #include <appbaslib.hxx>
116 #include "objstor.hxx"
117 #include "exoticfileloadexception.hxx"
119 using namespace ::com::sun::star;
120 using namespace ::com::sun::star::container;
121 using namespace ::com::sun::star::lang;
122 using namespace ::com::sun::star::ui::dialogs;
123 using namespace ::com::sun::star::uno;
124 using namespace ::com::sun::star::beans;
125 using namespace ::com::sun::star::ucb;
126 using namespace ::com::sun::star::task;
127 using namespace ::com::sun::star::document;
128 using namespace ::cppu;
131 void impl_addToModelCollection(const css::uno::Reference< css::frame::XModel >& xModel)
133 if (!xModel.is())
134 return;
136 css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
137 css::uno::Reference< css::frame::XGlobalEventBroadcaster > xModelCollection =
138 css::frame::theGlobalEventBroadcaster::get(xContext);
141 xModelCollection->insert(css::uno::makeAny(xModel));
143 catch ( uno::Exception& )
145 SAL_WARN( "sfx.doc", "The document seems to be in the collection already!" );
150 bool SfxObjectShell::Save()
152 SaveChildren();
153 return true;
157 bool SfxObjectShell::SaveAs( SfxMedium& rMedium )
159 return SaveAsChildren( rMedium );
163 bool SfxObjectShell::QuerySlotExecutable( sal_uInt16 /*nSlotId*/ )
165 return true;
169 bool GetEncryptionData_Impl( const SfxItemSet* pSet, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
171 bool bResult = false;
172 if ( pSet )
174 const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_ENCRYPTIONDATA, false);
175 if ( pEncryptionDataItem )
177 pEncryptionDataItem->GetValue() >>= o_rEncryptionData;
178 bResult = true;
180 else
182 const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_PASSWORD, false);
183 if ( pPasswordItem )
185 o_rEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( pPasswordItem->GetValue() );
186 bResult = true;
191 return bResult;
195 bool SfxObjectShell::PutURLContentsToVersionStream_Impl(
196 const OUString& aURL,
197 const uno::Reference< embed::XStorage >& xDocStorage,
198 const OUString& aStreamName )
200 bool bResult = false;
203 uno::Reference< embed::XStorage > xVersion = xDocStorage->openStorageElement(
204 "Versions",
205 embed::ElementModes::READWRITE );
207 DBG_ASSERT( xVersion.is(),
208 "The method must throw an exception if the storage can not be opened!" );
209 if ( !xVersion.is() )
210 throw uno::RuntimeException();
212 uno::Reference< io::XStream > xVerStream = xVersion->openStreamElement(
213 aStreamName,
214 embed::ElementModes::READWRITE );
215 DBG_ASSERT( xVerStream.is(), "The method must throw an exception if the storage can not be opened!" );
216 if ( !xVerStream.is() )
217 throw uno::RuntimeException();
219 uno::Reference< io::XOutputStream > xOutStream = xVerStream->getOutputStream();
220 uno::Reference< io::XTruncate > xTrunc( xOutStream, uno::UNO_QUERY_THROW );
222 uno::Reference< io::XInputStream > xTmpInStream =
223 ::comphelper::OStorageHelper::GetInputStreamFromURL(
224 aURL, comphelper::getProcessComponentContext() );
225 assert( xTmpInStream.is() );
227 xTrunc->truncate();
228 ::comphelper::OStorageHelper::CopyInputToOutput( xTmpInStream, xOutStream );
229 xOutStream->closeOutput();
231 uno::Reference< embed::XTransactedObject > xTransact( xVersion, uno::UNO_QUERY );
232 DBG_ASSERT( xTransact.is(), "The storage must implement XTransacted interface!\n" );
233 if ( xTransact.is() )
234 xTransact->commit();
236 bResult = true;
238 catch( uno::Exception& )
240 // TODO/LATER: handle the error depending on exception
241 SetError(ERRCODE_IO_GENERAL);
244 return bResult;
248 OUString SfxObjectShell::CreateTempCopyOfStorage_Impl( const uno::Reference< embed::XStorage >& xStorage )
250 OUString aTempURL = ::utl::TempFile().GetURL();
252 DBG_ASSERT( !aTempURL.isEmpty(), "Can't create a temporary file!\n" );
253 if ( !aTempURL.isEmpty() )
257 uno::Reference< embed::XStorage > xTempStorage =
258 ::comphelper::OStorageHelper::GetStorageFromURL( aTempURL, embed::ElementModes::READWRITE );
260 // the password will be transferred from the xStorage to xTempStorage by storage implementation
261 xStorage->copyToStorage( xTempStorage );
263 // the temporary storage was committed by the previous method and it will die by refcount
265 catch ( uno::Exception& )
267 SAL_WARN( "sfx.doc", "Creation of a storage copy is failed!" );
268 ::utl::UCBContentHelper::Kill( aTempURL );
270 aTempURL.clear();
272 // TODO/LATER: may need error code setting based on exception
273 SetError(ERRCODE_IO_GENERAL);
277 return aTempURL;
281 SvGlobalName const & SfxObjectShell::GetClassName() const
283 return GetFactory().GetClassId();
287 void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xStorage,
288 sal_Int32 nVersion, bool bTemplate ) const
290 uno::Reference< beans::XPropertySet > xProps( xStorage, uno::UNO_QUERY );
292 if ( !xProps.is() )
293 return;
295 SotClipboardFormatId nClipFormat = SotClipboardFormatId::NONE;
297 SvGlobalName aName;
298 OUString aFullTypeName;
299 FillClass( &aName, &nClipFormat, &aFullTypeName, nVersion, bTemplate );
301 if ( nClipFormat == SotClipboardFormatId::NONE )
302 return;
304 // basic doesn't have a ClipFormat
305 // without MediaType the storage is not really usable, but currently the BasicIDE still
306 // is an SfxObjectShell and so we can't take this as an error
307 datatransfer::DataFlavor aDataFlavor;
308 SotExchange::GetFormatDataFlavor( nClipFormat, aDataFlavor );
309 if ( aDataFlavor.MimeType.isEmpty() )
310 return;
314 xProps->setPropertyValue("MediaType", uno::makeAny( aDataFlavor.MimeType ) );
316 catch( uno::Exception& )
318 const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
321 SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013;
322 if (!utl::ConfigManager::IsFuzzing())
324 nDefVersion = GetODFSaneDefaultVersion();
327 // the default values, that should be used for ODF1.1 and older formats
328 uno::Sequence< beans::NamedValue > aEncryptionAlgs
330 { "StartKeyGenerationAlgorithm", css::uno::makeAny(xml::crypto::DigestID::SHA1) },
331 { "EncryptionAlgorithm", css::uno::makeAny(xml::crypto::CipherID::BLOWFISH_CFB_8) },
332 { "ChecksumAlgorithm", css::uno::makeAny(xml::crypto::DigestID::SHA1_1K) }
335 if (nDefVersion >= SvtSaveOptions::ODFSVER_012)
339 // older versions can not have this property set, it exists only starting from ODF1.2
340 uno::Reference<frame::XModule> const xModule(GetModel(), uno::UNO_QUERY);
341 bool const isBaseForm(xModule.is() &&
342 xModule->getIdentifier() == "com.sun.star.sdb.FormDesign");
343 SAL_INFO_IF(isBaseForm, "sfx.doc", "tdf#138209 force form export to ODF 1.2");
344 if (!isBaseForm && SvtSaveOptions::ODFSVER_013 <= nDefVersion)
346 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_013_TEXT));
348 else
350 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_012_TEXT));
353 catch( uno::Exception& )
357 auto pEncryptionAlgs = aEncryptionAlgs.getArray();
358 pEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256;
359 pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
360 pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
365 // set the encryption algorithms accordingly;
366 // the setting does not trigger encryption,
367 // it just provides the format for the case that contents should be encrypted
368 uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xStorage, uno::UNO_QUERY_THROW );
369 xEncr->setEncryptionAlgorithms( aEncryptionAlgs );
371 catch( uno::Exception& )
373 const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
378 void SfxObjectShell::PrepareSecondTryLoad_Impl()
380 // only for internal use
381 pImpl->m_xDocStorage.clear();
382 pImpl->m_bIsInit = false;
383 ResetError();
387 bool SfxObjectShell::GeneralInit_Impl( const uno::Reference< embed::XStorage >& xStorage,
388 bool bTypeMustBeSetAlready )
390 if ( pImpl->m_bIsInit )
391 return false;
393 pImpl->m_bIsInit = true;
394 if ( xStorage.is() )
396 // no notification is required the storage is set the first time
397 pImpl->m_xDocStorage = xStorage;
399 try {
400 uno::Reference < beans::XPropertySet > xPropSet( xStorage, uno::UNO_QUERY_THROW );
401 Any a = xPropSet->getPropertyValue("MediaType");
402 OUString aMediaType;
403 if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
405 if ( bTypeMustBeSetAlready )
407 SetError(ERRCODE_IO_BROKENPACKAGE);
408 return false;
411 SetupStorage( xStorage, SOFFICE_FILEFORMAT_CURRENT, false );
414 catch ( uno::Exception& )
416 SAL_WARN( "sfx.doc", "Can't check storage's mediatype!" );
419 else
420 pImpl->m_bCreateTempStor = true;
422 return true;
426 bool SfxObjectShell::InitNew( const uno::Reference< embed::XStorage >& xStorage )
428 return GeneralInit_Impl( xStorage, false );
432 bool SfxObjectShell::Load( SfxMedium& rMedium )
434 return GeneralInit_Impl(rMedium.GetStorage(), !tools::isEmptyFileUrl(rMedium.GetName()));
437 void SfxObjectShell::DoInitUnitTest()
439 pMedium = new SfxMedium;
442 bool SfxObjectShell::DoInitNew( SfxMedium* pMed )
443 /* [Description]
445 This from SvPersist inherited virtual method is called to initialize
446 the SfxObjectShell instance from a storage (PStore! = 0) or (PStore == 0)
448 Like with all Do...-methods there is a from a control, the actual
449 implementation is done by the virtual method in which also the
450 InitNew(SvStorate *) from the SfxObjectShell-Subclass is implemented.
452 For pStore == 0 the SfxObjectShell-instance is connected to an empty
453 SfxMedium, otherwise a SfxMedium, which refers to the SotStorage
454 passed as a parameter.
456 The object is only initialized correctly after InitNew() or Load().
458 [Return value]
459 true The object has been initialized.
460 false The object could not be initialized
464 ModifyBlocker_Impl aBlock( this );
465 pMedium = pMed;
466 if ( !pMedium )
468 pMedium = new SfxMedium;
471 pMedium->CanDisposeStorage_Impl( true );
473 if ( InitNew( pMed ? pMed->GetStorage() : uno::Reference < embed::XStorage >() ) )
475 // empty documents always get their macros from the user, so there is no reason to restrict access
476 pImpl->aMacroMode.allowMacroExecution();
477 if ( SfxObjectCreateMode::EMBEDDED == eCreateMode )
478 SetTitle(SfxResId(STR_NONAME));
480 uno::Reference< frame::XModel > xModel = GetModel();
481 if ( xModel.is() )
483 SfxItemSet *pSet = GetMedium()->GetItemSet();
484 uno::Sequence< beans::PropertyValue > aArgs;
485 TransformItems( SID_OPENDOC, *pSet, aArgs );
486 sal_Int32 nLength = aArgs.getLength();
487 aArgs.realloc( nLength + 1 );
488 auto pArgs = aArgs.getArray();
489 pArgs[nLength].Name = "Title";
490 pArgs[nLength].Value <<= GetTitle( SFX_TITLE_DETECT );
491 xModel->attachResource( OUString(), aArgs );
492 if (!utl::ConfigManager::IsFuzzing())
493 impl_addToModelCollection(xModel);
496 SetInitialized_Impl( true );
497 return true;
500 return false;
503 bool SfxObjectShell::ImportFromGeneratedStream_Impl(
504 const uno::Reference< io::XStream >& xStream,
505 const uno::Sequence< beans::PropertyValue >& rMediaDescr )
507 if ( !xStream.is() )
508 return false;
510 if ( pMedium && pMedium->HasStorage_Impl() )
511 pMedium->CloseStorage();
513 bool bResult = false;
517 uno::Reference< embed::XStorage > xStorage =
518 ::comphelper::OStorageHelper::GetStorageFromStream( xStream );
520 if ( !xStorage.is() )
521 throw uno::RuntimeException();
523 if ( !pMedium )
524 pMedium = new SfxMedium( xStorage, OUString() );
525 else
526 pMedium->SetStorage_Impl( xStorage );
528 SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
529 TransformParameters( SID_OPENDOC, rMediaDescr, aSet );
530 pMedium->GetItemSet()->Put( aSet );
531 pMedium->CanDisposeStorage_Impl( false );
532 uno::Reference<text::XTextRange> xInsertTextRange;
533 for (const auto& rProp : rMediaDescr)
535 if (rProp.Name == "TextInsertModeRange")
537 rProp.Value >>= xInsertTextRange;
541 if (xInsertTextRange.is())
543 bResult = InsertGeneratedStream(*pMedium, xInsertTextRange);
545 else
548 // allow the subfilter to reinit the model
549 if ( pImpl->m_bIsInit )
550 pImpl->m_bIsInit = false;
552 if ( LoadOwnFormat( *pMedium ) )
554 bHasName = true;
555 if ( !IsReadOnly() && IsLoadReadonly() )
556 SetReadOnlyUI();
558 bResult = true;
559 OSL_ENSURE( pImpl->m_xDocStorage == xStorage, "Wrong storage is used!" );
563 // now the medium can be disconnected from the storage
564 // the medium is not allowed to dispose the storage so CloseStorage() can be used
565 pMedium->CloseStorage();
567 catch( uno::Exception& )
571 return bResult;
575 bool SfxObjectShell::DoLoad( SfxMedium *pMed )
577 ModifyBlocker_Impl aBlock( this );
579 pMedium = pMed;
580 pMedium->CanDisposeStorage_Impl( true );
582 bool bOk = false;
583 std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter();
584 SfxItemSet* pSet = pMedium->GetItemSet();
585 if( pImpl->nEventId == SfxEventHintId::NONE )
587 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pSet, SID_TEMPLATE, false);
588 SetActivateEvent_Impl(
589 ( pTemplateItem && pTemplateItem->GetValue() )
590 ? SfxEventHintId::CreateDoc : SfxEventHintId::OpenDoc );
593 const SfxStringItem* pBaseItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_BASEURL, false);
594 OUString aBaseURL;
595 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
596 if( pBaseItem )
597 aBaseURL = pBaseItem->GetValue();
598 else
600 if ( pSalvageItem )
602 osl::FileBase::getFileURLFromSystemPath( pMed->GetPhysicalName(), aBaseURL );
604 else
605 aBaseURL = pMed->GetBaseURL();
607 pMed->GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, aBaseURL ) );
609 pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
610 pImpl->bModelInitialized = false;
612 if (pFilter && !pFilter->IsEnabled())
614 SetError( ERRCODE_IO_FILTERDISABLED );
617 if ( pFilter && pFilter->IsExoticFormat() && !QueryAllowExoticFormat_Impl( getInteractionHandler(), aBaseURL, pMed->GetFilter()->GetUIName() ) )
619 SetError( ERRCODE_IO_ABORT );
622 // initialize static language table so language-related extensions are learned before the document loads
623 (void)SvtLanguageTable::GetLanguageEntryCount();
625 //TODO/LATER: make a clear strategy how to handle "UsesStorage" etc.
626 bool bOwnStorageFormat = IsOwnStorageFormat( *pMedium );
627 bool bHasStorage = IsPackageStorageFormat_Impl( *pMedium );
628 if ( pMedium->GetFilter() )
630 ErrCode nError = HandleFilter( pMedium, this );
631 if ( nError != ERRCODE_NONE )
632 SetError(nError);
634 if (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARTPRESENTATION)
635 pSet->Put( SfxBoolItem( SID_DOC_STARTPRESENTATION, true) );
638 EnableSetModified( false );
640 pMedium->LockOrigFileOnDemand( true, false );
641 if ( GetError() == ERRCODE_NONE && bOwnStorageFormat && ( !pFilter || !( pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) ) )
643 uno::Reference< embed::XStorage > xStorage;
644 if ( pMedium->GetError() == ERRCODE_NONE )
645 xStorage = pMedium->GetStorage();
647 if( xStorage.is() && pMedium->GetLastStorageCreationState() == ERRCODE_NONE )
649 DBG_ASSERT( pFilter, "No filter for storage found!" );
653 bool bWarnMediaTypeFallback = false;
654 const SfxBoolItem* pRepairPackageItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_REPAIRPACKAGE, false);
656 // treat the package as broken if the mediatype was retrieved as a fallback
657 uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
658 xStorProps->getPropertyValue("MediaTypeFallbackUsed")
659 >>= bWarnMediaTypeFallback;
661 if ( pRepairPackageItem && pRepairPackageItem->GetValue() )
663 // the macros in repaired documents should be disabled
664 pMedium->GetItemSet()->Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::NEVER_EXECUTE ) );
666 // the mediatype was retrieved by using fallback solution but this is a repairing mode
667 // so it is acceptable to open the document if there is no contents that required manifest.xml
668 bWarnMediaTypeFallback = false;
671 if (bWarnMediaTypeFallback
672 || (!tools::isEmptyFileUrl(pMedium->GetName())
673 && !xStorage->getElementNames().hasElements()))
674 SetError(ERRCODE_IO_BROKENPACKAGE);
676 catch( uno::Exception& )
678 // TODO/LATER: may need error code setting based on exception
679 SetError(ERRCODE_IO_GENERAL);
682 // Load
683 if ( !GetError() )
685 pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
686 pImpl->bModelInitialized = false;
687 bOk = xStorage.is() && LoadOwnFormat( *pMed );
688 if ( bOk )
690 // the document loaded from template has no name
691 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
692 if ( !pTemplateItem || !pTemplateItem->GetValue() )
693 bHasName = true;
695 else
696 SetError(ERRCODE_ABORT);
699 else
700 SetError(pMed->GetLastStorageCreationState());
702 else if ( GetError() == ERRCODE_NONE && InitNew(nullptr) )
704 // set name before ConvertFrom, so that GetSbxObject() already works
705 bHasName = true;
706 SetName( SfxResId(STR_NONAME) );
708 if( !bHasStorage )
709 pMedium->GetInStream();
710 else
711 pMedium->GetStorage();
713 if ( GetError() == ERRCODE_NONE )
715 // Experimental PDF importing using PDFium. This is currently enabled for LOK only and
716 // we handle it not via XmlFilterAdaptor but a new SdPdfFiler.
717 #if !HAVE_FEATURE_POPPLER
718 constexpr bool bUsePdfium = true;
719 #else
720 const bool bUsePdfium
721 = comphelper::LibreOfficeKit::isActive() || getenv("LO_IMPORT_USE_PDFIUM");
722 #endif
723 const bool bPdfiumImport
724 = bUsePdfium && pMedium->GetFilter()
725 && (pMedium->GetFilter()->GetFilterName() == "draw_pdf_import");
727 pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
728 pImpl->bModelInitialized = false;
729 if (pMedium->GetFilter()
730 && (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER)
731 && !bPdfiumImport)
733 uno::Reference < beans::XPropertySet > xSet( GetModel(), uno::UNO_QUERY );
734 static const OUStringLiteral sLockUpdates(u"LockUpdates");
735 bool bSetProperty = true;
738 xSet->setPropertyValue( sLockUpdates, makeAny( true ) );
740 catch(const beans::UnknownPropertyException& )
742 bSetProperty = false;
744 bOk = ImportFrom(*pMedium, nullptr);
745 if(bSetProperty)
749 xSet->setPropertyValue( sLockUpdates, makeAny( false ) );
751 catch(const beans::UnknownPropertyException& )
754 UpdateLinks();
755 FinishedLoading();
757 else
759 if (tools::isEmptyFileUrl(pMedium->GetName()))
761 // The import filter would fail with empty input.
762 bOk = true;
764 else
766 bOk = ConvertFrom(*pMedium);
768 InitOwnModel_Impl();
773 if ( bOk )
775 if ( IsReadOnlyMedium() || IsLoadReadonly() )
776 SetReadOnlyUI();
780 ::ucbhelper::Content aContent( pMedium->GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
781 css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties();
782 if ( xProps.is() )
784 static const OUStringLiteral aAuthor( u"Author" );
785 static const OUStringLiteral aKeywords( u"Keywords" );
786 static const OUStringLiteral aSubject( u"Subject" );
787 Any aAny;
788 OUString aValue;
789 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
790 GetModel(), uno::UNO_QUERY_THROW);
791 uno::Reference<document::XDocumentProperties> xDocProps
792 = xDPS->getDocumentProperties();
793 if ( xProps->hasPropertyByName( aAuthor ) )
795 aAny = aContent.getPropertyValue( aAuthor );
796 if ( aAny >>= aValue )
797 xDocProps->setAuthor(aValue);
799 if ( xProps->hasPropertyByName( aKeywords ) )
801 aAny = aContent.getPropertyValue( aKeywords );
802 if ( aAny >>= aValue )
803 xDocProps->setKeywords(
804 ::comphelper::string::convertCommaSeparated(aValue));
807 if ( xProps->hasPropertyByName( aSubject ) )
809 aAny = aContent.getPropertyValue( aSubject );
810 if ( aAny >>= aValue ) {
811 xDocProps->setSubject(aValue);
816 catch( Exception& )
820 // If not loaded asynchronously call FinishedLoading
821 if ( !( pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) &&
822 ( !pMedium->GetFilter() || pMedium->GetFilter()->UsesStorage() )
824 FinishedLoading( SfxLoadedFlags::MAINDOCUMENT );
826 Broadcast( SfxHint(SfxHintId::NameChanged) );
828 if ( SfxObjectCreateMode::EMBEDDED != eCreateMode )
830 const SfxBoolItem* pAsTempItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
831 const SfxBoolItem* pPreviewItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_PREVIEW, false);
832 const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_HIDDEN, false);
833 if( bOk && !pMedium->GetOrigURL().isEmpty()
834 && !( pAsTempItem && pAsTempItem->GetValue() )
835 && !( pPreviewItem && pPreviewItem->GetValue() )
836 && !( pHiddenItem && pHiddenItem->GetValue() ) )
838 AddToRecentlyUsedList();
842 const SfxBoolItem* pDdeReconnectItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_DDE_RECONNECT_ONLOAD, false);
844 bool bReconnectDde = true; // by default, we try to auto-connect DDE connections.
845 if (pDdeReconnectItem)
846 bReconnectDde = pDdeReconnectItem->GetValue();
848 if (bReconnectDde)
849 ReconnectDdeLinks(*this);
852 return bOk;
855 bool SfxObjectShell::DoLoadExternal( SfxMedium *pMed )
857 pMedium = pMed;
858 return LoadExternal(*pMedium);
861 ErrCode SfxObjectShell::HandleFilter( SfxMedium* pMedium, SfxObjectShell const * pDoc )
863 ErrCode nError = ERRCODE_NONE;
864 SfxItemSet* pSet = pMedium->GetItemSet();
865 const SfxStringItem* pOptions = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_FILE_FILTEROPTIONS, false);
866 const SfxUnoAnyItem* pData = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_FILTER_DATA, false);
867 const bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
868 if ( !pData && (bTiledRendering || !pOptions) )
870 css::uno::Reference< XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
871 css::uno::Reference< XNameAccess > xFilterCFG;
872 if( xServiceManager.is() )
874 xFilterCFG.set( xServiceManager->createInstance("com.sun.star.document.FilterFactory"),
875 UNO_QUERY );
878 if( xFilterCFG.is() )
880 try {
881 bool bAbort = false;
882 std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter();
883 Sequence < PropertyValue > aProps;
884 Any aAny = xFilterCFG->getByName( pFilter->GetName() );
885 if ( aAny >>= aProps )
887 auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
888 [](const PropertyValue& rProp) { return rProp.Name == "UIComponent"; });
889 if (pProp != std::cend(aProps))
891 OUString aServiceName;
892 pProp->Value >>= aServiceName;
893 if( !aServiceName.isEmpty() )
895 css::uno::Reference< XInteractionHandler > rHandler = pMedium->GetInteractionHandler();
896 if( rHandler.is() )
898 // we need some properties in the media descriptor, so we have to make sure that they are in
899 Any aStreamAny;
900 aStreamAny <<= pMedium->GetInputStream();
901 if ( pSet->GetItemState( SID_INPUTSTREAM ) < SfxItemState::SET )
902 pSet->Put( SfxUnoAnyItem( SID_INPUTSTREAM, aStreamAny ) );
903 if ( pSet->GetItemState( SID_FILE_NAME ) < SfxItemState::SET )
904 pSet->Put( SfxStringItem( SID_FILE_NAME, pMedium->GetName() ) );
905 if ( pSet->GetItemState( SID_FILTER_NAME ) < SfxItemState::SET )
906 pSet->Put( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
908 Sequence< PropertyValue > rProperties;
909 TransformItems( SID_OPENDOC, *pSet, rProperties );
910 rtl::Reference<RequestFilterOptions> pFORequest = new RequestFilterOptions( pDoc->GetModel(), rProperties );
912 rHandler->handle( pFORequest );
914 if ( !pFORequest->isAbort() )
916 SfxAllItemSet aNewParams( pDoc->GetPool() );
917 TransformParameters( SID_OPENDOC,
918 pFORequest->getFilterOptions(),
919 aNewParams );
921 const SfxStringItem* pFilterOptions = aNewParams.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
922 if ( pFilterOptions )
923 pSet->Put( *pFilterOptions );
925 const SfxUnoAnyItem* pFilterData = aNewParams.GetItem<SfxUnoAnyItem>(SID_FILTER_DATA, false);
926 if ( pFilterData )
927 pSet->Put( *pFilterData );
929 else
930 bAbort = true;
936 if( bAbort )
938 // filter options were not entered
939 nError = ERRCODE_ABORT;
942 catch( NoSuchElementException& )
944 // the filter name is unknown
945 nError = ERRCODE_IO_INVALIDPARAMETER;
947 catch( Exception& )
949 nError = ERRCODE_ABORT;
954 return nError;
958 bool SfxObjectShell::IsOwnStorageFormat(const SfxMedium &rMedium)
960 return !rMedium.GetFilter() || // Embedded
961 ( rMedium.GetFilter()->IsOwnFormat() &&
962 rMedium.GetFilter()->UsesStorage() &&
963 rMedium.GetFilter()->GetVersion() >= SOFFICE_FILEFORMAT_60 );
967 bool SfxObjectShell::IsPackageStorageFormat_Impl(const SfxMedium &rMedium)
969 return !rMedium.GetFilter() || // Embedded
970 ( rMedium.GetFilter()->UsesStorage() &&
971 rMedium.GetFilter()->GetVersion() >= SOFFICE_FILEFORMAT_60 );
975 bool SfxObjectShell::DoSave()
976 // DoSave is only invoked for OLE. Save your own documents in the SFX through
977 // DoSave_Impl order to allow for the creation of backups.
978 // Save in your own format again.
980 bool bOk = false ;
982 ModifyBlocker_Impl aBlock( this );
984 pImpl->bIsSaving = true;
986 if (IsOwnStorageFormat(*GetMedium()))
988 SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013;
989 if (!utl::ConfigManager::IsFuzzing())
991 nDefVersion = GetODFSaneDefaultVersion();
993 uno::Reference<beans::XPropertySet> const xProps(GetMedium()->GetStorage(), uno::UNO_QUERY);
994 assert(xProps.is());
995 if (nDefVersion >= SvtSaveOptions::ODFSVER_012) // property exists only since ODF 1.2
997 try // tdf#134582 set Version on embedded objects as they
998 { // could have been loaded with a different/old version
999 uno::Reference<frame::XModule> const xModule(GetModel(), uno::UNO_QUERY);
1000 bool const isBaseForm(xModule.is() &&
1001 xModule->getIdentifier() == "com.sun.star.sdb.FormDesign");
1002 SAL_INFO_IF(isBaseForm, "sfx.doc", "tdf#138209 force form export to ODF 1.2");
1003 if (!isBaseForm && SvtSaveOptions::ODFSVER_013 <= nDefVersion)
1005 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_013_TEXT));
1007 else
1009 xProps->setPropertyValue("Version", uno::makeAny<OUString>(ODFVER_012_TEXT));
1012 catch (uno::Exception&)
1014 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::DoSave");
1019 uno::Sequence< beans::NamedValue > aEncryptionData;
1020 if ( IsPackageStorageFormat_Impl( *GetMedium() ) )
1022 if ( GetEncryptionData_Impl( GetMedium()->GetItemSet(), aEncryptionData ) )
1026 //TODO/MBA: GetOutputStorage?! Special mode, because it's "Save"?!
1027 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( GetMedium()->GetStorage(), aEncryptionData );
1028 bOk = true;
1030 catch( uno::Exception& )
1032 SetError(ERRCODE_IO_GENERAL);
1035 DBG_ASSERT( bOk, "The root storage must allow to set common password!\n" );
1037 else
1038 bOk = true;
1039 #if HAVE_FEATURE_SCRIPTING
1040 if ( HasBasic() )
1044 // The basic and dialogs related contents are still not able to proceed with save operation ( saveTo only )
1045 // so since the document storage is locked a workaround has to be used
1047 uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
1048 DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" );
1049 if ( !xTmpStorage.is() )
1050 throw uno::RuntimeException();
1052 static const OUStringLiteral aBasicStorageName( u"Basic" );
1053 static const OUStringLiteral aDialogsStorageName( u"Dialogs" );
1054 if ( GetMedium()->GetStorage()->hasByName( aBasicStorageName ) )
1055 GetMedium()->GetStorage()->copyElementTo( aBasicStorageName, xTmpStorage, aBasicStorageName );
1056 if ( GetMedium()->GetStorage()->hasByName( aDialogsStorageName ) )
1057 GetMedium()->GetStorage()->copyElementTo( aDialogsStorageName, xTmpStorage, aDialogsStorageName );
1059 GetBasicManager();
1061 // disconnect from the current storage
1062 pImpl->aBasicManager.setStorage( xTmpStorage );
1064 // store to the current storage
1065 pImpl->aBasicManager.storeLibrariesToStorage( GetMedium()->GetStorage() );
1067 // connect to the current storage back
1068 pImpl->aBasicManager.setStorage( GetMedium()->GetStorage() );
1070 catch( uno::Exception& )
1072 SetError(ERRCODE_IO_GENERAL);
1073 bOk = false;
1076 #endif
1079 if (bOk)
1080 bOk = Save();
1082 if (bOk)
1083 bOk = pMedium->Commit();
1086 return bOk;
1089 namespace
1091 class LockUIGuard
1093 public:
1094 LockUIGuard(SfxObjectShell const* pDoc)
1095 : m_pDoc(pDoc)
1097 Lock_Impl();
1099 ~LockUIGuard() { Unlock(); }
1101 void Unlock()
1103 if (m_bUnlock)
1104 Lock_Impl();
1107 private:
1108 void Lock_Impl()
1110 SfxViewFrame* pFrame = SfxViewFrame::GetFirst(m_pDoc);
1111 while (pFrame)
1113 pFrame->GetDispatcher()->Lock(!m_bUnlock);
1114 pFrame->Enable(m_bUnlock);
1115 pFrame = SfxViewFrame::GetNext(*pFrame, m_pDoc);
1117 m_bUnlock = !m_bUnlock;
1119 SfxObjectShell const* m_pDoc;
1120 bool m_bUnlock = false;
1124 static OUString lcl_strip_template(const OUString &aString)
1126 static constexpr OUStringLiteral sPostfix(u"_template");
1127 OUString sRes(aString);
1128 if (sRes.endsWith(sPostfix))
1129 sRes = sRes.copy(0, sRes.getLength() - sPostfix.getLength());
1130 return sRes;
1133 bool SfxObjectShell::SaveTo_Impl
1135 SfxMedium &rMedium, // Medium, in which it will be stored
1136 const SfxItemSet* pSet
1139 /* [Description]
1141 Writes the current contents to the medium rMedium. If the target medium is
1142 no storage, then saving to a temporary storage, or directly if the medium
1143 is transacted, if we ourselves have opened it, and if we are a server
1144 either the container a transacted storage provides or created a
1145 temporary storage by one self.
1149 SAL_INFO( "sfx.doc", "saving \"" << rMedium.GetName() << "\"" );
1151 UpdateDocInfoForSave();
1153 ModifyBlocker_Impl aMod(this);
1154 // tdf#41063, tdf#135244: prevent jumping to cursor at any temporary modification
1155 auto aViewGuard(LockAllViews());
1157 std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
1158 if ( !pFilter )
1160 // if no filter was set, use the default filter
1161 // this should be changed in the feature, it should be an error!
1162 SAL_WARN( "sfx.doc","No filter set!");
1163 pFilter = GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT );
1164 rMedium.SetFilter(pFilter);
1167 bool bStorageBasedSource = IsPackageStorageFormat_Impl( *pMedium );
1168 bool bStorageBasedTarget = IsPackageStorageFormat_Impl( rMedium );
1169 bool bOwnSource = IsOwnStorageFormat( *pMedium );
1170 bool bOwnTarget = IsOwnStorageFormat( rMedium );
1172 // Examine target format to determine whether to query if any password
1173 // protected libraries exceed the size we can handler
1174 if ( bOwnTarget && !QuerySaveSizeExceededModules_Impl( rMedium.GetInteractionHandler() ) )
1176 SetError(ERRCODE_IO_ABORT);
1177 return false;
1180 bool bNeedsDisconnectionOnFail = false;
1182 bool bStoreToSameLocation = false;
1184 // the detection whether the script is changed should be done before saving
1185 bool bTryToPreserveScriptSignature = false;
1186 // no way to detect whether a filter is oasis format, have to wait for saving process
1187 bool bNoPreserveForOasis = false;
1188 if ( bOwnSource && bOwnTarget
1189 && ( pImpl->nScriptingSignatureState == SignatureState::OK
1190 || pImpl->nScriptingSignatureState == SignatureState::NOTVALIDATED
1191 || pImpl->nScriptingSignatureState == SignatureState::INVALID ) )
1193 // the checking of the library modified state iterates over the libraries, should be done only when required
1194 // currently the check is commented out since it is broken, we have to check the signature every time we save
1195 // TODO/LATER: let isAnyContainerModified() work!
1196 bTryToPreserveScriptSignature = true; // !pImpl->pBasicManager->isAnyContainerModified();
1197 if ( bTryToPreserveScriptSignature )
1199 // check that the storage format stays the same
1200 SvtSaveOptions::ODFSaneDefaultVersion nVersion = GetODFSaneDefaultVersion();
1202 OUString aODFVersion;
1205 uno::Reference < beans::XPropertySet > xPropSet( GetStorage(), uno::UNO_QUERY_THROW );
1206 xPropSet->getPropertyValue("Version") >>= aODFVersion;
1208 catch( uno::Exception& )
1211 // preserve only if the same filter has been used
1212 // for templates, strip the _template from the filter name for comparison
1213 const OUString aMediumFilter = lcl_strip_template(pMedium->GetFilter()->GetFilterName());
1214 bTryToPreserveScriptSignature = pMedium->GetFilter() && pFilter && aMediumFilter == lcl_strip_template(pFilter->GetFilterName());
1216 // signatures were specified in ODF 1.2 but were used since much longer.
1217 // LO will still correctly validate an old style signature on an ODF 1.2
1218 // document, but technically this is not correct, so this prevents old
1219 // signatures to be copied over to a version 1.2 document
1220 bNoPreserveForOasis = (
1221 (0 <= aODFVersion.compareTo(ODFVER_012_TEXT) && nVersion < SvtSaveOptions::ODFSVER_012) ||
1222 (aODFVersion.isEmpty() && nVersion >= SvtSaveOptions::ODFSVER_012)
1227 // use UCB for case sensitive/insensitive file name comparison
1228 if ( !pMedium->GetName().equalsIgnoreAsciiCase("private:stream")
1229 && !rMedium.GetName().equalsIgnoreAsciiCase("private:stream")
1230 && ::utl::UCBContentHelper::EqualURLs( pMedium->GetName(), rMedium.GetName() ) )
1232 // Do not unlock the file during saving.
1233 // need to modify this for WebDAV if this method is called outside of
1234 // the process of saving a file
1235 pMedium->DisableUnlockWebDAV();
1236 bStoreToSameLocation = true;
1238 if ( pMedium->DocNeedsFileDateCheck() )
1239 rMedium.CheckFileDate( pMedium->GetInitFileDate( false ) );
1241 // before we overwrite the original file, we will make a backup if there is a demand for that
1242 // if the backup is not created here it will be created internally and will be removed in case of successful saving
1243 const bool bDoBackup = officecfg::Office::Common::Save::Document::CreateBackup::get();
1244 if ( bDoBackup )
1246 rMedium.DoBackup_Impl();
1247 if ( rMedium.GetError() )
1249 SetError(rMedium.GetErrorCode());
1250 rMedium.ResetError();
1254 if ( bStorageBasedSource && bStorageBasedTarget )
1256 // The active storage must be switched. The simple saving is not enough.
1257 // The problem is that the target medium contains target MediaDescriptor.
1259 // In future the switch of the persistence could be done on stream level:
1260 // a new wrapper service will be implemented that allows to exchange
1261 // persistence on the fly. So the real persistence will be set
1262 // to that stream only after successful commit of the storage.
1263 // TODO/LATER:
1264 // create wrapper stream based on the URL
1265 // create a new storage based on this stream
1266 // store to this new storage
1267 // commit the new storage
1268 // call saveCompleted based with this new storage ( get rid of old storage and "frees" URL )
1269 // commit the wrapper stream ( the stream will connect the URL only on commit, after that it will hold it )
1270 // if the last step is failed the stream should stay to be transacted and should be committed on any flush
1271 // so we can forget the stream in any way and the next storage commit will flush it
1273 bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
1274 *pMedium, rMedium );
1275 if ( bNeedsDisconnectionOnFail
1276 || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
1278 pMedium->CloseAndRelease();
1280 // TODO/LATER: for now the medium must be closed since it can already contain streams from old medium
1281 // in future those streams should not be copied in case a valid target url is provided,
1282 // if the url is not provided ( means the document is based on a stream ) this code is not
1283 // reachable.
1284 rMedium.CloseAndRelease();
1285 rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
1286 rMedium.GetOutputStorage();
1287 rMedium.SetHasEmbeddedObjects(false);
1290 else if ( !bStorageBasedSource && !bStorageBasedTarget )
1292 // the source and the target formats are alien
1293 // just disconnect the stream from the source format
1294 // so that the target medium can use it
1296 pMedium->CloseAndRelease();
1297 rMedium.CloseAndRelease();
1298 rMedium.CreateTempFileNoCopy();
1299 rMedium.GetOutStream();
1301 else if ( !bStorageBasedSource && bStorageBasedTarget )
1303 // the source format is an alien one but the target
1304 // format is an own one so just disconnect the source
1305 // medium
1307 pMedium->CloseAndRelease();
1308 rMedium.CloseAndRelease();
1309 rMedium.GetOutputStorage();
1311 else // means if ( bStorageBasedSource && !bStorageBasedTarget )
1313 // the source format is an own one but the target is
1314 // an alien format, just connect the source to temporary
1315 // storage
1317 bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
1318 *pMedium, rMedium );
1319 if ( bNeedsDisconnectionOnFail
1320 || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
1322 pMedium->CloseAndRelease();
1323 rMedium.CloseAndRelease();
1324 rMedium.CreateTempFileNoCopy();
1325 rMedium.GetOutStream();
1328 pMedium->DisableUnlockWebDAV(false);
1330 else
1332 // This is SaveAs or export action, prepare the target medium
1333 // the alien filters still might write directly to the file, that is of course a bug,
1334 // but for now the framework has to be ready for it
1335 // TODO/LATER: let the medium be prepared for alien formats as well
1337 rMedium.CloseAndRelease();
1338 if ( bStorageBasedTarget )
1340 rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
1341 rMedium.GetOutputStorage();
1342 rMedium.SetHasEmbeddedObjects(false);
1346 // TODO/LATER: error handling
1347 if( rMedium.GetErrorCode() || pMedium->GetErrorCode() || GetErrorCode() )
1349 SAL_WARN("sfx.doc", "SfxObjectShell::SaveTo_Impl: very early error return");
1350 return false;
1353 rMedium.LockOrigFileOnDemand( false, false );
1355 if ( bStorageBasedTarget )
1357 if ( rMedium.GetErrorCode() )
1358 return false;
1360 // If the filter is a "cross export" filter ( f.e. a filter for exporting an impress document from
1361 // a draw document ), the ClassId of the destination storage is different from the ClassId of this
1362 // document. It can be retrieved from the default filter for the desired target format
1363 SotClipboardFormatId nFormat = rMedium.GetFilter()->GetFormat();
1364 SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
1365 std::shared_ptr<const SfxFilter> pFilt = rMatcher.GetFilter4ClipBoardId( nFormat );
1366 if ( pFilt )
1368 if ( pFilt->GetServiceName() != rMedium.GetFilter()->GetServiceName() )
1370 datatransfer::DataFlavor aDataFlavor;
1371 SotExchange::GetFormatDataFlavor( nFormat, aDataFlavor );
1375 uno::Reference< beans::XPropertySet > xProps( rMedium.GetStorage(), uno::UNO_QUERY_THROW );
1376 xProps->setPropertyValue("MediaType",
1377 uno::makeAny( aDataFlavor.MimeType ) );
1379 catch( uno::Exception& )
1386 // TODO/LATER: error handling
1387 if( rMedium.GetErrorCode() || pMedium->GetErrorCode() || GetErrorCode() )
1388 return false;
1390 bool bOldStat = pImpl->bForbidReload;
1391 pImpl->bForbidReload = true;
1393 // lock user interface while saving the document
1394 LockUIGuard aLockUIGuard(this);
1396 bool bCopyTo = false;
1397 SfxItemSet *pMedSet = rMedium.GetItemSet();
1398 if( pMedSet )
1400 const SfxBoolItem* pSaveToItem = SfxItemSet::GetItem<SfxBoolItem>(pMedSet, SID_SAVETO, false);
1401 bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED ||
1402 (pSaveToItem && pSaveToItem->GetValue());
1405 bool bOk = false;
1406 // TODO/LATER: get rid of bOk
1407 if (bOwnTarget && pFilter && !(pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER))
1409 uno::Reference< embed::XStorage > xMedStorage = rMedium.GetStorage();
1410 if ( !xMedStorage.is() )
1412 // no saving without storage
1413 pImpl->bForbidReload = bOldStat;
1414 return false;
1417 // transfer password from the parameters to the storage
1418 uno::Sequence< beans::NamedValue > aEncryptionData;
1419 bool bPasswdProvided = false;
1420 if ( GetEncryptionData_Impl( rMedium.GetItemSet(), aEncryptionData ) )
1422 bPasswdProvided = true;
1423 try {
1424 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );
1425 bOk = true;
1427 catch( uno::Exception& )
1429 SAL_WARN( "sfx.doc", "Setting of common encryption key failed!" );
1430 SetError(ERRCODE_IO_GENERAL);
1433 else
1434 bOk = true;
1436 pFilter = rMedium.GetFilter();
1438 const SfxStringItem *pVersionItem = !rMedium.IsInCheckIn()? SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_COMMENTS, false): nullptr;
1439 OUString aTmpVersionURL;
1441 if ( bOk )
1443 bOk = false;
1444 // currently the case that the storage is the same should be impossible
1445 if ( xMedStorage == GetStorage() )
1447 OSL_ENSURE( !pVersionItem, "This scenario is impossible currently!" );
1448 // usual save procedure
1449 bOk = Save();
1451 else
1453 // save to target
1454 bOk = SaveAsOwnFormat( rMedium );
1455 if ( bOk && pVersionItem )
1457 aTmpVersionURL = CreateTempCopyOfStorage_Impl( xMedStorage );
1458 bOk = !aTmpVersionURL.isEmpty();
1463 //fdo#61320: only store thumbnail image if the corresponding option is enabled in the configuration
1464 if ( bOk && officecfg::Office::Common::Save::Document::GenerateThumbnail::get()
1465 && GetCreateMode() != SfxObjectCreateMode::EMBEDDED && !bPasswdProvided && IsUseThumbnailSave() )
1467 // store the thumbnail representation image
1468 // the thumbnail is not stored in case of encrypted document
1469 if ( !GenerateAndStoreThumbnail( bPasswdProvided, xMedStorage ) )
1471 // TODO: error handling
1472 SAL_WARN( "sfx.doc", "Couldn't store thumbnail representation!" );
1476 if ( bOk )
1478 if ( pImpl->bIsSaving || pImpl->bPreserveVersions )
1482 const Sequence < util::RevisionTag > aVersions = rMedium.GetVersionList();
1483 if ( aVersions.hasElements() )
1485 // copy the version streams
1486 static const OUStringLiteral aVersionsName( u"Versions" );
1487 uno::Reference< embed::XStorage > xNewVerStor = xMedStorage->openStorageElement(
1488 aVersionsName,
1489 embed::ElementModes::READWRITE );
1490 uno::Reference< embed::XStorage > xOldVerStor = GetStorage()->openStorageElement(
1491 aVersionsName,
1492 embed::ElementModes::READ );
1493 if ( !xNewVerStor.is() || !xOldVerStor.is() )
1494 throw uno::RuntimeException();
1496 for ( const auto& rVersion : aVersions )
1498 if ( xOldVerStor->hasByName( rVersion.Identifier ) )
1499 xOldVerStor->copyElementTo( rVersion.Identifier, xNewVerStor, rVersion.Identifier );
1502 uno::Reference< embed::XTransactedObject > xTransact( xNewVerStor, uno::UNO_QUERY );
1503 if ( xTransact.is() )
1504 xTransact->commit();
1507 catch( uno::Exception& )
1509 SAL_WARN( "sfx.doc", "Couldn't copy versions!" );
1510 bOk = false;
1511 // TODO/LATER: a specific error could be set
1515 if ( bOk && pVersionItem && !rMedium.IsInCheckIn() )
1517 // store a version also
1518 const SfxStringItem *pAuthorItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_AUTHOR, false);
1520 // version comment
1521 util::RevisionTag aInfo;
1522 aInfo.Comment = pVersionItem->GetValue();
1524 // version author
1525 if ( pAuthorItem )
1526 aInfo.Author = pAuthorItem->GetValue();
1527 else
1528 // if not transferred as a parameter, get it from user settings
1529 aInfo.Author = SvtUserOptions().GetFullName();
1531 DateTime aTime( DateTime::SYSTEM );
1532 aInfo.TimeStamp.Day = aTime.GetDay();
1533 aInfo.TimeStamp.Month = aTime.GetMonth();
1534 aInfo.TimeStamp.Year = aTime.GetYear();
1535 aInfo.TimeStamp.Hours = aTime.GetHour();
1536 aInfo.TimeStamp.Minutes = aTime.GetMin();
1537 aInfo.TimeStamp.Seconds = aTime.GetSec();
1539 // add new version information into the versionlist and save the versionlist
1540 // the version list must have been transferred from the "old" medium before
1541 rMedium.AddVersion_Impl(aInfo);
1542 rMedium.SaveVersionList_Impl();
1543 bOk = PutURLContentsToVersionStream_Impl(aTmpVersionURL, xMedStorage,
1544 aInfo.Identifier);
1546 else if ( bOk && ( pImpl->bIsSaving || pImpl->bPreserveVersions ) )
1548 rMedium.SaveVersionList_Impl();
1552 if ( !aTmpVersionURL.isEmpty() )
1553 ::utl::UCBContentHelper::Kill( aTmpVersionURL );
1555 else
1557 // it's a "SaveAs" in an alien format
1558 if ( rMedium.GetFilter() && ( rMedium.GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) )
1559 bOk = ExportTo( rMedium );
1560 else
1561 bOk = ConvertTo( rMedium );
1563 // after saving the document, the temporary object storage must be updated
1564 // if the old object storage was not a temporary one, it will be updated also, because it will be used
1565 // as a source for copying the objects into the new temporary storage that will be created below
1566 // updating means: all child objects must be stored into it
1567 // ( same as on loading, where these objects are copied to the temporary storage )
1568 // but don't commit these changes, because in the case when the old object storage is not a temporary one,
1569 // all changes will be written into the original file !
1571 if( bOk && !bCopyTo )
1572 // we also don't touch any graphical replacements here
1573 SaveChildren( true );
1576 if ( bOk )
1578 // if ODF version of oasis format changes on saving the signature should not be preserved
1579 if ( bTryToPreserveScriptSignature && bNoPreserveForOasis )
1580 bTryToPreserveScriptSignature = ( SotStorage::GetVersion( rMedium.GetStorage() ) == SOFFICE_FILEFORMAT_60 );
1582 uno::Reference< security::XDocumentDigitalSignatures > xDDSigns;
1583 if (bTryToPreserveScriptSignature)
1585 // if the scripting code was not changed and it is signed the signature should be preserved
1586 // unfortunately at this point we have only information whether the basic code has changed or not
1587 // so the only way is to check the signature if the basic was not changed
1590 // get the ODF version of the new medium
1591 OUString aVersion;
1594 uno::Reference < beans::XPropertySet > xPropSet( rMedium.GetStorage(), uno::UNO_QUERY_THROW );
1595 xPropSet->getPropertyValue("Version") >>= aVersion;
1597 catch( uno::Exception& )
1601 xDDSigns = security::DocumentDigitalSignatures::createWithVersion(comphelper::getProcessComponentContext(), aVersion);
1603 const OUString aScriptSignName = xDDSigns->getScriptingContentSignatureDefaultStreamName();
1605 if ( !aScriptSignName.isEmpty() )
1607 // target medium is still not committed, it should not be closed
1608 // commit the package storage and close it, but leave the streams open
1609 rMedium.StorageCommit_Impl();
1610 rMedium.CloseStorage();
1612 uno::Reference< embed::XStorage > xReadOrig = pMedium->GetZipStorageToSign_Impl();
1613 if ( !xReadOrig.is() )
1614 throw uno::RuntimeException();
1615 uno::Reference< embed::XStorage > xMetaInf = xReadOrig->openStorageElement(
1616 "META-INF",
1617 embed::ElementModes::READ );
1619 uno::Reference< embed::XStorage > xTarget = rMedium.GetZipStorageToSign_Impl( false );
1620 if ( !xTarget.is() )
1621 throw uno::RuntimeException();
1622 uno::Reference< embed::XStorage > xTargetMetaInf = xTarget->openStorageElement(
1623 "META-INF",
1624 embed::ElementModes::READWRITE );
1626 if ( xMetaInf.is() && xTargetMetaInf.is() )
1628 xMetaInf->copyElementTo( aScriptSignName, xTargetMetaInf, aScriptSignName );
1630 uno::Reference< embed::XTransactedObject > xTransact( xTargetMetaInf, uno::UNO_QUERY );
1631 if ( xTransact.is() )
1632 xTransact->commit();
1634 xTargetMetaInf->dispose();
1636 // now check the copied signature
1637 uno::Sequence< security::DocumentSignatureInformation > aInfos =
1638 xDDSigns->verifyScriptingContentSignatures( xTarget,
1639 uno::Reference< io::XInputStream >() );
1640 SignatureState nState = DocumentSignatures::getSignatureState(aInfos);
1641 if ( nState == SignatureState::OK || nState == SignatureState::NOTVALIDATED
1642 || nState == SignatureState::PARTIAL_OK)
1644 rMedium.SetCachedSignatureState_Impl( nState );
1646 // commit the ZipStorage from target medium
1647 xTransact.set( xTarget, uno::UNO_QUERY );
1648 if ( xTransact.is() )
1649 xTransact->commit();
1651 else
1653 // it should not happen, the copies signature is invalid!
1654 // throw the changes away
1655 SAL_WARN( "sfx.doc", "An invalid signature was copied!" );
1660 catch( uno::Exception& )
1664 rMedium.CloseZipStorage_Impl();
1667 const OUString sName( rMedium.GetName( ) );
1668 bOk = rMedium.Commit();
1669 const OUString sNewName( rMedium.GetName( ) );
1671 if ( sName != sNewName )
1672 GetMedium( )->SwitchDocumentToFile( sNewName );
1674 if ( bOk )
1676 // if the target medium is an alien format and the "old" medium was an own format and the "old" medium
1677 // has a name, the object storage must be exchanged, because now we need a new temporary storage
1678 // as object storage
1679 if ( !bCopyTo && bStorageBasedSource && !bStorageBasedTarget )
1681 if ( bStoreToSameLocation )
1683 // if the old medium already disconnected from document storage, the storage still must
1684 // be switched if backup file is used
1685 if ( bNeedsDisconnectionOnFail )
1686 ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
1688 else if (!pMedium->GetName().isEmpty()
1689 || ( pMedium->HasStorage_Impl() && pMedium->WillDisposeStorageOnClose_Impl() ) )
1691 OSL_ENSURE(!pMedium->GetName().isEmpty(), "Fallback is used, the medium without name should not dispose the storage!");
1692 // copy storage of old medium to new temporary storage and take this over
1693 if( !ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
1695 SAL_WARN( "sfx.doc", "Process after storing has failed." );
1696 bOk = false;
1701 else
1703 SAL_WARN( "sfx.doc", "Storing has failed." );
1705 // in case the document storage was connected to backup temporarily it must be disconnected now
1706 if ( bNeedsDisconnectionOnFail )
1707 ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
1711 // unlock user interface
1712 aLockUIGuard.Unlock();
1713 pImpl->bForbidReload = bOldStat;
1715 if ( bOk )
1719 ::ucbhelper::Content aContent( rMedium.GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
1720 css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties();
1721 if ( xProps.is() )
1723 static const OUStringLiteral aAuthor( u"Author" );
1724 static const OUStringLiteral aKeywords( u"Keywords" );
1725 static const OUStringLiteral aSubject( u"Subject" );
1727 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1728 GetModel(), uno::UNO_QUERY_THROW);
1729 uno::Reference<document::XDocumentProperties> xDocProps
1730 = xDPS->getDocumentProperties();
1732 if ( xProps->hasPropertyByName( aAuthor ) )
1734 aContent.setPropertyValue( aAuthor, Any(xDocProps->getAuthor()) );
1736 if ( xProps->hasPropertyByName( aKeywords ) )
1738 Any aAny;
1739 aAny <<= ::comphelper::string::convertCommaSeparated(
1740 xDocProps->getKeywords());
1741 aContent.setPropertyValue( aKeywords, aAny );
1743 if ( xProps->hasPropertyByName( aSubject ) )
1745 aContent.setPropertyValue( aSubject, Any(xDocProps->getSubject()) );
1749 catch( Exception& )
1754 return bOk;
1757 bool SfxObjectShell::DisconnectStorage_Impl( SfxMedium& rSrcMedium, SfxMedium& rTargetMedium )
1759 // this method disconnects the storage from source medium, and attaches it to the backup created by the target medium
1761 uno::Reference< embed::XStorage > xStorage = rSrcMedium.GetStorage();
1763 bool bResult = false;
1764 if ( xStorage == pImpl->m_xDocStorage )
1768 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
1769 const OUString aBackupURL = rTargetMedium.GetBackup_Impl();
1770 if ( aBackupURL.isEmpty() )
1772 // the backup could not be created, try to disconnect the storage and close the source SfxMedium
1773 // in this case the optimization is not possible, connect storage to a temporary file
1774 rTargetMedium.ResetError();
1775 xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
1776 rSrcMedium.CanDisposeStorage_Impl( false );
1777 rSrcMedium.Close();
1779 // now try to create the backup
1780 rTargetMedium.GetBackup_Impl();
1782 else
1784 // the following call will only compare stream sizes
1785 // TODO/LATER: this is a very risky part, since if the URL contents are different from the storage
1786 // contents, the storage will be broken
1787 xOptStorage->attachToURL( aBackupURL, true );
1789 // the storage is successfully attached to backup, thus it is owned by the document not by the medium
1790 rSrcMedium.CanDisposeStorage_Impl( false );
1791 bResult = true;
1794 catch ( uno::Exception& )
1797 return bResult;
1801 bool SfxObjectShell::ConnectTmpStorage_Impl(
1802 const uno::Reference< embed::XStorage >& xStorage,
1803 SfxMedium* pMediumArg )
1805 /* [Description]
1807 If the application operates on a temporary storage, then it may not take
1808 the temporary storage from the SaveCompleted. Therefore the new storage
1809 is connected already here in this case and SaveCompleted then does nothing.
1813 bool bResult = false;
1815 if ( xStorage.is() )
1819 // the empty argument means that the storage will create temporary stream itself
1820 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
1821 xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
1823 // the storage is successfully disconnected from the original sources, thus the medium must not dispose it
1824 if ( pMediumArg )
1825 pMediumArg->CanDisposeStorage_Impl( false );
1827 bResult = true;
1829 catch( uno::Exception& )
1833 // if switching of the storage does not work for any reason ( nonroot storage for example ) use the old method
1834 if ( !bResult ) try
1836 uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
1838 DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" );
1839 if ( !xTmpStorage.is() )
1840 throw uno::RuntimeException();
1842 // TODO/LATER: may be it should be done in SwitchPersistence also
1843 // TODO/LATER: find faster way to copy storage; perhaps sharing with backup?!
1844 xStorage->copyToStorage( xTmpStorage );
1845 bResult = SaveCompleted( xTmpStorage );
1847 if ( bResult )
1849 pImpl->aBasicManager.setStorage( xTmpStorage );
1851 // Get rid of this workaround after issue i113914 is fixed
1854 uno::Reference< script::XStorageBasedLibraryContainer > xBasicLibraries( pImpl->xBasicLibraries, uno::UNO_QUERY_THROW );
1855 xBasicLibraries->setRootStorage( xTmpStorage );
1857 catch( uno::Exception& )
1861 uno::Reference< script::XStorageBasedLibraryContainer > xDialogLibraries( pImpl->xDialogLibraries, uno::UNO_QUERY_THROW );
1862 xDialogLibraries->setRootStorage( xTmpStorage );
1864 catch( uno::Exception& )
1868 catch( uno::Exception& )
1871 if ( !bResult )
1873 // TODO/LATER: may need error code setting based on exception
1874 SetError(ERRCODE_IO_GENERAL);
1877 else if (!GetMedium()->GetFilter()->IsOwnFormat())
1878 bResult = true;
1880 return bResult;
1884 bool SfxObjectShell::DoSaveObjectAs( SfxMedium& rMedium, bool bCommit )
1886 bool bOk = false;
1888 ModifyBlocker_Impl aBlock( this );
1890 uno::Reference < embed::XStorage > xNewStor = rMedium.GetStorage();
1891 if ( !xNewStor.is() )
1892 return false;
1894 uno::Reference < beans::XPropertySet > xPropSet( xNewStor, uno::UNO_QUERY );
1895 if ( !xPropSet.is() )
1896 return false;
1898 Any a = xPropSet->getPropertyValue("MediaType");
1899 OUString aMediaType;
1900 if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
1902 SAL_WARN( "sfx.doc", "The mediatype must be set already!" );
1903 SetupStorage( xNewStor, SOFFICE_FILEFORMAT_CURRENT, false );
1906 pImpl->bIsSaving = false;
1907 bOk = SaveAsOwnFormat( rMedium );
1909 if ( bCommit )
1911 try {
1912 uno::Reference< embed::XTransactedObject > xTransact( xNewStor, uno::UNO_QUERY_THROW );
1913 xTransact->commit();
1915 catch( uno::Exception& )
1917 SAL_WARN( "sfx.doc", "The storage was not committed on DoSaveAs!" );
1921 return bOk;
1925 // TODO/LATER: may be the call must be removed completely
1926 bool SfxObjectShell::DoSaveAs( SfxMedium& rMedium )
1928 // here only root storages are included, which are stored via temp file
1929 rMedium.CreateTempFileNoCopy();
1930 SetError(rMedium.GetErrorCode());
1931 if ( GetError() )
1932 return false;
1934 // copy version list from "old" medium to target medium, so it can be used on saving
1935 if ( pImpl->bPreserveVersions )
1936 rMedium.TransferVersionList_Impl( *pMedium );
1938 bool bRet = SaveTo_Impl( rMedium, nullptr );
1939 if ( !bRet )
1940 SetError(rMedium.GetErrorCode());
1941 return bRet;
1945 bool SfxObjectShell::DoSaveCompleted( SfxMedium* pNewMed, bool bRegisterRecent )
1947 bool bOk = true;
1948 bool bMedChanged = pNewMed && pNewMed!=pMedium;
1950 DBG_ASSERT( !pNewMed || pNewMed->GetError() == ERRCODE_NONE, "DoSaveCompleted: Medium has error!" );
1952 // delete Medium (and Storage!) after all notifications
1953 SfxMedium* pOld = pMedium;
1954 if ( bMedChanged )
1956 pMedium = pNewMed;
1957 pMedium->CanDisposeStorage_Impl( true );
1960 std::shared_ptr<const SfxFilter> pFilter = pMedium ? pMedium->GetFilter() : nullptr;
1961 if ( pNewMed )
1963 if( bMedChanged )
1965 if (!pNewMed->GetName().isEmpty())
1966 bHasName = true;
1967 Broadcast( SfxHint(SfxHintId::NameChanged) );
1968 EnableSetModified(false);
1969 getDocProperties()->setGenerator(
1970 ::utl::DocInfoHelper::GetGeneratorString() );
1971 EnableSetModified();
1974 uno::Reference< embed::XStorage > xStorage;
1975 if ( !pFilter || IsPackageStorageFormat_Impl( *pMedium ) )
1977 uno::Reference < embed::XStorage > xOld = GetStorage();
1979 // when the package based medium is broken and has no storage or if the storage
1980 // is the same as the document storage the current document storage should be preserved
1981 xStorage = pMedium->GetStorage();
1982 bOk = SaveCompleted( xStorage );
1983 if ( bOk && xStorage.is() && xOld != xStorage
1984 && (!pOld || !pOld->HasStorage_Impl() || xOld != pOld->GetStorage() ) )
1986 // old own storage was not controlled by old Medium -> dispose it
1987 try {
1988 xOld->dispose();
1989 } catch( uno::Exception& )
1991 // the storage is disposed already
1992 // can happen during reload scenario when the medium has
1993 // disposed it during the closing
1994 // will be fixed in one of the next milestones
1998 else
2000 if (pImpl->m_bSavingForSigning && pFilter && pFilter->GetSupportsSigning())
2001 // So that pMedium->pImpl->xStream becomes a non-empty
2002 // reference, and at the end we attempt locking again in
2003 // SfxMedium::LockOrigFileOnDemand().
2004 pMedium->GetMedium_Impl();
2006 if( pMedium->GetOpenMode() & StreamMode::WRITE )
2007 pMedium->GetInStream();
2008 xStorage = GetStorage();
2011 // TODO/LATER: may be this code will be replaced, but not sure
2012 // Set storage in document library containers
2013 pImpl->aBasicManager.setStorage( xStorage );
2015 // Get rid of this workaround after issue i113914 is fixed
2018 uno::Reference< script::XStorageBasedLibraryContainer > xBasicLibraries( pImpl->xBasicLibraries, uno::UNO_QUERY_THROW );
2019 xBasicLibraries->setRootStorage( xStorage );
2021 catch( uno::Exception& )
2025 uno::Reference< script::XStorageBasedLibraryContainer > xDialogLibraries( pImpl->xDialogLibraries, uno::UNO_QUERY_THROW );
2026 xDialogLibraries->setRootStorage( xStorage );
2028 catch( uno::Exception& )
2031 else
2033 if( pMedium )
2035 if( pFilter && !IsPackageStorageFormat_Impl( *pMedium ) && (pMedium->GetOpenMode() & StreamMode::WRITE ))
2037 pMedium->ReOpen();
2038 bOk = SaveCompletedChildren();
2040 else
2041 bOk = SaveCompleted( nullptr );
2043 // either Save or ConvertTo
2044 else
2045 bOk = SaveCompleted( nullptr );
2048 if ( bOk && pNewMed )
2050 if( bMedChanged )
2052 delete pOld;
2054 uno::Reference< frame::XModel > xModel = GetModel();
2055 if ( xModel.is() )
2057 const OUString& aURL {pNewMed->GetOrigURL()};
2058 uno::Sequence< beans::PropertyValue > aMediaDescr;
2059 TransformItems( SID_OPENDOC, *pNewMed->GetItemSet(), aMediaDescr );
2062 xModel->attachResource( aURL, aMediaDescr );
2064 catch( uno::Exception& )
2068 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
2069 bool bTemplate = pTemplateItem && pTemplateItem->GetValue();
2071 // before the title regenerated the document must lose the signatures
2072 pImpl->nDocumentSignatureState = SignatureState::NOSIGNATURES;
2073 if (!bTemplate)
2075 pImpl->nScriptingSignatureState = pNewMed->GetCachedSignatureState_Impl();
2076 OSL_ENSURE( pImpl->nScriptingSignatureState != SignatureState::BROKEN, "The signature must not be broken at this place" );
2078 // TODO/LATER: in future the medium must control own signature state, not the document
2079 pNewMed->SetCachedSignatureState_Impl( SignatureState::NOSIGNATURES ); // set the default value back
2081 else
2082 pNewMed->SetCachedSignatureState_Impl( pImpl->nScriptingSignatureState );
2084 // Set new title
2085 if (!pNewMed->GetName().isEmpty() && SfxObjectCreateMode::EMBEDDED != eCreateMode)
2086 InvalidateName();
2087 SetModified(false); // reset only by set medium
2088 Broadcast( SfxHint(SfxHintId::ModeChanged) );
2090 // this is the end of the saving process, it is possible that
2091 // the file was changed
2092 // between medium commit and this step (attributes change and so on)
2093 // so get the file date again
2094 if ( pNewMed->DocNeedsFileDateCheck() )
2095 pNewMed->GetInitFileDate( true );
2099 pMedium->ClearBackup_Impl();
2100 pMedium->LockOrigFileOnDemand( true, false );
2102 if (bRegisterRecent)
2103 AddToRecentlyUsedList();
2105 return bOk;
2108 void SfxObjectShell::AddToRecentlyUsedList()
2110 INetURLObject aUrl( pMedium->GetOrigURL() );
2112 if ( aUrl.GetProtocol() == INetProtocol::File )
2114 std::shared_ptr<const SfxFilter> pOrgFilter = pMedium->GetFilter();
2115 Application::AddToRecentDocumentList( aUrl.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
2116 pOrgFilter ? pOrgFilter->GetMimeType() : OUString(),
2117 pOrgFilter ? pOrgFilter->GetServiceName() : OUString() );
2122 bool SfxObjectShell::ConvertFrom
2124 SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the source file
2125 (for example file name, <SfxFilter>,
2126 Open-Modi and so on) */
2129 /* [Description]
2131 This method is called for loading of documents over all filters which are
2132 not SfxFilterFlags::OWN or for which no clipboard format has been registered
2133 (thus no storage format that is used). In other words, with this method
2134 it is imported.
2136 Files which are to be opened here should be opened through 'rMedium'
2137 to guarantee the right open modes. Especially if the format is retained
2138 (only possible with SfxFilterFlags::SIMULATE or SfxFilterFlags::OWN) file which must
2139 be opened STREAM_SHARE_DENYWRITE.
2141 [Return value]
2143 bool true
2144 The document could be loaded.
2146 false
2147 The document could not be loaded, an error code
2148 received through <SvMedium::GetError()const>
2150 [Example]
2152 bool DocSh::ConvertFrom( SfxMedium &rMedium )
2154 SvStreamRef xStream = rMedium.GetInStream();
2155 if( xStream.is() )
2157 xStream->SetBufferSize(4096);
2158 *xStream >> ...;
2160 // Do not call 'rMedium.CloseInStream()'! Keep File locked!
2161 return ERRCODE_NONE == rMedium.GetError();
2164 return false;
2167 [Cross-references]
2169 <SfxObjectShell::ConvertTo(SfxMedium&)>
2170 <SfxFilterFlags::REGISTRATION>
2173 return false;
2176 bool SfxObjectShell::ImportFrom(SfxMedium& rMedium,
2177 css::uno::Reference<css::text::XTextRange> const& xInsertPosition)
2179 const OUString aFilterName( rMedium.GetFilter()->GetFilterName() );
2181 uno::Reference< lang::XMultiServiceFactory > xMan = ::comphelper::getProcessServiceFactory();
2182 uno::Reference < lang::XMultiServiceFactory > xFilterFact (
2183 xMan->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
2185 uno::Sequence < beans::PropertyValue > aProps;
2186 uno::Reference < container::XNameAccess > xFilters ( xFilterFact, uno::UNO_QUERY );
2187 if ( xFilters->hasByName( aFilterName ) )
2189 xFilters->getByName( aFilterName ) >>= aProps;
2190 rMedium.GetItemSet()->Put( SfxStringItem( SID_FILTER_NAME, aFilterName ) );
2193 OUString aFilterImplName;
2194 auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
2195 [](const beans::PropertyValue& rFilterProp) { return rFilterProp.Name == "FilterService"; });
2196 if (pProp != std::cend(aProps))
2197 pProp->Value >>= aFilterImplName;
2199 uno::Reference< document::XFilter > xLoader;
2200 if ( !aFilterImplName.isEmpty() )
2204 xLoader.set( xFilterFact->createInstanceWithArguments( aFilterName, uno::Sequence < uno::Any >() ), uno::UNO_QUERY );
2206 catch(const uno::Exception&)
2208 xLoader.clear();
2211 if ( xLoader.is() )
2213 // it happens that xLoader does not support xImporter!
2216 uno::Reference< lang::XComponent > xComp( GetModel(), uno::UNO_QUERY_THROW );
2217 uno::Reference< document::XImporter > xImporter( xLoader, uno::UNO_QUERY_THROW );
2218 xImporter->setTargetDocument( xComp );
2220 uno::Sequence < beans::PropertyValue > lDescriptor;
2221 rMedium.GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, rMedium.GetName() ) );
2222 TransformItems( SID_OPENDOC, *rMedium.GetItemSet(), lDescriptor );
2224 css::uno::Sequence < css::beans::PropertyValue > aArgs ( lDescriptor.getLength() );
2225 css::beans::PropertyValue * pNewValue = aArgs.getArray();
2226 const css::beans::PropertyValue * pOldValue = lDescriptor.getConstArray();
2227 static const OUStringLiteral sInputStream ( u"InputStream" );
2229 bool bHasInputStream = false;
2230 bool bHasBaseURL = false;
2231 sal_Int32 nEnd = lDescriptor.getLength();
2233 for ( sal_Int32 i = 0; i < nEnd; i++ )
2235 pNewValue[i] = pOldValue[i];
2236 if ( pOldValue [i].Name == sInputStream )
2237 bHasInputStream = true;
2238 else if ( pOldValue[i].Name == "DocumentBaseURL" )
2239 bHasBaseURL = true;
2242 if ( !bHasInputStream )
2244 aArgs.realloc ( ++nEnd );
2245 auto pArgs = aArgs.getArray();
2246 pArgs[nEnd-1].Name = sInputStream;
2247 pArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XInputStream > ( new utl::OSeekableInputStreamWrapper ( *rMedium.GetInStream() ) );
2250 if ( !bHasBaseURL )
2252 aArgs.realloc ( ++nEnd );
2253 auto pArgs = aArgs.getArray();
2254 pArgs[nEnd-1].Name = "DocumentBaseURL";
2255 pArgs[nEnd-1].Value <<= rMedium.GetBaseURL();
2258 if (xInsertPosition.is()) {
2259 aArgs.realloc( nEnd += 2 );
2260 auto pArgs = aArgs.getArray();
2261 pArgs[nEnd-2].Name = "InsertMode";
2262 pArgs[nEnd-2].Value <<= true;
2263 pArgs[nEnd-1].Name = "TextInsertModeRange";
2264 pArgs[nEnd-1].Value <<= xInsertPosition;
2267 // #i119492# During loading, some OLE objects like chart will be set
2268 // modified flag, so needs to reset the flag to false after loading
2269 bool bRtn = true;
2270 if (!tools::isEmptyFileUrl(rMedium.GetName()))
2272 bRtn = xLoader->filter(aArgs);
2274 const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
2275 for ( const auto& rName : aNames )
2277 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
2278 OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
2279 if ( xObj.is() )
2281 sal_Int32 nState = xObj->getCurrentState();
2282 if ( nState == embed::EmbedStates::LOADED || nState == embed::EmbedStates::RUNNING ) // means that the object is not active
2284 uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
2285 if (xModifiable.is() && xModifiable->isModified())
2287 uno::Reference<embed::XEmbedPersist> const xPers(xObj, uno::UNO_QUERY);
2288 assert(xPers.is() && "Modified object without persistence!");
2289 // store it before resetting modified!
2290 xPers->storeOwn();
2291 xModifiable->setModified(false);
2297 // tdf#107690 import custom document property _MarkAsFinal as SecurityOptOpenReadonly
2298 // (before this fix, LibreOffice opened read-only OOXML documents as editable,
2299 // also saved and exported _MarkAsFinal=true silently, resulting unintended read-only
2300 // warning info bar in MSO)
2301 uno::Reference< document::XDocumentPropertiesSupplier > xPropSupplier(GetModel(), uno::UNO_QUERY_THROW);
2302 uno::Reference<document::XDocumentProperties> xDocProps = xPropSupplier->getDocumentProperties() ;
2303 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocProps->getUserDefinedProperties();
2304 if (xPropertyContainer.is())
2306 uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
2307 if (xPropertySet.is())
2309 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
2310 if (xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName("_MarkAsFinal"))
2312 if (xPropertySet->getPropertyValue("_MarkAsFinal").get<bool>())
2314 uno::Reference< lang::XMultiServiceFactory > xFactory(GetModel(), uno::UNO_QUERY);
2315 uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
2316 xSettings->setPropertyValue("LoadReadonly", uno::makeAny(true));
2318 xPropertyContainer->removeProperty("_MarkAsFinal");
2323 return bRtn;
2325 catch (const packages::zip::ZipIOException&)
2327 SetError(ERRCODE_IO_BROKENPACKAGE);
2329 catch (const lang::WrappedTargetRuntimeException& rWrapped)
2331 io::WrongFormatException e;
2332 if (rWrapped.TargetException >>= e)
2334 SetError(*new StringErrorInfo(ERRCODE_SFX_FORMAT_ROWCOL,
2335 e.Message, DialogMask::ButtonsOk | DialogMask::MessageError ));
2338 catch (const css::io::IOException& e)
2340 SetError(*new StringErrorInfo(ERRCODE_SFX_FORMAT_ROWCOL,
2341 e.Message, DialogMask::ButtonsOk | DialogMask::MessageError ));
2343 catch (const std::exception& e)
2345 const char *msg = e.what();
2346 const OUString sError(msg, strlen(msg), RTL_TEXTENCODING_ASCII_US);
2347 SetError(*new StringErrorInfo(ERRCODE_SFX_DOLOADFAILED,
2348 sError, DialogMask::ButtonsOk | DialogMask::MessageError));
2350 catch (...)
2352 std::abort(); // cannot happen
2356 return false;
2359 bool SfxObjectShell::ExportTo( SfxMedium& rMedium )
2361 const OUString aFilterName( rMedium.GetFilter()->GetFilterName() );
2362 uno::Reference< document::XExporter > xExporter;
2365 uno::Reference< lang::XMultiServiceFactory > xMan = ::comphelper::getProcessServiceFactory();
2366 uno::Reference < lang::XMultiServiceFactory > xFilterFact (
2367 xMan->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
2369 uno::Sequence < beans::PropertyValue > aProps;
2370 uno::Reference < container::XNameAccess > xFilters ( xFilterFact, uno::UNO_QUERY );
2371 if ( xFilters->hasByName( aFilterName ) )
2372 xFilters->getByName( aFilterName ) >>= aProps;
2374 OUString aFilterImplName;
2375 auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
2376 [](const beans::PropertyValue& rFilterProp) { return rFilterProp.Name == "FilterService"; });
2377 if (pProp != std::cend(aProps))
2378 pProp->Value >>= aFilterImplName;
2380 if ( !aFilterImplName.isEmpty() )
2384 xExporter.set( xFilterFact->createInstanceWithArguments( aFilterName, uno::Sequence < uno::Any >() ), uno::UNO_QUERY );
2386 catch(const uno::Exception&)
2388 xExporter.clear();
2393 if ( xExporter.is() )
2395 try{
2396 uno::Reference< lang::XComponent > xComp( GetModel(), uno::UNO_QUERY_THROW );
2397 uno::Reference< document::XFilter > xFilter( xExporter, uno::UNO_QUERY_THROW );
2398 xExporter->setSourceDocument( xComp );
2400 css::uno::Sequence < css::beans::PropertyValue > aOldArgs;
2401 SfxItemSet* pItems = rMedium.GetItemSet();
2402 TransformItems( SID_SAVEASDOC, *pItems, aOldArgs );
2404 const css::beans::PropertyValue * pOldValue = aOldArgs.getConstArray();
2405 css::uno::Sequence < css::beans::PropertyValue > aArgs ( aOldArgs.getLength() );
2406 css::beans::PropertyValue * pNewValue = aArgs.getArray();
2408 // put in the REAL file name, and copy all PropertyValues
2409 static const OUStringLiteral sOutputStream ( u"OutputStream" );
2410 static const OUStringLiteral sStream ( u"StreamForOutput" );
2411 bool bHasOutputStream = false;
2412 bool bHasStream = false;
2413 bool bHasBaseURL = false;
2414 bool bHasFilterName = false;
2415 bool bIsRedactMode = false;
2416 bool bIsPreview = false;
2417 sal_Int32 nEnd = aOldArgs.getLength();
2419 for ( sal_Int32 i = 0; i < nEnd; i++ )
2421 pNewValue[i] = pOldValue[i];
2422 if ( pOldValue[i].Name == "FileName" )
2423 pNewValue[i].Value <<= rMedium.GetName();
2424 else if ( pOldValue[i].Name == sOutputStream )
2425 bHasOutputStream = true;
2426 else if ( pOldValue[i].Name == sStream )
2427 bHasStream = true;
2428 else if ( pOldValue[i].Name == "DocumentBaseURL" )
2429 bHasBaseURL = true;
2430 else if( pOldValue[i].Name == "FilterName" )
2431 bHasFilterName = true;
2434 const css::uno::Sequence<css::beans::PropertyValue>& rMediumArgs = rMedium.GetArgs();
2435 for ( sal_Int32 i = 0; i < rMediumArgs.getLength(); i++ )
2437 if( rMediumArgs[i].Name == "IsPreview" )
2438 rMediumArgs[i].Value >>= bIsPreview;
2441 // FIXME: Handle this inside TransformItems()
2442 if (pItems->GetItemState(SID_IS_REDACT_MODE) == SfxItemState::SET)
2443 bIsRedactMode = true;
2445 if ( !bHasOutputStream )
2447 aArgs.realloc ( ++nEnd );
2448 auto pArgs = aArgs.getArray();
2449 pArgs[nEnd-1].Name = sOutputStream;
2450 pArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XOutputStream > ( new utl::OOutputStreamWrapper ( *rMedium.GetOutStream() ) );
2453 // add stream as well, for OOX export and maybe others
2454 if ( !bHasStream )
2456 aArgs.realloc ( ++nEnd );
2457 auto pArgs = aArgs.getArray();
2458 pArgs[nEnd-1].Name = sStream;
2459 pArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XStream > ( new utl::OStreamWrapper ( *rMedium.GetOutStream() ) );
2462 if ( !bHasBaseURL )
2464 aArgs.realloc ( ++nEnd );
2465 auto pArgs = aArgs.getArray();
2466 pArgs[nEnd-1].Name = "DocumentBaseURL";
2467 pArgs[nEnd-1].Value <<= rMedium.GetBaseURL( true );
2470 if( !bHasFilterName )
2472 aArgs.realloc( ++nEnd );
2473 auto pArgs = aArgs.getArray();
2474 pArgs[nEnd-1].Name = "FilterName";
2475 pArgs[nEnd-1].Value <<= aFilterName;
2478 if (bIsRedactMode)
2480 aArgs.realloc( ++nEnd );
2481 auto pArgs = aArgs.getArray();
2482 pArgs[nEnd-1].Name = "IsRedactMode";
2483 pArgs[nEnd-1].Value <<= bIsRedactMode;
2486 if (bIsPreview)
2488 aArgs.realloc( ++nEnd );
2489 auto pArgs = aArgs.getArray();
2490 pArgs[nEnd-1].Name = "IsPreview";
2491 pArgs[nEnd-1].Value <<= bIsPreview;
2494 return xFilter->filter( aArgs );
2496 catch (const css::uno::RuntimeException & e)
2498 SAL_INFO("sfx.doc", "ExportTo: " << e);
2500 catch (const std::exception & e)
2502 SAL_INFO("sfx.doc", "ExportTo: " << e.what());
2504 catch(...)
2506 SAL_INFO("sfx.doc", "ExportTo: Unknown exception!");
2510 return false;
2514 bool SfxObjectShell::ConvertTo
2516 SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the target file
2517 (for example file name, <SfxFilter>,
2518 Open-Modi and so on) */
2521 /* [Description]
2523 This method is called for saving of documents over all filters which are
2524 not SfxFilterFlags::OWN or for which no clipboard format has been registered
2525 (thus no storage format that is used). In other words, with this method
2526 it is exported.
2528 Files which are to be opened here should be opened through 'rMedium'
2529 to guarantee the right open modes. Especially if the format is retained
2530 (only possible with SfxFilterFlags::SIMULATE or SfxFilterFlags::OWN) file which must
2531 be opened STREAM_SHARE_DENYWRITE.
2533 [Return value]
2535 bool true
2536 The document could be saved.
2538 false
2539 The document could not be saved, an error code is
2540 received by <SvMedium::GetError()const>
2543 [Example]
2545 bool DocSh::ConvertTo( SfxMedium &rMedium )
2547 SvStreamRef xStream = rMedium.GetOutStream();
2548 if ( xStream.is() )
2550 xStream->SetBufferSize(4096);
2551 *xStream << ...;
2553 rMedium.CloseOutStream(); // opens the InStream automatically
2554 return ERRCODE_NONE == rMedium.GetError();
2556 return false ;
2559 [Cross-references]
2561 <SfxObjectShell::ConvertFrom(SfxMedium&)>
2562 <SfxFilterFlags::REGISTRATION>
2566 return false;
2570 bool SfxObjectShell::DoSave_Impl( const SfxItemSet* pArgs )
2572 SfxMedium* pRetrMedium = GetMedium();
2573 std::shared_ptr<const SfxFilter> pFilter = pRetrMedium->GetFilter();
2575 // copy the original itemset, but remove the "version" item, because pMediumTmp
2576 // is a new medium "from scratch", so no version should be stored into it
2577 std::shared_ptr<SfxItemSet> pSet = std::make_shared<SfxAllItemSet>(*pRetrMedium->GetItemSet());
2578 pSet->ClearItem( SID_VERSION );
2579 pSet->ClearItem( SID_DOC_BASEURL );
2581 // copy the version comment and major items for the checkin only
2582 if ( pRetrMedium->IsInCheckIn( ) )
2584 const SfxPoolItem* pMajor = pArgs->GetItem( SID_DOCINFO_MAJOR );
2585 if ( pMajor )
2586 pSet->Put( *pMajor );
2588 const SfxPoolItem* pComments = pArgs->GetItem( SID_DOCINFO_COMMENTS );
2589 if ( pComments )
2590 pSet->Put( *pComments );
2593 // create a medium as a copy; this medium is only for writing, because it
2594 // uses the same name as the original one writing is done through a copy,
2595 // that will be transferred to the target (of course after calling HandsOff)
2596 SfxMedium* pMediumTmp = new SfxMedium( pRetrMedium->GetName(), pRetrMedium->GetOpenMode(), pFilter, std::move(pSet) );
2597 pMediumTmp->SetInCheckIn( pRetrMedium->IsInCheckIn( ) );
2598 pMediumTmp->SetLongName( pRetrMedium->GetLongName() );
2599 if ( pMediumTmp->GetErrorCode() != ERRCODE_NONE )
2601 SetError(pMediumTmp->GetError());
2602 delete pMediumTmp;
2603 return false;
2606 // copy version list from "old" medium to target medium, so it can be used on saving
2607 pMediumTmp->TransferVersionList_Impl( *pRetrMedium );
2609 // an interaction handler here can acquire only in case of GUI Saving
2610 // and should be removed after the saving is done
2611 css::uno::Reference< XInteractionHandler > xInteract;
2612 const SfxUnoAnyItem* pxInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pArgs, SID_INTERACTIONHANDLER, false);
2613 if ( pxInteractionItem && ( pxInteractionItem->GetValue() >>= xInteract ) && xInteract.is() )
2614 pMediumTmp->GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny( xInteract ) ) );
2616 const SfxBoolItem* pNoFileSync = pArgs->GetItem<SfxBoolItem>(SID_NO_FILE_SYNC, false);
2617 if (pNoFileSync && pNoFileSync->GetValue())
2618 pMediumTmp->DisableFileSync(true);
2620 bool bSaved = false;
2621 if( !GetError() && SaveTo_Impl( *pMediumTmp, pArgs ) )
2623 bSaved = true;
2625 if( pMediumTmp->GetItemSet() )
2627 pMediumTmp->GetItemSet()->ClearItem( SID_INTERACTIONHANDLER );
2628 pMediumTmp->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
2631 SetError(pMediumTmp->GetErrorCode());
2633 bool bOpen = DoSaveCompleted( pMediumTmp );
2635 DBG_ASSERT(bOpen,"Error handling for DoSaveCompleted not implemented");
2637 else
2639 // transfer error code from medium to objectshell
2640 SetError(pMediumTmp->GetError());
2642 // reconnect to object storage
2643 DoSaveCompleted();
2645 if( pRetrMedium->GetItemSet() )
2647 pRetrMedium->GetItemSet()->ClearItem( SID_INTERACTIONHANDLER );
2648 pRetrMedium->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
2651 delete pMediumTmp;
2654 SetModified( !bSaved );
2655 return bSaved;
2659 bool SfxObjectShell::Save_Impl( const SfxItemSet* pSet )
2661 if ( IsReadOnly() )
2663 SetError(ERRCODE_SFX_DOCUMENTREADONLY);
2664 return false;
2667 pImpl->bIsSaving = true;
2668 bool bSaved = false;
2669 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_DOC_SALVAGE, false);
2670 if ( pSalvageItem )
2672 const SfxStringItem* pFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_FILTER_NAME, false);
2673 std::shared_ptr<const SfxFilter> pFilter;
2674 if ( pFilterItem )
2675 pFilter = SfxFilterMatcher( GetFactory().GetFactoryName() ).GetFilter4FilterName( OUString() );
2677 SfxMedium *pMed = new SfxMedium(
2678 pSalvageItem->GetValue(), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, pFilter );
2680 const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_PASSWORD, false);
2681 if ( pPasswordItem )
2682 pMed->GetItemSet()->Put( *pPasswordItem );
2684 bSaved = DoSaveAs( *pMed );
2685 if ( bSaved )
2686 bSaved = DoSaveCompleted( pMed );
2687 else
2688 delete pMed;
2690 else
2691 bSaved = DoSave_Impl( pSet );
2692 return bSaved;
2695 bool SfxObjectShell::CommonSaveAs_Impl(const INetURLObject& aURL, const OUString& aFilterName,
2696 SfxItemSet& rItemSet,
2697 const uno::Sequence<beans::PropertyValue>& rArgs)
2699 if( aURL.HasError() )
2701 SetError(ERRCODE_IO_INVALIDPARAMETER);
2702 return false;
2705 if ( aURL != INetURLObject( OUString( "private:stream" ) ) )
2707 // Is there already a Document with this name?
2708 SfxObjectShell* pDoc = nullptr;
2709 for ( SfxObjectShell* pTmp = SfxObjectShell::GetFirst();
2710 pTmp && !pDoc;
2711 pTmp = SfxObjectShell::GetNext(*pTmp) )
2713 if( ( pTmp != this ) && pTmp->GetMedium() )
2715 INetURLObject aCompare( pTmp->GetMedium()->GetName() );
2716 if ( aCompare == aURL )
2717 pDoc = pTmp;
2720 if ( pDoc )
2722 // Then error message: "already opened"
2723 SetError(ERRCODE_SFX_ALREADYOPEN);
2724 return false;
2728 DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" );
2729 DBG_ASSERT( rItemSet.Count() != 0, "Incorrect Parameter");
2731 const SfxBoolItem* pSaveToItem = rItemSet.GetItem<SfxBoolItem>(SID_SAVETO, false);
2732 bool bSaveTo = pSaveToItem && pSaveToItem->GetValue();
2734 std::shared_ptr<const SfxFilter> pFilter = GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName );
2735 if ( !pFilter
2736 || !pFilter->CanExport()
2737 || (!bSaveTo && !pFilter->CanImport()) )
2739 SetError(ERRCODE_IO_INVALIDPARAMETER);
2740 return false;
2744 const SfxBoolItem* pCopyStreamItem = rItemSet.GetItem<SfxBoolItem>(SID_COPY_STREAM_IF_POSSIBLE, false);
2745 if ( bSaveTo && pCopyStreamItem && pCopyStreamItem->GetValue() && !IsModified() )
2747 if (pMedium->TryDirectTransfer(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), rItemSet))
2748 return true;
2750 rItemSet.ClearItem( SID_COPY_STREAM_IF_POSSIBLE );
2752 SfxMedium *pActMed = GetMedium();
2753 const INetURLObject aActName(pActMed->GetName());
2755 bool bWasReadonly = IsReadOnly();
2757 if ( aURL == aActName && aURL != INetURLObject( OUString("private:stream") )
2758 && IsReadOnly() )
2760 SetError(ERRCODE_SFX_DOCUMENTREADONLY);
2761 return false;
2764 if (SfxItemState::SET != rItemSet.GetItemState(SID_UNPACK) && officecfg::Office::Common::Save::Document::Unpacked::get())
2765 rItemSet.Put(SfxBoolItem(SID_UNPACK, false));
2767 OUString aTempFileURL;
2768 if ( IsDocShared() )
2769 aTempFileURL = pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
2771 if (PreDoSaveAs_Impl(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), aFilterName,
2772 rItemSet, rArgs))
2774 // Update Data on media
2775 SfxItemSet *pSet = GetMedium()->GetItemSet();
2776 pSet->ClearItem( SID_INTERACTIONHANDLER );
2777 pSet->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
2778 pSet->ClearItem( SID_STANDARD_DIR );
2779 pSet->ClearItem( SID_PATH );
2781 if ( !bSaveTo )
2783 pSet->ClearItem( SID_REFERER );
2784 pSet->ClearItem( SID_POSTDATA );
2785 pSet->ClearItem( SID_TEMPLATE );
2786 pSet->ClearItem( SID_DOC_READONLY );
2787 pSet->ClearItem( SID_CONTENTTYPE );
2788 pSet->ClearItem( SID_CHARSET );
2789 pSet->ClearItem( SID_FILTER_NAME );
2790 pSet->ClearItem( SID_OPTIONS );
2791 pSet->ClearItem( SID_VERSION );
2792 pSet->ClearItem( SID_EDITDOC );
2793 pSet->ClearItem( SID_OVERWRITE );
2794 pSet->ClearItem( SID_DEFAULTFILEPATH );
2795 pSet->ClearItem( SID_DEFAULTFILENAME );
2797 const SfxStringItem* pFilterItem = rItemSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
2798 if ( pFilterItem )
2799 pSet->Put( *pFilterItem );
2801 const SfxStringItem* pOptionsItem = rItemSet.GetItem<SfxStringItem>(SID_OPTIONS, false);
2802 if ( pOptionsItem )
2803 pSet->Put( *pOptionsItem );
2805 const SfxStringItem* pFilterOptItem = rItemSet.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
2806 if ( pFilterOptItem )
2807 pSet->Put( *pFilterOptItem );
2809 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
2810 if ( IsDocShared() && !aTempFileURL.isEmpty() )
2812 // this is a shared document that has to be disconnected from the old location
2813 FreeSharedFile( aTempFileURL );
2815 if ( pFilter->IsOwnFormat()
2816 && pFilter->UsesStorage()
2817 && pFilter->GetVersion() >= SOFFICE_FILEFORMAT_60 )
2819 // the target format is the own format
2820 // the target document must be shared
2821 SwitchToShared( true, false );
2824 #endif
2827 if ( bWasReadonly && !bSaveTo )
2828 Broadcast( SfxHint(SfxHintId::ModeChanged) );
2830 return true;
2832 else
2833 return false;
2836 bool SfxObjectShell::PreDoSaveAs_Impl(const OUString& rFileName, const OUString& aFilterName,
2837 SfxItemSet const& rItemSet,
2838 const uno::Sequence<beans::PropertyValue>& rArgs)
2840 // copy all items stored in the itemset of the current medium
2841 std::shared_ptr<SfxAllItemSet> xMergedParams = std::make_shared<SfxAllItemSet>( *pMedium->GetItemSet() );
2843 // in "SaveAs" title and password will be cleared ( maybe the new itemset contains new values, otherwise they will be empty )
2844 // #i119366# - As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both.
2845 // Also, ( maybe the new itemset contains new values, otherwise they will be empty )
2846 if (xMergedParams->HasItem(SID_ENCRYPTIONDATA))
2848 bool bPasswordProtected = true;
2849 const SfxUnoAnyItem* pEncryptionDataItem
2850 = xMergedParams->GetItem<SfxUnoAnyItem>(SID_ENCRYPTIONDATA, false);
2851 if (pEncryptionDataItem)
2853 uno::Sequence<beans::NamedValue> aEncryptionData;
2854 pEncryptionDataItem->GetValue() >>= aEncryptionData;
2855 for (const auto& rItem : std::as_const(aEncryptionData))
2857 if (rItem.Name == "CryptoType")
2859 OUString aValue;
2860 rItem.Value >>= aValue;
2861 if (aValue != "StrongEncryptionDataSpace")
2863 // This is not just a password protected document. Let's keep encryption data as is.
2864 bPasswordProtected = false;
2866 break;
2870 if (bPasswordProtected)
2872 // For password protected documents remove encryption data during "Save as..."
2873 xMergedParams->ClearItem(SID_PASSWORD);
2874 xMergedParams->ClearItem(SID_ENCRYPTIONDATA);
2878 xMergedParams->ClearItem( SID_DOCINFO_TITLE );
2880 xMergedParams->ClearItem( SID_INPUTSTREAM );
2881 xMergedParams->ClearItem( SID_STREAM );
2882 xMergedParams->ClearItem( SID_CONTENT );
2883 xMergedParams->ClearItem( SID_DOC_READONLY );
2884 xMergedParams->ClearItem( SID_DOC_BASEURL );
2886 xMergedParams->ClearItem( SID_REPAIRPACKAGE );
2888 // "SaveAs" will never store any version information - it's a complete new file !
2889 xMergedParams->ClearItem( SID_VERSION );
2891 // merge the new parameters into the copy
2892 // all values present in both itemsets will be overwritten by the new parameters
2893 xMergedParams->Put(rItemSet);
2895 SAL_WARN_IF( xMergedParams->GetItemState( SID_DOC_SALVAGE) >= SfxItemState::SET,
2896 "sfx.doc","Salvage item present in Itemset, check the parameters!");
2898 // should be unnecessary - too hot to handle!
2899 xMergedParams->ClearItem( SID_DOC_SALVAGE );
2901 // create a medium for the target URL
2902 SfxMedium *pNewFile = new SfxMedium( rFileName, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, nullptr, xMergedParams );
2903 pNewFile->SetArgs(rArgs);
2905 const SfxBoolItem* pNoFileSync = xMergedParams->GetItem<SfxBoolItem>(SID_NO_FILE_SYNC, false);
2906 if (pNoFileSync && pNoFileSync->GetValue())
2907 pNewFile->DisableFileSync(true);
2909 bool bUseThumbnailSave = IsUseThumbnailSave();
2910 comphelper::ScopeGuard aThumbnailGuard(
2911 [this, bUseThumbnailSave] { this->SetUseThumbnailSave(bUseThumbnailSave); });
2912 const SfxBoolItem* pNoThumbnail = xMergedParams->GetItem<SfxBoolItem>(SID_NO_THUMBNAIL, false);
2913 if (pNoThumbnail)
2914 // Thumbnail generation should be avoided just for this save.
2915 SetUseThumbnailSave(!pNoThumbnail->GetValue());
2916 else
2917 aThumbnailGuard.dismiss();
2919 // set filter; if no filter is given, take the default filter of the factory
2920 if ( !aFilterName.isEmpty() )
2922 pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName ) );
2924 if(aFilterName == "writer_pdf_Export" && pNewFile->GetItemSet())
2926 uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions(2);
2927 auto pSaveToFilterDataOptions = aSaveToFilterDataOptions.getArray();
2928 bool bRet = false;
2930 for(int i = 0 ; i< rArgs.getLength() ; ++i)
2932 auto aProp = rArgs[i];
2933 if(aProp.Name == "EncryptFile")
2935 pSaveToFilterDataOptions[0].Name = aProp.Name;
2936 pSaveToFilterDataOptions[0].Value = aProp.Value;
2937 bRet = true;
2939 if(aProp.Name == "DocumentOpenPassword")
2941 pSaveToFilterDataOptions[1].Name = aProp.Name;
2942 pSaveToFilterDataOptions[1].Value = aProp.Value;
2943 bRet = true;
2947 if( bRet )
2948 pNewFile->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA, uno::makeAny(aSaveToFilterDataOptions)));
2951 else
2952 pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT ) );
2954 if ( pNewFile->GetErrorCode() != ERRCODE_NONE )
2956 // creating temporary file failed ( f.e. floppy disk not inserted! )
2957 SetError(pNewFile->GetError());
2958 delete pNewFile;
2959 return false;
2962 if (comphelper::LibreOfficeKit::isActive())
2964 // Before saving, commit in-flight changes.
2965 TerminateEditing();
2968 // check if a "SaveTo" is wanted, no "SaveAs"
2969 const SfxBoolItem* pSaveToItem = xMergedParams->GetItem<SfxBoolItem>(SID_SAVETO, false);
2970 bool bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED || (pSaveToItem && pSaveToItem->GetValue());
2972 // distinguish between "Save" and "SaveAs"
2973 pImpl->bIsSaving = false;
2975 // copy version list from "old" medium to target medium, so it can be used on saving
2976 if ( pImpl->bPreserveVersions )
2977 pNewFile->TransferVersionList_Impl( *pMedium );
2979 // Save the document ( first as temporary file, then transfer to the target URL by committing the medium )
2980 bool bOk = false;
2981 if ( !pNewFile->GetErrorCode() && SaveTo_Impl( *pNewFile, nullptr ) )
2983 // transfer a possible error from the medium to the document
2984 SetError(pNewFile->GetErrorCode());
2986 // notify the document that saving was done successfully
2987 if ( !bCopyTo )
2989 bOk = DoSaveCompleted( pNewFile );
2991 else
2992 bOk = DoSaveCompleted();
2994 if( bOk )
2996 if( !bCopyTo )
2997 SetModified( false );
2999 else
3001 // TODO/LATER: the code below must be dead since the storage commit makes all the stuff
3002 // and the DoSaveCompleted call should not be able to fail in general
3004 DBG_ASSERT( !bCopyTo, "Error while reconnecting to medium, can't be handled!");
3005 SetError(pNewFile->GetErrorCode());
3007 if ( !bCopyTo )
3009 // reconnect to the old medium
3010 bool bRet = DoSaveCompleted( pMedium );
3011 DBG_ASSERT( bRet, "Error in DoSaveCompleted, can't be handled!");
3014 // TODO/LATER: disconnect the new file from the storage for the case when pure saving is done
3015 // if storing has corrupted the file, probably it must be restored either here or
3016 // by the storage
3017 delete pNewFile;
3018 pNewFile = nullptr;
3021 else
3023 SetError(pNewFile->GetErrorCode());
3025 // reconnect to the old storage
3026 DoSaveCompleted();
3028 delete pNewFile;
3029 pNewFile = nullptr;
3032 if ( bCopyTo )
3033 delete pNewFile;
3034 else if( !bOk )
3035 SetModified();
3037 return bOk;
3041 bool SfxObjectShell::LoadFrom( SfxMedium& /*rMedium*/ )
3043 SAL_WARN( "sfx.doc", "Base implementation, must not be called in general!" );
3044 return true;
3048 bool SfxObjectShell::CanReload_Impl()
3050 /* [Description]
3052 Internal method for determining whether a reload of the document
3053 (as RevertToSaved or last known version) is possible.
3057 return pMedium && HasName() && !IsInModalMode() && !pImpl->bForbidReload;
3061 HiddenInformation SfxObjectShell::GetHiddenInformationState( HiddenInformation nStates )
3063 HiddenInformation nState = HiddenInformation::NONE;
3064 if ( nStates & HiddenInformation::DOCUMENTVERSIONS )
3066 if ( GetMedium()->GetVersionList().hasElements() )
3067 nState |= HiddenInformation::DOCUMENTVERSIONS;
3070 return nState;
3073 sal_Int16 SfxObjectShell::QueryHiddenInformation(HiddenWarningFact eFact, weld::Window* pParent)
3075 sal_Int16 nRet = RET_YES;
3076 TranslateId pResId;
3077 SvtSecurityOptions::EOption eOption = SvtSecurityOptions::EOption();
3079 switch ( eFact )
3081 case HiddenWarningFact::WhenSaving :
3083 pResId = STR_HIDDENINFO_CONTINUE_SAVING;
3084 eOption = SvtSecurityOptions::EOption::DocWarnSaveOrSend;
3085 break;
3087 case HiddenWarningFact::WhenPrinting :
3089 pResId = STR_HIDDENINFO_CONTINUE_PRINTING;
3090 eOption = SvtSecurityOptions::EOption::DocWarnPrint;
3091 break;
3093 case HiddenWarningFact::WhenSigning :
3095 pResId = STR_HIDDENINFO_CONTINUE_SIGNING;
3096 eOption = SvtSecurityOptions::EOption::DocWarnSigning;
3097 break;
3099 case HiddenWarningFact::WhenCreatingPDF :
3101 pResId = STR_HIDDENINFO_CONTINUE_CREATEPDF;
3102 eOption = SvtSecurityOptions::EOption::DocWarnCreatePdf;
3103 break;
3105 default:
3106 assert(false); // this cannot happen
3109 if ( SvtSecurityOptions::IsOptionSet( eOption ) )
3111 OUString sMessage( SfxResId(STR_HIDDENINFO_CONTAINS) );
3112 HiddenInformation nWantedStates = HiddenInformation::RECORDEDCHANGES | HiddenInformation::NOTES;
3113 if ( eFact != HiddenWarningFact::WhenPrinting )
3114 nWantedStates |= HiddenInformation::DOCUMENTVERSIONS;
3115 HiddenInformation nStates = GetHiddenInformationState( nWantedStates );
3116 bool bWarning = false;
3118 if ( nStates & HiddenInformation::RECORDEDCHANGES )
3120 sMessage += SfxResId(STR_HIDDENINFO_RECORDCHANGES) + "\n";
3121 bWarning = true;
3123 if ( nStates & HiddenInformation::NOTES )
3125 sMessage += SfxResId(STR_HIDDENINFO_NOTES) + "\n";
3126 bWarning = true;
3128 if ( nStates & HiddenInformation::DOCUMENTVERSIONS )
3130 sMessage += SfxResId(STR_HIDDENINFO_DOCVERSIONS) + "\n";
3131 bWarning = true;
3134 if ( bWarning )
3136 sMessage += "\n" + SfxResId(pResId);
3137 std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pParent,
3138 VclMessageType::Warning, VclButtonsType::YesNo, sMessage));
3139 xWarn->set_default_response(RET_NO);
3140 nRet = xWarn->run();
3144 return nRet;
3147 bool SfxObjectShell::IsSecurityOptOpenReadOnly() const
3149 return IsLoadReadonly();
3152 void SfxObjectShell::SetSecurityOptOpenReadOnly( bool _b )
3154 SetLoadReadonly( _b );
3157 bool SfxObjectShell::LoadOwnFormat( SfxMedium& rMedium )
3159 SAL_INFO( "sfx.doc", "loading \" " << rMedium.GetName() << "\"" );
3161 uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage();
3162 if ( xStorage.is() )
3164 // Password
3165 const SfxStringItem* pPasswdItem = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_PASSWORD, false);
3166 if ( pPasswdItem || ERRCODE_IO_ABORT != CheckPasswd_Impl( this, pMedium ) )
3168 uno::Sequence< beans::NamedValue > aEncryptionData;
3169 if ( GetEncryptionData_Impl(pMedium->GetItemSet(), aEncryptionData) )
3173 // the following code must throw an exception in case of failure
3174 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xStorage, aEncryptionData );
3176 catch( uno::Exception& )
3178 // TODO/LATER: handle the error code
3182 // load document
3183 return Load( rMedium );
3185 return false;
3187 else
3188 return false;
3191 bool SfxObjectShell::SaveAsOwnFormat( SfxMedium& rMedium )
3193 uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage();
3194 if( xStorage.is() )
3196 sal_Int32 nVersion = rMedium.GetFilter()->GetVersion();
3198 // OASIS templates have own mediatypes (SO7 also actually, but it is too late to use them here)
3199 const bool bTemplate = rMedium.GetFilter()->IsOwnTemplateFormat()
3200 && nVersion > SOFFICE_FILEFORMAT_60;
3202 SetupStorage( xStorage, nVersion, bTemplate );
3203 #if HAVE_FEATURE_SCRIPTING
3204 if ( HasBasic() )
3206 // Initialize Basic
3207 GetBasicManager();
3209 // Save dialog/script container
3210 pImpl->aBasicManager.storeLibrariesToStorage( xStorage );
3212 #endif
3214 if (comphelper::LibreOfficeKit::isActive())
3216 // Because XMLTextFieldExport::ExportFieldDeclarations (called from SwXMLExport)
3217 // calls SwXTextFieldMasters::getByName, which in turn maps property names by
3218 // calling SwStyleNameMapper::GetTextUINameArray, which uses
3219 // SvtSysLocale().GetUILanguageTag() to do the mapping, saving indirectly depends
3220 // on the UI language. This is an unfortunate depenency. Here we use the loader's language.
3221 const LanguageTag viewLanguage = comphelper::LibreOfficeKit::getLanguageTag();
3222 const LanguageTag loadLanguage = SfxLokHelper::getLoadLanguage();
3224 // Use the default language for saving and restore later if necessary.
3225 bool restoreLanguage = false;
3226 if (viewLanguage != loadLanguage)
3228 restoreLanguage = true;
3229 comphelper::LibreOfficeKit::setLanguageTag(loadLanguage);
3232 // Restore the view's original language automatically and as necessary.
3233 const ::comphelper::ScopeGuard aGuard(
3234 [&viewLanguage, restoreLanguage]()
3236 if (restoreLanguage
3237 && viewLanguage != comphelper::LibreOfficeKit::getLanguageTag())
3238 comphelper::LibreOfficeKit::setLanguageTag(viewLanguage);
3241 return SaveAs(rMedium);
3244 return SaveAs( rMedium );
3246 else return false;
3249 uno::Reference< embed::XStorage > const & SfxObjectShell::GetStorage()
3251 if ( !pImpl->m_xDocStorage.is() )
3253 OSL_ENSURE( pImpl->m_bCreateTempStor, "The storage must exist already!" );
3254 try {
3255 // no notification is required the storage is set the first time
3256 pImpl->m_xDocStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
3257 OSL_ENSURE( pImpl->m_xDocStorage.is(), "The method must either return storage or throw exception!" );
3259 SetupStorage( pImpl->m_xDocStorage, SOFFICE_FILEFORMAT_CURRENT, false );
3260 pImpl->m_bCreateTempStor = false;
3261 if (!utl::ConfigManager::IsFuzzing())
3262 SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
3264 catch( uno::Exception& )
3266 // TODO/LATER: error handling?
3267 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::GetStorage");
3271 OSL_ENSURE( pImpl->m_xDocStorage.is(), "The document storage must be created!" );
3272 return pImpl->m_xDocStorage;
3276 void SfxObjectShell::SaveChildren( bool bObjectsOnly )
3278 if ( pImpl->mxObjectContainer )
3280 bool bOasis = ( SotStorage::GetVersion( GetStorage() ) > SOFFICE_FILEFORMAT_60 );
3281 GetEmbeddedObjectContainer().StoreChildren(bOasis,bObjectsOnly);
3285 bool SfxObjectShell::SaveAsChildren( SfxMedium& rMedium )
3287 uno::Reference < embed::XStorage > xStorage = rMedium.GetStorage();
3288 if ( !xStorage.is() )
3289 return false;
3291 if ( xStorage == GetStorage() )
3293 SaveChildren();
3294 return true;
3297 if ( pImpl->mxObjectContainer )
3299 bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
3300 GetEmbeddedObjectContainer().StoreAsChildren(bOasis,SfxObjectCreateMode::EMBEDDED == eCreateMode,xStorage);
3303 uno::Sequence<OUString> aExceptions;
3304 if (const SfxBoolItem* pNoEmbDS
3305 = SfxItemSet::GetItem(rMedium.GetItemSet(), SID_NO_EMBEDDED_DS, false))
3307 // Don't save data source in case a temporary is being saved for preview in MM wizard
3308 if (pNoEmbDS->GetValue())
3309 aExceptions = uno::Sequence<OUString>{ "EmbeddedDatabase" };
3312 return CopyStoragesOfUnknownMediaType(GetStorage(), xStorage, aExceptions);
3315 bool SfxObjectShell::SaveCompletedChildren()
3317 bool bResult = true;
3319 if ( pImpl->mxObjectContainer )
3321 const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
3322 for ( const auto& rName : aNames )
3324 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
3325 OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
3326 if ( xObj.is() )
3328 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
3329 if ( xPersist.is() )
3333 xPersist->saveCompleted( false/*bSuccess*/ );
3335 catch( uno::Exception& )
3337 // TODO/LATER: error handling
3338 bResult = false;
3339 break;
3346 return bResult;
3349 bool SfxObjectShell::SwitchChildrenPersistance( const uno::Reference< embed::XStorage >& xStorage,
3350 bool bForceNonModified )
3352 if ( !xStorage.is() )
3354 // TODO/LATER: error handling
3355 return false;
3358 if ( pImpl->mxObjectContainer )
3359 pImpl->mxObjectContainer->SetPersistentEntries(xStorage,bForceNonModified);
3361 return true;
3364 // Never call this method directly, always use the DoSaveCompleted call
3365 bool SfxObjectShell::SaveCompleted( const uno::Reference< embed::XStorage >& xStorage )
3367 bool bResult = false;
3368 bool bSendNotification = false;
3369 uno::Reference< embed::XStorage > xOldStorageHolder;
3371 // check for wrong creation of object container
3372 bool bHasContainer( pImpl->mxObjectContainer );
3374 if ( !xStorage.is() || xStorage == GetStorage() )
3376 // no persistence change
3377 bResult = SaveCompletedChildren();
3379 else
3381 if ( pImpl->mxObjectContainer )
3382 GetEmbeddedObjectContainer().SwitchPersistence( xStorage );
3384 bResult = SwitchChildrenPersistance( xStorage, true );
3387 if ( bResult )
3389 if ( xStorage.is() && pImpl->m_xDocStorage != xStorage )
3391 // make sure that until the storage is assigned the object
3392 // container is not created by accident!
3393 DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
3394 xOldStorageHolder = pImpl->m_xDocStorage;
3395 pImpl->m_xDocStorage = xStorage;
3396 bSendNotification = true;
3398 if ( IsEnableSetModified() )
3399 SetModified( false );
3402 else
3404 if ( pImpl->mxObjectContainer )
3405 GetEmbeddedObjectContainer().SwitchPersistence( pImpl->m_xDocStorage );
3407 // let already successfully connected objects be switched back
3408 SwitchChildrenPersistance( pImpl->m_xDocStorage, true );
3411 if ( bSendNotification )
3413 SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
3416 return bResult;
3419 static bool StoragesOfUnknownMediaTypeAreCopied_Impl( const uno::Reference< embed::XStorage >& xSource,
3420 const uno::Reference< embed::XStorage >& xTarget )
3422 OSL_ENSURE( xSource.is() && xTarget.is(), "Source and/or target storages are not available!" );
3423 if ( !xSource.is() || !xTarget.is() || xSource == xTarget )
3424 return true;
3428 const uno::Sequence< OUString > aSubElements = xSource->getElementNames();
3429 for ( const auto& rSubElement : aSubElements )
3431 if ( xSource->isStorageElement( rSubElement ) )
3433 OUString aMediaType;
3434 static const OUStringLiteral aMediaTypePropName( u"MediaType" );
3435 bool bGotMediaType = false;
3439 uno::Reference< embed::XOptimizedStorage > xOptStorage( xSource, uno::UNO_QUERY_THROW );
3440 bGotMediaType =
3441 ( xOptStorage->getElementPropertyValue( rSubElement, aMediaTypePropName ) >>= aMediaType );
3443 catch( uno::Exception& )
3446 if ( !bGotMediaType )
3448 uno::Reference< embed::XStorage > xSubStorage;
3449 try {
3450 xSubStorage = xSource->openStorageElement( rSubElement, embed::ElementModes::READ );
3451 } catch( uno::Exception& )
3454 if ( !xSubStorage.is() )
3456 xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
3457 xSource->copyStorageElementLastCommitTo( rSubElement, xSubStorage );
3460 uno::Reference< beans::XPropertySet > xProps( xSubStorage, uno::UNO_QUERY_THROW );
3461 xProps->getPropertyValue( aMediaTypePropName ) >>= aMediaType;
3464 // TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist
3465 // probably it should be placed in the MimeType-ClassID table or in standalone table
3466 if ( !aMediaType.isEmpty()
3467 && aMediaType != "application/vnd.sun.star.oleobject" )
3469 css::datatransfer::DataFlavor aDataFlavor;
3470 aDataFlavor.MimeType = aMediaType;
3471 SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
3473 switch ( nFormat )
3475 case SotClipboardFormatId::STARWRITER_60 :
3476 case SotClipboardFormatId::STARWRITERWEB_60 :
3477 case SotClipboardFormatId::STARWRITERGLOB_60 :
3478 case SotClipboardFormatId::STARDRAW_60 :
3479 case SotClipboardFormatId::STARIMPRESS_60 :
3480 case SotClipboardFormatId::STARCALC_60 :
3481 case SotClipboardFormatId::STARCHART_60 :
3482 case SotClipboardFormatId::STARMATH_60 :
3483 case SotClipboardFormatId::STARWRITER_8:
3484 case SotClipboardFormatId::STARWRITERWEB_8:
3485 case SotClipboardFormatId::STARWRITERGLOB_8:
3486 case SotClipboardFormatId::STARDRAW_8:
3487 case SotClipboardFormatId::STARIMPRESS_8:
3488 case SotClipboardFormatId::STARCALC_8:
3489 case SotClipboardFormatId::STARCHART_8:
3490 case SotClipboardFormatId::STARMATH_8:
3491 break;
3493 default:
3495 if ( !xTarget->hasByName( rSubElement ) )
3496 return false;
3503 catch( uno::Exception& )
3505 SAL_WARN( "sfx.doc", "Can not check storage consistency!" );
3508 return true;
3511 bool SfxObjectShell::SwitchPersistence( const uno::Reference< embed::XStorage >& xStorage )
3513 bool bResult = false;
3514 // check for wrong creation of object container
3515 bool bHasContainer( pImpl->mxObjectContainer );
3516 if ( xStorage.is() )
3518 if ( pImpl->mxObjectContainer )
3519 GetEmbeddedObjectContainer().SwitchPersistence( xStorage );
3520 bResult = SwitchChildrenPersistance( xStorage );
3522 // TODO/LATER: substorages that have unknown mimetypes probably should be copied to the target storage here
3523 OSL_ENSURE( StoragesOfUnknownMediaTypeAreCopied_Impl( pImpl->m_xDocStorage, xStorage ),
3524 "Some of substorages with unknown mimetypes is lost!" );
3527 if ( bResult )
3529 // make sure that until the storage is assigned the object container is not created by accident!
3530 DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
3531 if ( pImpl->m_xDocStorage != xStorage )
3532 DoSaveCompleted( new SfxMedium( xStorage, GetMedium()->GetBaseURL() ) );
3534 if ( IsEnableSetModified() )
3535 SetModified(); // ???
3538 return bResult;
3541 bool SfxObjectShell::CopyStoragesOfUnknownMediaType(const uno::Reference< embed::XStorage >& xSource,
3542 const uno::Reference< embed::XStorage >& xTarget,
3543 const uno::Sequence<OUString>& rExceptions)
3545 // This method does not commit the target storage and should not do it
3546 bool bResult = true;
3550 const css::uno::Sequence<OUString> aSubElementNames = xSource->getElementNames();
3551 for (const OUString& rSubElement : aSubElementNames)
3553 if (std::find(rExceptions.begin(), rExceptions.end(), rSubElement) != rExceptions.end())
3554 continue;
3556 if (rSubElement == "Configurations")
3558 // The workaround for compatibility with SO7, "Configurations" substorage must be preserved
3559 if (xSource->isStorageElement(rSubElement))
3561 OSL_ENSURE(!xTarget->hasByName(rSubElement), "The target storage is an output "
3562 "storage, the element should not "
3563 "exist in the target!");
3565 xSource->copyElementTo(rSubElement, xTarget, rSubElement);
3568 else if (xSource->isStorageElement(rSubElement))
3570 OUString aMediaType;
3571 static const OUStringLiteral aMediaTypePropName( u"MediaType" );
3572 bool bGotMediaType = false;
3576 uno::Reference< embed::XOptimizedStorage > xOptStorage( xSource, uno::UNO_QUERY_THROW );
3577 bGotMediaType = (xOptStorage->getElementPropertyValue(rSubElement, aMediaTypePropName)
3578 >>= aMediaType);
3580 catch( uno::Exception& )
3583 if ( !bGotMediaType )
3585 uno::Reference< embed::XStorage > xSubStorage;
3586 try {
3587 xSubStorage
3588 = xSource->openStorageElement(rSubElement, embed::ElementModes::READ);
3589 } catch( uno::Exception& )
3592 if ( !xSubStorage.is() )
3594 // TODO/LATER: as optimization in future a substorage of target storage could be used
3595 // instead of the temporary storage; this substorage should be removed later
3596 // if the MimeType is wrong
3597 xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
3598 xSource->copyStorageElementLastCommitTo(rSubElement, xSubStorage);
3601 uno::Reference< beans::XPropertySet > xProps( xSubStorage, uno::UNO_QUERY_THROW );
3602 xProps->getPropertyValue( aMediaTypePropName ) >>= aMediaType;
3605 // TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist
3606 // probably it should be placed in the MimeType-ClassID table or in standalone table
3607 if ( !aMediaType.isEmpty()
3608 && aMediaType != "application/vnd.sun.star.oleobject" )
3610 css::datatransfer::DataFlavor aDataFlavor;
3611 aDataFlavor.MimeType = aMediaType;
3612 SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
3614 switch ( nFormat )
3616 case SotClipboardFormatId::STARWRITER_60 :
3617 case SotClipboardFormatId::STARWRITERWEB_60 :
3618 case SotClipboardFormatId::STARWRITERGLOB_60 :
3619 case SotClipboardFormatId::STARDRAW_60 :
3620 case SotClipboardFormatId::STARIMPRESS_60 :
3621 case SotClipboardFormatId::STARCALC_60 :
3622 case SotClipboardFormatId::STARCHART_60 :
3623 case SotClipboardFormatId::STARMATH_60 :
3624 case SotClipboardFormatId::STARWRITER_8:
3625 case SotClipboardFormatId::STARWRITERWEB_8:
3626 case SotClipboardFormatId::STARWRITERGLOB_8:
3627 case SotClipboardFormatId::STARDRAW_8:
3628 case SotClipboardFormatId::STARIMPRESS_8:
3629 case SotClipboardFormatId::STARCALC_8:
3630 case SotClipboardFormatId::STARCHART_8:
3631 case SotClipboardFormatId::STARMATH_8:
3632 break;
3634 default:
3636 OSL_ENSURE(rSubElement == "Configurations2"
3637 || nFormat == SotClipboardFormatId::STARBASE_8
3638 || !xTarget->hasByName(rSubElement),
3639 "The target storage is an output storage, the element "
3640 "should not exist in the target!");
3642 if (!xTarget->hasByName(rSubElement))
3644 xSource->copyElementTo(rSubElement, xTarget, rSubElement);
3652 catch( uno::Exception& )
3654 bResult = false;
3655 // TODO/LATER: a specific error could be provided
3658 return bResult;
3661 bool SfxObjectShell::GenerateAndStoreThumbnail(bool bEncrypted, const uno::Reference<embed::XStorage>& xStorage)
3663 //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
3664 bIsInGenerateThumbnail = true;
3666 bool bResult = false;
3670 uno::Reference<embed::XStorage> xThumbnailStorage = xStorage->openStorageElement("Thumbnails", embed::ElementModes::READWRITE);
3672 if (xThumbnailStorage.is())
3674 uno::Reference<io::XStream> xStream = xThumbnailStorage->openStreamElement("thumbnail.png", embed::ElementModes::READWRITE);
3676 if (xStream.is() && WriteThumbnail(bEncrypted, xStream))
3678 uno::Reference<embed::XTransactedObject> xTransactedObject(xThumbnailStorage, uno::UNO_QUERY_THROW);
3679 xTransactedObject->commit();
3680 bResult = true;
3684 catch( uno::Exception& )
3688 //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
3689 bIsInGenerateThumbnail = false;
3691 return bResult;
3694 bool SfxObjectShell::WriteThumbnail(bool bEncrypted, const uno::Reference<io::XStream>& xStream)
3696 bool bResult = false;
3698 if (!xStream.is())
3699 return false;
3703 uno::Reference<io::XTruncate> xTruncate(xStream->getOutputStream(), uno::UNO_QUERY_THROW);
3704 xTruncate->truncate();
3706 uno::Reference <beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
3707 if (xSet.is())
3708 xSet->setPropertyValue("MediaType", uno::makeAny(OUString("image/png")));
3709 if (bEncrypted)
3711 const OUString sResID = GraphicHelper::getThumbnailReplacementIDByFactoryName_Impl(
3712 GetFactory().GetFactoryName());
3713 if (!sResID.isEmpty())
3714 bResult = GraphicHelper::getThumbnailReplacement_Impl(sResID, xStream);
3716 else
3718 std::shared_ptr<GDIMetaFile> xMetaFile = GetPreviewMetaFile();
3719 if (xMetaFile)
3721 bResult = GraphicHelper::getThumbnailFormatFromGDI_Impl(xMetaFile.get(), xStream);
3725 catch(uno::Exception&)
3728 return bResult;
3731 void SfxObjectShell::UpdateLinks()
3735 bool SfxObjectShell::LoadExternal( SfxMedium& )
3737 // Not implemented. It's an error if the code path ever comes here.
3738 assert(false);
3739 return false;
3742 bool SfxObjectShell::InsertGeneratedStream(SfxMedium&,
3743 uno::Reference<text::XTextRange> const&)
3745 // Not implemented. It's an error if the code path ever comes here.
3746 assert(false);
3747 return false;
3750 bool SfxObjectShell::IsConfigOptionsChecked() const
3752 return pImpl->m_bConfigOptionsChecked;
3755 void SfxObjectShell::SetConfigOptionsChecked( bool bChecked )
3757 pImpl->m_bConfigOptionsChecked = bChecked;
3760 void SfxObjectShell::SetMacroCallsSeenWhileLoading()
3762 pImpl->m_bMacroCallsSeenWhileLoading = true;
3765 bool SfxObjectShell::GetMacroCallsSeenWhileLoading() const
3767 if (officecfg::Office::Common::Security::Scripting::CheckDocumentEvents::get())
3768 return pImpl->m_bMacroCallsSeenWhileLoading;
3769 return false;
3772 bool SfxObjectShell::QuerySaveSizeExceededModules_Impl( const uno::Reference< task::XInteractionHandler >& xHandler )
3774 #if !HAVE_FEATURE_SCRIPTING
3775 (void) xHandler;
3776 #else
3777 if ( !HasBasic() )
3778 return true;
3780 if ( !pImpl->aBasicManager.isValid() )
3781 GetBasicManager();
3782 std::vector< OUString > sModules;
3783 if ( xHandler.is() )
3785 if( pImpl->aBasicManager.LegacyPsswdBinaryLimitExceeded( sModules ) )
3787 rtl::Reference<ModuleSizeExceeded> pReq = new ModuleSizeExceeded( sModules );
3788 xHandler->handle( pReq );
3789 return pReq->isApprove();
3792 #endif
3793 // No interaction handler, default is to continue to save
3794 return true;
3797 bool SfxObjectShell::QueryAllowExoticFormat_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& rURL, const OUString& rFilterUIName )
3799 if ( SvtSecurityOptions::isTrustedLocationUri( rURL ) )
3801 // Always load from trusted location
3802 return true;
3804 if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 0 )
3806 // Refuse loading without question
3807 return false;
3809 else if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 2 )
3811 // Always load without question
3812 return true;
3814 else if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 1 && xHandler.is() )
3816 // Display a warning and let the user decide
3817 rtl::Reference<ExoticFileLoadException> xException(new ExoticFileLoadException( rURL, rFilterUIName ));
3818 xHandler->handle( xException );
3819 return xException->isApprove();
3821 // No interaction handler, default is to continue to load
3822 return true;
3825 uno::Reference< task::XInteractionHandler > SfxObjectShell::getInteractionHandler() const
3827 uno::Reference< task::XInteractionHandler > xRet;
3828 if ( GetMedium() )
3829 xRet = GetMedium()->GetInteractionHandler();
3830 return xRet;
3833 OUString SfxObjectShell::getDocumentBaseURL() const
3835 return GetMedium()->GetBaseURL();
3838 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */