DBG_ASSERT( xVersion.is(), "The method must throw an exception if the storage can not be opened!" ); if ( !xVersion.is() ) throw uno::RuntimeException();
uno::Reference< io::XStream > xVerStream = xVersion->openStreamElement(
aStreamName,
embed::ElementModes::READWRITE );
DBG_ASSERT( xVerStream.is(), "The method must throw an exception if the storage can not be opened!" ); if ( !xVerStream.is() ) throw uno::RuntimeException();
// the password will be transferred from the xStorage to xTempStorage by storage implementation
xStorage->copyToStorage( xTempStorage );
// the temporary storage was committed by the previous method and it will die by refcount
} catch ( uno::Exception& )
{
SAL_WARN( "sfx.doc", "Creation of a storage copy is failed!" );
::utl::UCBContentHelper::Kill( aTempURL );
aTempURL.clear();
// TODO/LATER: may need error code setting based on exception
SetError(ERRCODE_IO_GENERAL);
}
}
if ( nClipFormat == SotClipboardFormatId::NONE ) return;
// basic doesn't have a ClipFormat // without MediaType the storage is not really usable, but currently the BasicIDE still // is an SfxObjectShell and so we can't take this as an error
datatransfer::DataFlavor aDataFlavor;
SotExchange::GetFormatDataFlavor( nClipFormat, aDataFlavor ); if ( aDataFlavor.MimeType.isEmpty() ) return;
// the default values, that should be used for ODF1.1 and older formats
uno::Sequence< beans::NamedValue > aEncryptionAlgs
{
{ u"StartKeyGenerationAlgorithm"_ustr, css::uno::Any(xml::crypto::DigestID::SHA1) },
{ u"EncryptionAlgorithm"_ustr, css::uno::Any(xml::crypto::CipherID::BLOWFISH_CFB_8) },
{ u"ChecksumAlgorithm"_ustr, css::uno::Any(xml::crypto::DigestID::SHA1_1K) },
{ u"KeyDerivationFunction"_ustr, css::uno::Any(xml::crypto::KDFID::PBKDF2) },
};
if (nDefVersion >= SvtSaveOptions::ODFSVER_012)
{ try
{ // older versions can not have this property set, it exists only starting from ODF1.2
xProps->setPropertyValue(u"Version"_ustr, getODFVersionAny(nDefVersion));
} catch( uno::Exception& )
{
}
try
{ // set the encryption algorithms accordingly; // the setting does not trigger encryption, // it just provides the format for the case that contents should be encrypted
uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xStorage, uno::UNO_QUERY_THROW );
xEncr->setEncryptionAlgorithms( aEncryptionAlgs );
} catch( uno::Exception& )
{ const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
}
}
void SfxObjectShell::PrepareSecondTryLoad_Impl()
{ // only for internal use
pImpl->m_xDocStorage.clear();
pImpl->mxObjectContainer.reset();
pImpl->nDocumentSignatureState = SignatureState::UNKNOWN;
pImpl->nScriptingSignatureState = SignatureState::UNKNOWN;
pImpl->m_bIsInit = false;
ResetError();
}
{
ModifyBlocker_Impl aBlock( this );
pMedium = new SfxMedium;
pMedium->CanDisposeStorage_Impl( true );
if ( InitNew( nullptr ) )
{ // empty documents always get their macros from the user, so there is no reason to restrict access
pImpl->aMacroMode.allowMacroExecution(); if ( SfxObjectCreateMode::EMBEDDED == eCreateMode )
SetTitle(SfxResId(STR_NONAME));
// now the medium can be disconnected from the storage // the medium is not allowed to dispose the storage so CloseStorage() can be used
pMedium->CloseStorage();
} catch( uno::Exception& )
{
}
return bResult;
}
bool SfxObjectShell::DoLoad( SfxMedium *pMed )
{
ModifyBlocker_Impl aBlock( this );
// initialize static language table so language-related extensions are learned before the document loads
(void)SvtLanguageTable::GetLanguageEntryCount();
//TODO/LATER: make a clear strategy how to handle "UsesStorage" etc. bool bOwnStorageFormat = IsOwnStorageFormat( *pMedium ); bool bHasStorage = IsPackageStorageFormat_Impl( *pMedium ); if ( pMedium->GetFilter() )
{
ErrCode nError = HandleFilter( pMedium, this ); if ( nError != ERRCODE_NONE )
SetError(nError);
if (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARTPRESENTATION)
rSet.Put(SfxUInt16Item(SID_DOC_STARTPRESENTATION, 1));
}
EnableSetModified( false );
// tdf#53614 - don't try to lock file after cancelling the import process if (GetErrorIgnoreWarning() != ERRCODE_ABORT)
pMedium->LockOrigFileOnDemand( true, false ); if ( GetErrorIgnoreWarning() == ERRCODE_NONE && bOwnStorageFormat && ( !pFilter || !( pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) ) )
{
uno::Reference< embed::XStorage > xStorage; if ( pMedium->GetErrorIgnoreWarning() == ERRCODE_NONE )
xStorage = pMedium->GetStorage();
if( xStorage.is() && pMedium->GetLastStorageCreationState() == ERRCODE_NONE )
{
DBG_ASSERT( pFilter, "No filter for storage found!" );
try
{ bool bWarnMediaTypeFallback = false;
// treat the package as broken if the mediatype was retrieved as a fallback
uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
xStorProps->getPropertyValue(u"MediaTypeFallbackUsed"_ustr)
>>= bWarnMediaTypeFallback;
if (pMedium->IsRepairPackage())
{ // the macros in repaired documents should be disabled
pMedium->GetItemSet().Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::NEVER_EXECUTE ) );
// the mediatype was retrieved by using fallback solution but this is a repairing mode // so it is acceptable to open the document if there is no contents that required manifest.xml
bWarnMediaTypeFallback = false;
}
if (bWarnMediaTypeFallback || !xStorage->getElementNames().hasElements())
SetError(ERRCODE_IO_BROKENPACKAGE);
} catch( uno::Exception& )
{ // TODO/LATER: may need error code setting based on exception
SetError(ERRCODE_IO_GENERAL);
}
// Load if ( !GetErrorIgnoreWarning() )
{
pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
pImpl->bModelInitialized = false;
bOk = xStorage.is() && LoadOwnFormat( *pMed ); if ( bOk )
{ // the document loaded from template has no name const SfxBoolItem* pTemplateItem = rSet.GetItem(SID_TEMPLATE, false); if ( !pTemplateItem || !pTemplateItem->GetValue() )
bHasName = true;
} else
SetError(ERRCODE_ABORT);
}
} else
SetError(pMed->GetLastStorageCreationState());
} elseif ( GetErrorIgnoreWarning() == ERRCODE_NONE && InitNew(nullptr) )
{ // set name before ConvertFrom, so that GetSbxObject() already works
bHasName = true;
SetName( SfxResId(STR_NONAME) );
if ( GetErrorIgnoreWarning() == ERRCODE_NONE )
{ // Experimental PDF importing using PDFium. This is currently enabled for LOK only and // we handle it not via XmlFilterAdaptor but a new SdPdfFilter. #if !HAVE_FEATURE_POPPLER
constexpr bool bUsePdfium = true; #else constbool bUsePdfium
= comphelper::LibreOfficeKit::isActive() || getenv("LO_IMPORT_USE_PDFIUM"); #endif constbool bPdfiumImport
= bUsePdfium && pMedium->GetFilter()
&& (pMedium->GetFilter()->GetFilterName() == "draw_pdf_import");
bool bReconnectDde = true; // by default, we try to auto-connect DDE connections. if (pDdeReconnectItem)
bReconnectDde = pDdeReconnectItem->GetValue();
// Count the occurrences of each character within the line. // Skip strings. const sal_Unicode *pEnd = sLine.getStr() + sLine.getLength(); for (const sal_Unicode *p = sLine.getStr(); p < pEnd; p++)
{ if (*p == cStringDelimiter)
{
bIsDelimiter = !bIsDelimiter; continue;
} if (bIsDelimiter) continue;
// If restricted only to common separators then skip the rest if (usetCommonSeps.find(*p) == usetCommonSeps.end()) continue;
auto it_elem = aCharsCount.find(*p); if (it_elem == aCharsCount.cend())
aCharsCount.insert(std::pair<sal_uInt32, sal_uInt32>(*p, 1)); else
it_elem->second ++;
}
if (bIsDelimiter) continue;
nLinesCount ++;
// For each character count the lines that contain it and different number of occurrences. // And the global maximum for the first statistic. for (auto aCurLineChar=aCharsCount.cbegin(); aCurLineChar != aCharsCount.cend(); aCurLineChar++)
{ auto aCurStats = aStats.find(aCurLineChar->first); if (aCurStats == aStats.cend())
aCurStats = aStats.insert(std::pair<sal_Unicode, std::pair<sal_uInt32, sal_uInt32>>(aCurLineChar->first, std::pair<sal_uInt32, sal_uInt32>(1, 1))).first; else
{
aCurStats->second.first ++;// Increment number of lines that contain the current character
std::vector<std::unordered_map<sal_Unicode, sal_uInt32>>::const_iterator aPrevLineChar; for (aPrevLineChar=aLinesCharsCount.cbegin(); aPrevLineChar != aLinesCharsCount.cend(); aPrevLineChar++)
{ auto aPrevStats = aPrevLineChar->find(aCurLineChar->first); if (aPrevStats != aPrevLineChar->cend() && aPrevStats->second == aCurLineChar->second) break;
} if (aPrevLineChar == aLinesCharsCount.cend())
aCurStats->second.second ++;// Increment number of different number of occurrences.
}
// Update the maximum of number of lines that contain the same character. This is a global value. if (nMaxLinesSameChar < aCurStats->second.first)
nMaxLinesSameChar = aCurStats->second.first;
}
SAL_INFO("sfx.doc", "" << nLinesCount << " lines processed in " << tools::Time::GetSystemTicks() - nStartTime << " ms while detecting separator.");
// Compute the global minimum of different number of occurrences. // But only for characters which occur in a maximum number of lines (previously computed). for (auto it=aStats.cbegin(); it != aStats.cend(); it++) if (it->second.first == nMaxLinesSameChar && nMinDiffs > it->second.second)
nMinDiffs = it->second.second;
// Compute the initial list of separators: those with the maximum lines of occurrence and // the minimum of different number of occurrences. for (auto it=aStats.cbegin(); it != aStats.cend(); it++) if (it->second.first == nMaxLinesSameChar && it->second.second == nMinDiffs)
sInitSeps += OUStringChar(it->first);
// If forced to most common or there are multiple separators then pick up only the most common by importance.
sal_Int32 nInitSepIdx;
sal_Int32 nComSepIdx; for (nComSepIdx = 0; nComSepIdx < sCommonSeps.getLength(); nComSepIdx++)
{
sal_Unicode c = sCommonSeps[nComSepIdx]; for (nInitSepIdx = sInitSeps.getLength() - 1; nInitSepIdx >= 0; nInitSepIdx --)
{ if (c == sInitSeps[nInitSepIdx])
{
separators += OUStringChar(c); break;
}
}
}
stream.Seek(nInitPos);
}
void SfxObjectShell::DetectCsvFilterOptions(SvStream& stream, OUString& aFilterOptions)
{
rtl_TextEncoding eCharSet = RTL_TEXTENCODING_DONTKNOW;
std::u16string_view aSeps;
std::u16string_view aDelimiter;
std::u16string_view aCharSet;
std::u16string_view aRest;
OUString aOrigFilterOpts = aFilterOptions; bool bDelimiter = false, bCharSet = false, bRest = false; // This indicates the presence of the token even if empty ;)
// Process earlier as the input could contain express detection instructions. // This is relevant for "automatic" use case. For interactive use case the // FilterOptions should not be detected here (the detection is done before entering // interactive state). For now this is focused on CSV files.
DetectFilterOptions(pMedium);
bool SfxObjectShell::DoSave() // DoSave is only invoked for OLE. Save your own documents in the SFX through // DoSave_Impl order to allow for the creation of backups. // Save in your own format again.
{ bool bOk = false ;
{
ModifyBlocker_Impl aBlock( this );
pImpl->bIsSaving = true;
if (IsOwnStorageFormat(*GetMedium()))
{
SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013; if (!comphelper::IsFuzzing())
{
nDefVersion = GetODFSaneDefaultVersion();
}
uno::Reference<beans::XPropertySet> const xProps(GetMedium()->GetStorage(), uno::UNO_QUERY);
assert(xProps.is()); if (nDefVersion >= SvtSaveOptions::ODFSVER_012) // property exists only since ODF 1.2
{ try// tdf#134582 set Version on embedded objects as they
{ // could have been loaded with a different/old version
xProps->setPropertyValue(u"Version"_ustr, getODFVersionAny(nDefVersion));
} catch (uno::Exception&)
{
TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::DoSave");
}
}
}
if ( IsPackageStorageFormat_Impl( *GetMedium() ) )
{
GetMedium()->GetStorage(); // sets encryption properties if necessary if (GetMedium()->GetErrorCode())
{
SetError(ERRCODE_IO_GENERAL);
} else
{
bOk = true;
} #if HAVE_FEATURE_SCRIPTING if ( HasBasic() )
{ try
{ // The basic and dialogs related contents are still not able to proceed with save operation ( saveTo only ) // so since the document storage is locked a workaround has to be used
uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" ); if ( !xTmpStorage.is() ) throw uno::RuntimeException();
// disconnect from the current storage
pImpl->aBasicManager.setStorage( xTmpStorage );
// store to the current storage
pImpl->aBasicManager.storeLibrariesToStorage( GetMedium()->GetStorage() );
// connect to the current storage back
pImpl->aBasicManager.setStorage( GetMedium()->GetStorage() );
} catch( uno::Exception& )
{
SetError(ERRCODE_IO_GENERAL);
bOk = false;
}
} #endif
}
std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter(); if ( !pFilter )
{ // if no filter was set, use the default filter // this should be changed in the feature, it should be an error!
SAL_WARN( "sfx.doc","No filter set!");
pFilter = GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT );
rMedium.SetFilter(pFilter);
}
// Examine target format to determine whether to query if any password // protected libraries exceed the size we can handler if ( bOwnTarget && !QuerySaveSizeExceededModules_Impl( rMedium.GetInteractionHandler() ) )
{
SetError(ERRCODE_IO_ABORT); returnfalse;
}
// the detection whether the script is changed should be done before saving bool bTryToPreserveScriptSignature = false; // no way to detect whether a filter is oasis format, have to wait for saving process bool bNoPreserveForOasis = false; if ( bOwnSource && bOwnTarget
&& ( pImpl->nScriptingSignatureState == SignatureState::OK
|| pImpl->nScriptingSignatureState == SignatureState::NOTVALIDATED
|| pImpl->nScriptingSignatureState == SignatureState::INVALID ) )
{ // the checking of the library modified state iterates over the libraries, should be done only when required // currently the check is commented out since it is broken, we have to check the signature every time we save // TODO/LATER: let isAnyContainerModified() work!
bTryToPreserveScriptSignature = true; // !pImpl->pBasicManager->isAnyContainerModified(); if ( bTryToPreserveScriptSignature )
{ // check that the storage format stays the same
// preserve only if the same filter has been used // for templates, strip the _template from the filter name for comparison const OUString aMediumFilter = lcl_strip_template(pMedium->GetFilter()->GetFilterName());
bTryToPreserveScriptSignature = pMedium->GetFilter() && pFilter && aMediumFilter == lcl_strip_template(pFilter->GetFilterName());
// signatures were specified in ODF 1.2 but were used since much longer. // LO will still correctly validate an old style signature on an ODF 1.2 // document, but technically this is not correct, so this prevents old // signatures to be copied over to a version 1.2 document
bNoPreserveForOasis = (
(0 <= aODFVersion.compareTo(ODFVER_012_TEXT) && nVersion < SvtSaveOptions::ODFSVER_012) ||
(aODFVersion.isEmpty() && nVersion >= SvtSaveOptions::ODFSVER_012)
);
}
}
uno::Reference<io::XStream> xODFDecryptedInnerPackageStream;
uno::Reference<embed::XStorage> xODFDecryptedInnerPackage;
uno::Sequence<beans::NamedValue> aEncryptionData; if (GetEncryptionData_Impl(&rMedium.GetItemSet(), aEncryptionData))
{
assert(aEncryptionData.getLength() != 0); if (bOwnTarget && ::sfx2::UseODFWholesomeEncryption(nVersion))
{ // when embedded objects are stored here, it should be called from // this function for the root document and encryption data was cleared
assert(GetCreateMode() != SfxObjectCreateMode::EMBEDDED); // clear now to store inner package (+ embedded objects) unencrypted
rMedium.GetItemSet().ClearItem(SID_ENCRYPTIONDATA);
rMedium.GetItemSet().ClearItem(SID_PASSWORD);
xODFDecryptedInnerPackageStream.set(
xContext->getServiceManager()->createInstanceWithContext(
u"com.sun.star.comp.MemoryStream"_ustr, xContext),
UNO_QUERY_THROW);
xODFDecryptedInnerPackage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
PACKAGE_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream,
css::embed::ElementModes::WRITE, xContext, false);
assert(xODFDecryptedInnerPackage.is());
}
}
bool isStreamAndInputStreamCleared(false); // use UCB for case sensitive/insensitive file name comparison if ( !pMedium->GetName().equalsIgnoreAsciiCase("private:stream")
&& !rMedium.GetName().equalsIgnoreAsciiCase("private:stream")
&& ::utl::UCBContentHelper::EqualURLs( pMedium->GetName(), rMedium.GetName() ) )
{ // Do not unlock the file during saving. // need to modify this for WebDAV if this method is called outside of // the process of saving a file
pMedium->DisableUnlockWebDAV();
bStoreToSameLocation = true;
if ( pMedium->DocNeedsFileDateCheck() )
{
rMedium.CheckFileDate( pMedium->GetInitFileDate( false ) ); if (rMedium.GetErrorCode() == ERRCODE_ABORT)
{ // if user cancels the save, exit early to avoid resetting SfxMedium values that // would cause an invalid subsequent filedate check returnfalse;
}
}
// before we overwrite the original file, we will make a backup if there is a demand for that // if the backup is not created here it will be created internally and will be removed in case of successful saving constbool bDoBackup = officecfg::Office::Common::Save::Document::CreateBackup::get() && !comphelper::LibreOfficeKit::isActive(); if ( bDoBackup )
{
rMedium.DoBackup_Impl(/*bForceUsingBackupPath=*/false); if ( rMedium.GetErrorIgnoreWarning() )
{
SetError(rMedium.GetErrorCode());
rMedium.ResetError();
}
}
if ( bStorageBasedSource && bStorageBasedTarget )
{ // The active storage must be switched. The simple saving is not enough. // The problem is that the target medium contains target MediaDescriptor.
// In future the switch of the persistence could be done on stream level: // a new wrapper service will be implemented that allows to exchange // persistence on the fly. So the real persistence will be set // to that stream only after successful commit of the storage. // TODO/LATER: // create wrapper stream based on the URL // create a new storage based on this stream // store to this new storage // commit the new storage // call saveCompleted based with this new storage ( get rid of old storage and "frees" URL ) // commit the wrapper stream ( the stream will connect the URL only on commit, after that it will hold it ) // if the last step is failed the stream should stay to be transacted and should be committed on any flush // so we can forget the stream in any way and the next storage commit will flush it
// TODO/LATER: for now the medium must be closed since it can already contain streams from old medium // in future those streams should not be copied in case a valid target url is provided, // if the url is not provided ( means the document is based on a stream ) this code is not // reachable.
rMedium.CloseAndRelease();
rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects()); if (xODFDecryptedInnerPackageStream.is())
{
assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
} else
{
rMedium.GetOutputStorage();
}
rMedium.SetHasEmbeddedObjects(false);
}
} elseif ( !bStorageBasedSource && !bStorageBasedTarget )
{ // the source and the target formats are alien // just disconnect the stream from the source format // so that the target medium can use it
pMedium->CloseAndRelease();
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true;
rMedium.CreateTempFileNoCopy();
rMedium.GetOutStream();
} elseif ( !bStorageBasedSource && bStorageBasedTarget )
{ // the source format is an alien one but the target // format is an own one so just disconnect the source // medium
pMedium->CloseAndRelease();
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true; if (xODFDecryptedInnerPackageStream.is())
{
assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
} else
{
rMedium.GetOutputStorage();
}
} else// means if ( bStorageBasedSource && !bStorageBasedTarget )
{ // the source format is an own one but the target is // an alien format, just connect the source to temporary // storage
bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
*pMedium, rMedium ); if ( bNeedsDisconnectionOnFail
|| ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
{
pMedium->CloseAndRelease();
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true;
rMedium.CreateTempFileNoCopy();
rMedium.GetOutStream();
}
}
pMedium->DisableUnlockWebDAV(false);
} else
{ // This is SaveAs or export action, prepare the target medium // the alien filters still might write directly to the file, that is of course a bug, // but for now the framework has to be ready for it // TODO/LATER: let the medium be prepared for alien formats as well
rMedium.CloseAndRelease();
isStreamAndInputStreamCleared = true; if ( bStorageBasedTarget )
{
rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects()); if (xODFDecryptedInnerPackageStream.is())
{
assert(!rMedium.GetItemSet().GetItem(SID_STREAM)); // this should set only xStorage, all of the streams remain null
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
} else
{
rMedium.GetOutputStorage();
}
rMedium.SetHasEmbeddedObjects(false);
}
}
// these have been cleared on all paths that don't take above error return
assert(isStreamAndInputStreamCleared); (void) isStreamAndInputStreamCleared;
rMedium.LockOrigFileOnDemand( false, false );
if ( bStorageBasedTarget )
{ if ( rMedium.GetErrorCode() ) returnfalse;
// If the filter is a "cross export" filter ( f.e. a filter for exporting an impress document from // a draw document ), the ClassId of the destination storage is different from the ClassId of this // document. It can be retrieved from the default filter for the desired target format
SotClipboardFormatId nFormat = rMedium.GetFilter()->GetFormat();
SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
std::shared_ptr<const SfxFilter> pFilt = rMatcher.GetFilter4ClipBoardId( nFormat ); if ( pFilt )
{ if ( pFilt->GetServiceName() != rMedium.GetFilter()->GetServiceName() )
{
datatransfer::DataFlavor aDataFlavor;
SotExchange::GetFormatDataFlavor( nFormat, aDataFlavor );
bool bOk = true; // TODO/LATER: get rid of bOk if (bOwnTarget && pFilter && !(pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER))
{
uno::Reference< embed::XStorage > xMedStorage = rMedium.GetStorage(); if (!xMedStorage.is() || rMedium.GetErrorCode())
{ // no saving without storage
pImpl->bForbidReload = bOldStat; returnfalse;
}
// transfer password from the parameters to the storage boolconst bPasswdProvided(aEncryptionData.getLength() != 0);
pFilter = rMedium.GetFilter();
if ( bOk )
{
bOk = false; // currently the case that the storage is the same should be impossible if ( xMedStorage == GetStorage() )
{
OSL_ENSURE( !pVersionItem, "This scenario is impossible currently!" ); // usual save procedure
bOk = Save();
} else
{ // save to target
bOk = SaveAsOwnFormat( rMedium ); if ( bOk && pVersionItem )
{
aTmpVersionURL = CreateTempCopyOfStorage_Impl( xMedStorage );
bOk = !aTmpVersionURL.isEmpty();
}
}
}
//fdo#61320: only store thumbnail image if the corresponding option is enabled in the configuration if ( bOk && officecfg::Office::Common::Save::Document::GenerateThumbnail::get()
&& GetCreateMode() != SfxObjectCreateMode::EMBEDDED && !bPasswdProvided && IsUseThumbnailSave() )
{ // store the thumbnail representation image // the thumbnail is not stored in case of encrypted document if ( !GenerateAndStoreThumbnail( bPasswdProvided, xMedStorage ) )
{ // TODO: error handling
SAL_WARN( "sfx.doc", "Couldn't store thumbnail representation!" );
}
}
if ( bOk )
{ if ( pImpl->bIsSaving || pImpl->bPreserveVersions )
{ try
{ const Sequence < util::RevisionTag > aVersions = rMedium.GetVersionList(); if ( aVersions.hasElements() )
{ // copy the version streams static constexpr OUString aVersionsName( u"Versions"_ustr );
uno::Reference< embed::XStorage > xNewVerStor = xMedStorage->openStorageElement(
aVersionsName,
embed::ElementModes::READWRITE );
uno::Reference< embed::XStorage > xOldVerStor = GetStorage()->openStorageElement(
aVersionsName,
embed::ElementModes::READ ); if ( !xNewVerStor.is() || !xOldVerStor.is() ) throw uno::RuntimeException();
for ( constauto& rVersion : aVersions )
{ if ( xOldVerStor->hasByName( rVersion.Identifier ) )
xOldVerStor->copyElementTo( rVersion.Identifier, xNewVerStor, rVersion.Identifier );
}
uno::Reference< embed::XTransactedObject > xTransact( xNewVerStor, uno::UNO_QUERY ); if ( xTransact.is() )
xTransact->commit();
}
} catch( uno::Exception& )
{
SAL_WARN( "sfx.doc", "Couldn't copy versions!" );
bOk = false; // TODO/LATER: a specific error could be set
}
}
if ( bOk && pVersionItem && !rMedium.IsInCheckIn() )
{ // store a version also const SfxStringItem *pAuthorItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_AUTHOR, false);
// version comment
util::RevisionTag aInfo;
aInfo.Comment = pVersionItem->GetValue();
// version author if ( pAuthorItem )
aInfo.Author = pAuthorItem->GetValue(); else // if not transferred as a parameter, get it from user settings
aInfo.Author = SvtUserOptions().GetFullName();
// add new version information into the versionlist and save the versionlist // the version list must have been transferred from the "old" medium before
rMedium.AddVersion_Impl(aInfo);
rMedium.SaveVersionList_Impl();
bOk = PutURLContentsToVersionStream_Impl(aTmpVersionURL, xMedStorage,
aInfo.Identifier);
} elseif ( bOk && ( pImpl->bIsSaving || pImpl->bPreserveVersions ) )
{
rMedium.SaveVersionList_Impl();
}
}
if ( !aTmpVersionURL.isEmpty() )
::utl::UCBContentHelper::Kill( aTmpVersionURL );
} else
{ // it's a "SaveAs" in an alien format if ( rMedium.GetFilter() && ( rMedium.GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) )
bOk = ExportTo( rMedium ); else
bOk = ConvertTo( rMedium );
// after saving the document, the temporary object storage must be updated // if the old object storage was not a temporary one, it will be updated also, because it will be used // as a source for copying the objects into the new temporary storage that will be created below // updating means: all child objects must be stored into it // ( same as on loading, where these objects are copied to the temporary storage ) // but don't commit these changes, because in the case when the old object storage is not a temporary one, // all changes will be written into the original file !
if( bOk && !bCopyTo ) // we also don't touch any graphical replacements here
SaveChildren( true );
}
if ( bOk )
{
uno::Any mediaType; if (xODFDecryptedInnerPackageStream.is())
{ // before the signature copy closes it
mediaType = uno::Reference<beans::XPropertySet>(xODFDecryptedInnerPackage,
uno::UNO_QUERY_THROW)->getPropertyValue(u"MediaType"_ustr);
}
// if ODF version of oasis format changes on saving the signature should not be preserved if ( bTryToPreserveScriptSignature && bNoPreserveForOasis )
bTryToPreserveScriptSignature = ( SotStorage::GetVersion( rMedium.GetStorage() ) == SOFFICE_FILEFORMAT_60 );
uno::Reference< security::XDocumentDigitalSignatures > xDDSigns; if (bTryToPreserveScriptSignature)
{ // if the scripting code was not changed and it is signed the signature should be preserved // unfortunately at this point we have only information whether the basic code has changed or not // so the only way is to check the signature if the basic was not changed try
{ // get the ODF version of the new medium
OUString aVersion; try
{
uno::Reference < beans::XPropertySet > xPropSet( rMedium.GetStorage(), uno::UNO_QUERY ); if (xPropSet)
xPropSet->getPropertyValue(u"Version"_ustr) >>= aVersion;
} catch( uno::Exception& )
{
}
if ( !aScriptSignName.isEmpty() )
{ // target medium is still not committed, it should not be closed // commit the package storage and close it, but leave the streams open
rMedium.StorageCommit_Impl();
rMedium.CloseStorage();
// signature must use Zip storage, not Package storage
uno::Reference<embed::XStorage> const xReadOrig(
pMedium->GetScriptingStorageToSign_Impl());
uno::Reference<embed::XStorage> xTarget; if (xODFDecryptedInnerPackageStream.is())
{
xTarget = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
ZIP_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream);
} else
{
xTarget = rMedium.GetZipStorageToSign_Impl(false);
}
// commit the ZipStorage from target medium
xTransact.set( xTarget, uno::UNO_QUERY ); if ( xTransact.is() )
xTransact->commit(); if (xODFDecryptedInnerPackageStream.is())
{ // recreate, to have it with copied sig
xODFDecryptedInnerPackage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
PACKAGE_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream,
css::embed::ElementModes::WRITE, xContext, false);
}
} else
{ // it should not happen, the copies signature is invalid! // throw the changes away
SAL_WARN( "sfx.doc", "An invalid signature was copied!" );
}
}
}
} catch( uno::Exception& )
{
}
rMedium.CloseZipStorage_Impl();
}
if (xODFDecryptedInnerPackageStream.is())
{
rMedium.StorageCommit_Impl(); // prevent dispose as inner storage will be needed later
assert(!rMedium.WillDisposeStorageOnClose_Impl());
rMedium.CloseStorage(); // restore encryption for outer package, note: disable for debugging
rMedium.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
assert(xODFDecryptedInnerPackageStream.is()); // now create the outer storage
uno::Reference<embed::XStorage> const xOuterStorage(rMedium.GetOutputStorage());
assert(xOuterStorage.is());
assert(!rMedium.GetErrorCode()); // the outer storage needs the same properties as the inner one boolconst isTemplate{rMedium.GetFilter()->IsOwnTemplateFormat()};
SetupStorage(xOuterStorage, SOFFICE_FILEFORMAT_CURRENT, isTemplate);
if ( sName != sNewName )
GetMedium( )->SwitchDocumentToFile( sNewName );
if (xODFDecryptedInnerPackageStream.is())
{ // set the inner storage on the medium again, after Switch
rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
}
if ( bOk )
{ // if the target medium is an alien format and the "old" medium was an own format and the "old" medium // has a name, the object storage must be exchanged, because now we need a new temporary storage // as object storage if ( !bCopyTo && bStorageBasedSource && !bStorageBasedTarget )
{ if ( bStoreToSameLocation )
{ // if the old medium already disconnected from document storage, the storage still must // be switched if backup file is used if ( bNeedsDisconnectionOnFail )
ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
} elseif (!pMedium->GetName().isEmpty()
|| ( pMedium->HasStorage_Impl() && pMedium->WillDisposeStorageOnClose_Impl() ) )
{
OSL_ENSURE(!pMedium->GetName().isEmpty(), "Fallback is used, the medium without name should not dispose the storage!"); // copy storage of old medium to new temporary storage and take this over if( !ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
{
SAL_WARN( "sfx.doc", "Process after storing has failed." );
bOk = false;
}
}
}
} else
{
SAL_WARN( "sfx.doc", "Storing has failed." );
// in case the document storage was connected to backup temporarily it must be disconnected now if ( bNeedsDisconnectionOnFail )
ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
}
}
// unlock user interface
aLockUIGuard.Unlock();
pImpl->bForbidReload = bOldStat;
// ucbhelper::Content is unable to do anything useful with a private:stream if (bOk && !rMedium.GetName().equalsIgnoreAsciiCase("private:stream"))
{ try
{
::ucbhelper::Content aContent( rMedium.GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties(); if ( xProps.is() )
{ static constexpr OUString aAuthor( u"Author"_ustr ); static constexpr OUString aKeywords( u"Keywords"_ustr ); static constexpr OUString aSubject( u"Subject"_ustr );
bool SfxObjectShell::DisconnectStorage_Impl( SfxMedium& rSrcMedium, SfxMedium& rTargetMedium )
{ // this method disconnects the storage from source medium, and attaches it to the backup created by the target medium
bool bResult = false; if ( xStorage == pImpl->m_xDocStorage )
{ try
{
uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW ); const OUString aBackupURL = rTargetMedium.GetBackup_Impl(); if ( aBackupURL.isEmpty() )
{ // the backup could not be created, try to disconnect the storage and close the source SfxMedium // in this case the optimization is not possible, connect storage to a temporary file
rTargetMedium.ResetError();
xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
rSrcMedium.CanDisposeStorage_Impl( false );
rSrcMedium.Close();
// now try to create the backup
rTargetMedium.GetBackup_Impl();
} else
{ // the following call will only compare stream sizes // TODO/LATER: this is a very risky part, since if the URL contents are different from the storage // contents, the storage will be broken
xOptStorage->attachToURL( aBackupURL, true );
// the storage is successfully attached to backup, thus it is owned by the document not by the medium
rSrcMedium.CanDisposeStorage_Impl( false );
bResult = true;
}
} catch ( uno::Exception& )
{}
} return bResult;
}
if ( xStorage.is() )
{ try
{ // the empty argument means that the storage will create temporary stream itself
uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
// the storage is successfully disconnected from the original sources, thus the medium must not dispose it if ( pMediumArg )
pMediumArg->CanDisposeStorage_Impl( false );
bResult = true;
} catch( uno::Exception& )
{
}
// if switching of the storage does not work for any reason ( nonroot storage for example ) use the old method if ( !bResult ) try
{
uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" ); if ( !xTmpStorage.is() ) throw uno::RuntimeException();
// TODO/LATER: may be it should be done in SwitchPersistence also // TODO/LATER: find faster way to copy storage; perhaps sharing with backup?!
xStorage->copyToStorage( xTmpStorage );
bResult = SaveCompleted( xTmpStorage );
if ( bResult )
{
pImpl->aBasicManager.setStorage( xTmpStorage );
if (pImpl->xBasicLibraries)
pImpl->xBasicLibraries->setRootStorage( xTmpStorage ); if (pImpl->xDialogLibraries)
pImpl->xDialogLibraries->setRootStorage( xTmpStorage );
}
} catch( uno::Exception& )
{}
if ( !bResult )
{ // TODO/LATER: may need error code setting based on exception
SetError(ERRCODE_IO_GENERAL);
}
} elseif (!GetMedium()->GetFilter()->IsOwnFormat())
bResult = true;
Any a = xPropSet->getPropertyValue(u"MediaType"_ustr);
OUString aMediaType; if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
{
SAL_WARN( "sfx.doc", "The mediatype must be set already!" );
SetupStorage( xNewStor, SOFFICE_FILEFORMAT_CURRENT, false );
}
pImpl->bIsSaving = false;
bOk = SaveAsOwnFormat( rMedium );
if ( bCommit )
{ try {
uno::Reference< embed::XTransactedObject > xTransact( xNewStor, uno::UNO_QUERY_THROW );
xTransact->commit();
} catch( uno::Exception& )
{
SAL_WARN( "sfx.doc", "The storage was not committed on DoSaveAs!" );
}
}
return bOk;
}
// TODO/LATER: may be the call must be removed completely bool SfxObjectShell::DoSaveAs( SfxMedium& rMedium )
{ // here only root storages are included, which are stored via temp file
rMedium.CreateTempFileNoCopy();
SetError(rMedium.GetErrorCode()); if ( GetErrorIgnoreWarning() ) returnfalse;
// copy version list from "old" medium to target medium, so it can be used on saving if ( pImpl->bPreserveVersions )
rMedium.TransferVersionList_Impl( *pMedium );
// when the package based medium is broken and has no storage or if the storage // is the same as the document storage the current document storage should be preserved
xStorage = pMedium->GetStorage();
bOk = SaveCompleted( xStorage ); if ( bOk && xStorage.is() && xOld != xStorage
&& (!pOld || !pOld->HasStorage_Impl() || xOld != pOld->GetStorage() ) )
{ // old own storage was not controlled by old Medium -> dispose it try {
xOld->dispose();
} catch( uno::Exception& )
{ // the storage is disposed already // can happen during reload scenario when the medium has // disposed it during the closing // will be fixed in one of the next milestones
}
}
} else
{ if (pImpl->m_bSavingForSigning && pFilter && pFilter->GetSupportsSigning()) // So that pMedium->pImpl->xStream becomes a non-empty // reference, and at the end we attempt locking again in // SfxMedium::LockOrigFileOnDemand().
pMedium->GetMedium_Impl();
// TODO/LATER: may be this code will be replaced, but not sure // Set storage in document library containers
pImpl->aBasicManager.setStorage( xStorage );
if (pImpl->xBasicLibraries)
pImpl->xBasicLibraries->setRootStorage( xStorage ); if (pImpl->xDialogLibraries)
pImpl->xDialogLibraries->setRootStorage( xStorage );
} else
{ if( pMedium )
{ if( pFilter && !IsPackageStorageFormat_Impl( *pMedium ) && (pMedium->GetOpenMode() & StreamMode::WRITE ))
{
pMedium->ReOpen();
bOk = SaveCompletedChildren();
} else
bOk = SaveCompleted( nullptr );
} // either Save or ConvertTo else
bOk = SaveCompleted( nullptr );
}
if ( bOk && pNewMed )
{ if( bMedChanged )
{ delete pOld;
// before the title regenerated the document must lose the signatures
pImpl->nDocumentSignatureState = SignatureState::NOSIGNATURES; if (!bTemplate)
{
pImpl->nScriptingSignatureState = pNewMed->GetCachedSignatureState_Impl();
OSL_ENSURE( pImpl->nScriptingSignatureState != SignatureState::BROKEN, "The signature must not be broken at this place" );
// TODO/LATER: in future the medium must control own signature state, not the document
pNewMed->SetCachedSignatureState_Impl( SignatureState::NOSIGNATURES ); // set the default value back
} else
pNewMed->SetCachedSignatureState_Impl( pImpl->nScriptingSignatureState );
// Set new title if (!pNewMed->GetName().isEmpty() && SfxObjectCreateMode::EMBEDDED != eCreateMode)
InvalidateName();
SetModified(false); // reset only by set medium
Broadcast( SfxHint(SfxHintId::ModeChanged) );
// this is the end of the saving process, it is possible that // the file was changed // between medium commit and this step (attributes change and so on) // so get the file date again if ( pNewMed->DocNeedsFileDateCheck() )
pNewMed->GetInitFileDate( true );
}
}
bool SfxObjectShell::ConvertFrom
(
SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the source file
(for example file name, <SfxFilter>,
Open-Modi and so on) */
)
// #i119492# During loading, some OLE objects like chart will be set // modified flag, so needs to reset the flag to false after loading bool bRtn = xLoader->filter(aArgs); const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames(); for ( constauto& rName : aNames )
{
uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" ); if ( xObj.is() )
{
sal_Int32 nState = xObj->getCurrentState(); if ( nState == embed::EmbedStates::LOADED || nState == embed::EmbedStates::RUNNING ) // means that the object is not active
{
uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY ); if (xModifiable.is() && xModifiable->isModified())
{
uno::Reference<embed::XEmbedPersist> const xPers(xObj, uno::UNO_QUERY);
assert(xPers.is() && "Modified object without persistence!"); // store it before resetting modified!
xPers->storeOwn();
xModifiable->setModified(false);
}
}
}
}
// tdf#107690 import custom document property _MarkAsFinal as SecurityOptOpenReadonly // (before this fix, LibreOffice opened read-only OOXML documents as editable, // also saved and exported _MarkAsFinal=true silently, resulting unintended read-only // warning info bar in MSO)
uno::Reference< document::XDocumentPropertiesSupplier > xPropSupplier(GetModel(), uno::UNO_QUERY_THROW);
uno::Reference<document::XDocumentProperties> xDocProps = xPropSupplier->getDocumentProperties() ;
uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocProps->getUserDefinedProperties(); if (xPropertyContainer.is())
{
uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY); if (xPropertySet.is())
{
uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo(); if (xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName(u"_MarkAsFinal"_ustr))
{
Any anyMarkAsFinal = xPropertySet->getPropertyValue(u"_MarkAsFinal"_ustr); if (
( (anyMarkAsFinal.getValueType() == cppu::UnoType<bool>::get()) && (anyMarkAsFinal.get<bool>()) ) ||
( (anyMarkAsFinal.getValueType() == cppu::UnoType<OUString>::get()) && (anyMarkAsFinal.get<OUString>() == "true") )
)
{
uno::Reference< lang::XMultiServiceFactory > xFactory(GetModel(), uno::UNO_QUERY);
uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
xSettings->setPropertyValue(u"LoadReadonly"_ustr, uno::Any(true));
}
xPropertyContainer->removeProperty(u"_MarkAsFinal"_ustr);
}
}
}
bool SfxObjectShell::ConvertTo
(
SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the target file
(for example file name, <SfxFilter>,
Open-Modi and so on) */
)
// copy the original itemset, but remove the "version" item, because pMediumTmp // is a new medium "from scratch", so no version should be stored into it
std::shared_ptr<SfxItemSet> pSet = std::make_shared<SfxAllItemSet>(pRetrMedium->GetItemSet());
pSet->ClearItem( SID_VERSION );
pSet->ClearItem( SID_DOC_BASEURL );
// copy the version comment and major items for the checkin only if ( pRetrMedium->IsInCheckIn( ) )
{ const SfxPoolItem* pMajor = pArgs->GetItem( SID_DOCINFO_MAJOR ); if ( pMajor )
pSet->Put( *pMajor );
// create a medium as a copy; this medium is only for writing, because it // uses the same name as the original one writing is done through a copy, // that will be transferred to the target (of course after calling HandsOff)
SfxMedium* pMediumTmp = new SfxMedium(pRetrMedium->GetName(), pRetrMedium->GetOpenMode(),
std::move(pFilter), std::move(pSet));
pMediumTmp->SetInCheckIn( pRetrMedium->IsInCheckIn( ) );
pMediumTmp->SetLongName( pRetrMedium->GetLongName() ); if ( pMediumTmp->GetErrorCode() != ERRCODE_NONE )
{
SetError(pMediumTmp->GetErrorIgnoreWarning()); delete pMediumTmp; returnfalse;
}
// copy version list from "old" medium to target medium, so it can be used on saving if (pImpl->bPreserveVersions)
pMediumTmp->TransferVersionList_Impl( *pRetrMedium );
// Save the original interaction handler
Any aOriginalInteract; if (const SfxUnoAnyItem *pItem = pRetrMedium->GetItemSet().GetItemIfSet(SID_INTERACTIONHANDLER, false))
{
aOriginalInteract = pItem->GetValue(); #ifndef NDEBUG // The original pRetrMedium and potential replacement pMediumTmp have the same interaction handler at this point const SfxUnoAnyItem *pMediumItem = pMediumTmp->GetItemSet().GetItemIfSet(SID_INTERACTIONHANDLER, false);
assert(pMediumItem && pMediumItem->GetValue() == aOriginalInteract); #endif
}
// an interaction handler here can acquire only in case of GUI Saving // and should be removed after the saving is done
css::uno::Reference< XInteractionHandler > xInteract; const SfxUnoAnyItem* pxInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pArgs, SID_INTERACTIONHANDLER, false); if ( pxInteractionItem && ( pxInteractionItem->GetValue() >>= xInteract ) && xInteract.is() )
pMediumTmp->GetItemSet().Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, Any( xInteract ) ) );
if (aOriginalInteract.hasValue())
pMediumTmp->GetItemSet().Put(SfxUnoAnyItem(SID_INTERACTIONHANDLER, aOriginalInteract)); else
pMediumTmp->GetItemSet().ClearItem(SID_INTERACTIONHANDLER);
pMediumTmp->GetItemSet().ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
SetError(pMediumTmp->GetErrorCode());
bool bOpen = DoSaveCompleted( pMediumTmp );
DBG_ASSERT(bOpen,"Error handling for DoSaveCompleted not implemented");
} else
{ // transfer error code from medium to objectshell
ErrCodeMsg errCode = pMediumTmp->GetErrorIgnoreWarning();
SetError(errCode);
if (errCode == ERRCODE_ABORT)
{ // avoid doing DoSaveCompleted() which updates the SfxMedium timestamp values // and prevents subsequent filedate checks from being accurate delete pMediumTmp; returnfalse;
}
// reconnect to object storage
DoSaveCompleted();
if (aOriginalInteract.hasValue())
pRetrMedium->GetItemSet().Put(SfxUnoAnyItem(SID_INTERACTIONHANDLER, aOriginalInteract)); else
pRetrMedium->GetItemSet().ClearItem(SID_INTERACTIONHANDLER);
pRetrMedium->GetItemSet().ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT if ( IsDocShared() && !aTempFileURL.isEmpty() )
{ // this is a shared document that has to be disconnected from the old location
FreeSharedFile( aTempFileURL );
if ( pFilter->IsOwnFormat()
&& pFilter->UsesStorage()
&& pFilter->GetVersion() >= SOFFICE_FILEFORMAT_60 )
{ // the target format is the own format // the target document must be shared
SwitchToShared( true, false );
}
} #endif
}
if ( bWasReadonly && !bSaveTo )
Broadcast( SfxHint(SfxHintId::ModeChanged) );
returntrue;
} else returnfalse;
}
bool SfxObjectShell::PreDoSaveAs_Impl(const OUString& rFileName, const OUString& aFilterName,
SfxItemSet const& rItemSet, const uno::Sequence<beans::PropertyValue>& rArgs)
{ // copy all items stored in the itemset of the current medium
std::shared_ptr<SfxAllItemSet> xMergedParams = std::make_shared<SfxAllItemSet>( pMedium->GetItemSet() );
// in "SaveAs" title and password will be cleared ( maybe the new itemset contains new values, otherwise they will be empty ) // #i119366# - As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both. // Also, ( maybe the new itemset contains new values, otherwise they will be empty ) if (xMergedParams->HasItem(SID_ENCRYPTIONDATA))
{ bool bPasswordProtected = true; const SfxUnoAnyItem* pEncryptionDataItem
= xMergedParams->GetItem<SfxUnoAnyItem>(SID_ENCRYPTIONDATA, false); if (pEncryptionDataItem)
{
uno::Sequence<beans::NamedValue> aEncryptionData;
pEncryptionDataItem->GetValue() >>= aEncryptionData; for (constauto& rItem : aEncryptionData)
{ if (rItem.Name == "CryptoType")
{
OUString aValue;
rItem.Value >>= aValue; if (aValue != "StrongEncryptionDataSpace")
{ // This is not just a password protected document. Let's keep encryption data as is.
bPasswordProtected = false;
} break;
}
}
} if (bPasswordProtected)
{ // For password protected documents remove encryption data during "Save as..."
xMergedParams->ClearItem(SID_PASSWORD);
xMergedParams->ClearItem(SID_ENCRYPTIONDATA);
}
}
// "SaveAs" will never store any version information - it's a complete new file !
xMergedParams->ClearItem( SID_VERSION );
// merge the new parameters into the copy // all values present in both itemsets will be overwritten by the new parameters
xMergedParams->Put(rItemSet);
SAL_WARN_IF( xMergedParams->GetItemState( SID_DOC_SALVAGE) >= SfxItemState::SET, "sfx.doc","Salvage item present in Itemset, check the parameters!");
// should be unnecessary - too hot to handle!
xMergedParams->ClearItem( SID_DOC_SALVAGE );
// create a medium for the target URL
SfxMedium *pNewFile = new SfxMedium( rFileName, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, nullptr, xMergedParams );
pNewFile->SetArgs(rArgs);
bool bUseThumbnailSave = IsUseThumbnailSave();
comphelper::ScopeGuard aThumbnailGuard(
[this, bUseThumbnailSave] { this->SetUseThumbnailSave(bUseThumbnailSave); }); const SfxBoolItem* pNoThumbnail = xMergedParams->GetItem<SfxBoolItem>(SID_NO_THUMBNAIL, false); if (pNoThumbnail) // Thumbnail generation should be avoided just for this save.
SetUseThumbnailSave(!pNoThumbnail->GetValue()); else
aThumbnailGuard.dismiss();
// set filter; if no filter is given, take the default filter of the factory if ( !aFilterName.isEmpty() )
{
pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName ) );
if (aFilterName == "writer_pdf_Export")
{
uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions(2); auto pSaveToFilterDataOptions = aSaveToFilterDataOptions.getArray(); bool bRet = false;
if ( pNewFile->GetErrorCode() != ERRCODE_NONE )
{ // creating temporary file failed ( f.e. floppy disk not inserted! )
SetError(pNewFile->GetErrorIgnoreWarning()); delete pNewFile; returnfalse;
}
if (comphelper::LibreOfficeKit::isActive())
{ // Before saving, commit in-flight changes.
TerminateEditing();
}
// check if a "SaveTo" is wanted, no "SaveAs" const SfxBoolItem* pSaveToItem = xMergedParams->GetItem<SfxBoolItem>(SID_SAVETO, false); bool bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED || (pSaveToItem && pSaveToItem->GetValue());
// distinguish between "Save" and "SaveAs"
pImpl->bIsSaving = false;
// copy version list from "old" medium to target medium, so it can be used on saving if ( pImpl->bPreserveVersions )
pNewFile->TransferVersionList_Impl( *pMedium );
// Save the document ( first as temporary file, then transfer to the target URL by committing the medium ) bool bOk = false; if ( !pNewFile->GetErrorCode() && SaveTo_Impl( *pNewFile, nullptr ) )
{ // transfer a possible error from the medium to the document
SetError(pNewFile->GetErrorCode());
// notify the document that saving was done successfully if ( !bCopyTo )
{
bOk = DoSaveCompleted( pNewFile );
} else
bOk = DoSaveCompleted();
if( bOk )
{ if( !bCopyTo )
SetModified( false );
} else
{ // TODO/LATER: the code below must be dead since the storage commit makes all the stuff // and the DoSaveCompleted call should not be able to fail in general
DBG_ASSERT( !bCopyTo, "Error while reconnecting to medium, can't be handled!");
SetError(pNewFile->GetErrorCode());
if ( !bCopyTo )
{ // reconnect to the old medium bool bRet = DoSaveCompleted( pMedium );
DBG_ASSERT( bRet, "Error in DoSaveCompleted, can't be handled!");
}
// TODO/LATER: disconnect the new file from the storage for the case when pure saving is done // if storing has corrupted the file, probably it must be restored either here or // by the storage delete pNewFile;
pNewFile = nullptr;
}
} else
{
SetError(pNewFile->GetErrorCode());
// reconnect to the old storage
DoSaveCompleted();
delete pNewFile;
pNewFile = nullptr;
}
if ( bCopyTo ) delete pNewFile; elseif( !bOk )
SetModified();
return bOk;
}
bool SfxObjectShell::LoadFrom( SfxMedium& /*rMedium*/ )
{
SAL_WARN( "sfx.doc", "Base implementation, must not be called in general!" ); returntrue;
}
uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage(); if ( xStorage.is() )
{ // Password const SfxStringItem* pPasswdItem = rMedium.GetItemSet().GetItem(SID_PASSWORD, false); if ( pPasswdItem || ERRCODE_IO_ABORT != CheckPasswd_Impl( this, pMedium ) )
{ // note: this could be needed in case no interaction handler is // provided (which CheckPasswd_Impl needs) but a password item is, // but it could be done in a better way
uno::Sequence< beans::NamedValue > aEncryptionData; if ( GetEncryptionData_Impl(&pMedium->GetItemSet(), aEncryptionData) )
{ try
{ // the following code must throw an exception in case of failure
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xStorage, aEncryptionData );
} catch( uno::Exception& )
{ // TODO/LATER: handle the error code
}
}
// OASIS templates have own mediatypes (SO7 also actually, but it is too late to use them here) constbool bTemplate = rMedium.GetFilter()->IsOwnTemplateFormat()
&& nVersion > SOFFICE_FILEFORMAT_60;
// Save dialog/script container
pImpl->aBasicManager.storeLibrariesToStorage( xStorage );
} #endif
if (comphelper::LibreOfficeKit::isActive())
{ // Because XMLTextFieldExport::ExportFieldDeclarations (called from SwXMLExport) // calls SwXTextFieldMasters::getByName, which in turn maps property names by // calling SwStyleNameMapper::GetTextUINameArray, which uses // SvtSysLocale().GetUILanguageTag() to do the mapping, saving indirectly depends // on the UI language. This is an unfortunate dependency. Here we use the loader's language. const LanguageTag& viewLanguage = comphelper::LibreOfficeKit::getLanguageTag(); const LanguageTag loadLanguage = SfxLokHelper::getLoadLanguage();
// Use the default language for saving and restore later if necessary. bool restoreLanguage = false; if (viewLanguage != loadLanguage)
{
restoreLanguage = true;
comphelper::LibreOfficeKit::setLanguageTag(loadLanguage);
}
// Restore the view's original language automatically and as necessary. const ::comphelper::ScopeGuard aGuard(
[&viewLanguage, restoreLanguage]()
{ if (restoreLanguage
&& viewLanguage != comphelper::LibreOfficeKit::getLanguageTag())
comphelper::LibreOfficeKit::setLanguageTag(viewLanguage);
});
return SaveAs(rMedium);
}
return SaveAs( rMedium );
} elsereturnfalse;
}
uno::Reference< embed::XStorage > const & SfxObjectShell::GetStorage()
{ if ( !pImpl->m_xDocStorage.is() )
{
OSL_ENSURE( pImpl->m_bCreateTempStor, "The storage must exist already!" ); try { // no notification is required the storage is set the first time
pImpl->m_xDocStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
OSL_ENSURE( pImpl->m_xDocStorage.is(), "The method must either return storage or throw exception!" );
uno::Sequence<OUString> aExceptions; if (const SfxBoolItem* pNoEmbDS = rMedium.GetItemSet().GetItem(SID_NO_EMBEDDED_DS, false))
{ // Don't save data source in case a temporary is being saved for preview in MM wizard if (pNoEmbDS->GetValue())
aExceptions = uno::Sequence<OUString>{ u"EmbeddedDatabase"_ustr };
}
if ( bResult )
{ if ( xStorage.is() && pImpl->m_xDocStorage != xStorage )
{ // make sure that until the storage is assigned the object // container is not created by accident!
DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
xOldStorageHolder = pImpl->m_xDocStorage;
pImpl->m_xDocStorage = xStorage;
bSendNotification = true;
if ( IsEnableSetModified() )
SetModified( false );
}
} else
{ if ( pImpl->mxObjectContainer )
GetEmbeddedObjectContainer().SwitchPersistence( pImpl->m_xDocStorage );
// let already successfully connected objects be switched back
SwitchChildrenPersistence( pImpl->m_xDocStorage, true );
}
if ( bSendNotification )
{
SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
}
// TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist // probably it should be placed in the MimeType-ClassID table or in standalone table if ( !aMediaType.isEmpty()
&& aMediaType != "application/vnd.sun.star.oleobject" )
{
css::datatransfer::DataFlavor aDataFlavor;
aDataFlavor.MimeType = aMediaType;
SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
switch ( nFormat )
{ case SotClipboardFormatId::STARWRITER_60 : case SotClipboardFormatId::STARWRITERWEB_60 : case SotClipboardFormatId::STARWRITERGLOB_60 : case SotClipboardFormatId::STARDRAW_60 : case SotClipboardFormatId::STARIMPRESS_60 : case SotClipboardFormatId::STARCALC_60 : case SotClipboardFormatId::STARCHART_60 : case SotClipboardFormatId::STARMATH_60 : case SotClipboardFormatId::STARWRITER_8: case SotClipboardFormatId::STARWRITERWEB_8: case SotClipboardFormatId::STARWRITERGLOB_8: case SotClipboardFormatId::STARDRAW_8: case SotClipboardFormatId::STARIMPRESS_8: case SotClipboardFormatId::STARCALC_8: case SotClipboardFormatId::STARCHART_8: case SotClipboardFormatId::STARMATH_8: break;
// TODO/LATER: substorages that have unknown mimetypes probably should be copied to the target storage here
OSL_ENSURE( StoragesOfUnknownMediaTypeAreCopied_Impl( pImpl->m_xDocStorage, xStorage ), "Some of substorages with unknown mimetypes is lost!" );
}
if ( bResult )
{ // make sure that until the storage is assigned the object container is not created by accident!
DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" ); if ( pImpl->m_xDocStorage != xStorage )
DoSaveCompleted( new SfxMedium( xStorage, GetMedium()->GetBaseURL() ) );
if ( IsEnableSetModified() )
SetModified(); // ???
}
if (rSubElement == "Configurations")
{ // The workaround for compatibility with SO7, "Configurations" substorage must be preserved if (xSource->isStorageElement(rSubElement))
{
OSL_ENSURE(!xTarget->hasByName(rSubElement), "The target storage is an output " "storage, the element should not " "exist in the target!");
if ( !xSubStorage.is() )
{ // TODO/LATER: as optimization in future a substorage of target storage could be used // instead of the temporary storage; this substorage should be removed later // if the MimeType is wrong
xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
xSource->copyStorageElementLastCommitTo(rSubElement, xSubStorage);
}
// TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist // probably it should be placed in the MimeType-ClassID table or in standalone table if ( !aMediaType.isEmpty()
&& aMediaType != "application/vnd.sun.star.oleobject" )
{
css::datatransfer::DataFlavor aDataFlavor;
aDataFlavor.MimeType = aMediaType;
SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
switch ( nFormat )
{ case SotClipboardFormatId::STARWRITER_60 : case SotClipboardFormatId::STARWRITERWEB_60 : case SotClipboardFormatId::STARWRITERGLOB_60 : case SotClipboardFormatId::STARDRAW_60 : case SotClipboardFormatId::STARIMPRESS_60 : case SotClipboardFormatId::STARCALC_60 : case SotClipboardFormatId::STARCHART_60 : case SotClipboardFormatId::STARMATH_60 : case SotClipboardFormatId::STARWRITER_8: case SotClipboardFormatId::STARWRITERWEB_8: case SotClipboardFormatId::STARWRITERGLOB_8: case SotClipboardFormatId::STARDRAW_8: case SotClipboardFormatId::STARIMPRESS_8: case SotClipboardFormatId::STARCALC_8: case SotClipboardFormatId::STARCHART_8: case SotClipboardFormatId::STARMATH_8: break;
default:
{
OSL_ENSURE(rSubElement == "Configurations2"
|| nFormat == SotClipboardFormatId::STARBASE_8
|| !xTarget->hasByName(rSubElement), "The target storage is an output storage, the element " "should not exist in the target!");
if (!xTarget->hasByName(rSubElement))
{
xSource->copyElementTo(rSubElement, xTarget, rSubElement);
}
}
}
}
}
}
} catch( uno::Exception& )
{
bResult = false; // TODO/LATER: a specific error could be provided
}
return bResult;
}
bool SfxObjectShell::GenerateAndStoreThumbnail(bool bEncrypted, const uno::Reference<embed::XStorage>& xStorage)
{ //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
bIsInGenerateThumbnail = true;
bool SfxObjectShell::LoadExternal( SfxMedium& )
{ // Not implemented. It's an error if the code path ever comes here.
assert(false); returnfalse;
}
bool SfxObjectShell::InsertGeneratedStream(SfxMedium&,
uno::Reference<text::XTextRange> const&)
{ // Not implemented. It's an error if the code path ever comes here.
assert(false); returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.