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. 4 * 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/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 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 . 18 */ 19 20 #include <config_features.h> 21 22 #ifdef UNX 23 #include <sys/stat.h> 24 #endif 25 26 #include <sfx2/docfile.hxx> 27 #include <sfx2/signaturestate.hxx> 28 29 #include <uno/mapping.hxx> 30 #include <com/sun/star/task/InteractionHandler.hpp> 31 #include <com/sun/star/task/XStatusIndicator.hpp> 32 #include <com/sun/star/uno/Reference.h> 33 #include <com/sun/star/ucb/XContent.hpp> 34 #include <com/sun/star/container/XChild.hpp> 35 #include <com/sun/star/document/XDocumentRevisionListPersistence.hpp> 36 #include <com/sun/star/document/LockedDocumentRequest.hpp> 37 #include <com/sun/star/document/LockedOnSavingRequest.hpp> 38 #include <com/sun/star/document/OwnLockOnDocumentRequest.hpp> 39 #include <com/sun/star/document/LockFileIgnoreRequest.hpp> 40 #include <com/sun/star/document/LockFileCorruptRequest.hpp> 41 #include <com/sun/star/document/ChangedByOthersRequest.hpp> 42 #include <com/sun/star/beans/XPropertySet.hpp> 43 #include <com/sun/star/embed/XTransactedObject.hpp> 44 #include <com/sun/star/embed/ElementModes.hpp> 45 #include <com/sun/star/embed/UseBackupException.hpp> 46 #include <com/sun/star/embed/XOptimizedStorage.hpp> 47 #include <com/sun/star/graphic/XGraphic.hpp> 48 #include <com/sun/star/ucb/ContentCreationException.hpp> 49 #include <com/sun/star/ucb/InteractiveIOException.hpp> 50 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp> 51 #include <com/sun/star/ucb/CommandFailedException.hpp> 52 #include <com/sun/star/ucb/CommandAbortedException.hpp> 53 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp> 54 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp> 55 #include <com/sun/star/ucb/Lock.hpp> 56 #include <com/sun/star/ucb/XCommandEnvironment.hpp> 57 #include <com/sun/star/ucb/XContentIdentifierFactory.hpp> 58 #include <com/sun/star/ucb/XContentProvider.hpp> 59 #include <com/sun/star/ucb/XProgressHandler.hpp> 60 #include <com/sun/star/ucb/XCommandInfo.hpp> 61 #include <com/sun/star/io/XOutputStream.hpp> 62 #include <com/sun/star/io/XInputStream.hpp> 63 #include <com/sun/star/io/XTruncate.hpp> 64 #include <com/sun/star/io/XStreamListener.hpp> 65 #include <com/sun/star/io/XSeekable.hpp> 66 #include <com/sun/star/ucb/XSimpleFileAccess.hpp> 67 #include <com/sun/star/lang/XInitialization.hpp> 68 #include <com/sun/star/lang/XSingleServiceFactory.hpp> 69 #include <com/sun/star/ucb/InsertCommandArgument.hpp> 70 #include <com/sun/star/ucb/NameClash.hpp> 71 #include <com/sun/star/ucb/TransferInfo.hpp> 72 #include <com/sun/star/ucb/OpenCommandArgument2.hpp> 73 #include <com/sun/star/ucb/OpenMode.hpp> 74 #include <com/sun/star/beans/NamedValue.hpp> 75 #include <com/sun/star/beans/PropertyValue.hpp> 76 #include <com/sun/star/security/DocumentSignatureInformation.hpp> 77 #include <com/sun/star/security/DocumentDigitalSignatures.hpp> 78 #include <com/sun/star/security/XCertificate.hpp> 79 #include <tools/urlobj.hxx> 80 #include <tools/fileutil.hxx> 81 #include <unotools/configmgr.hxx> 82 #include <unotools/tempfile.hxx> 83 #include <comphelper/fileurl.hxx> 84 #include <comphelper/processfactory.hxx> 85 #include <comphelper/interaction.hxx> 86 #include <comphelper/sequence.hxx> 87 #include <comphelper/simplefileaccessinteraction.hxx> 88 #include <framework/interaction.hxx> 89 #include <unotools/streamhelper.hxx> 90 #include <unotools/localedatawrapper.hxx> 91 #include <utility> 92 #include <svl/stritem.hxx> 93 #include <svl/eitem.hxx> 94 #include <svl/lckbitem.hxx> 95 #include <svtools/sfxecode.hxx> 96 #include <svl/itemset.hxx> 97 #include <svl/intitem.hxx> 98 #include <svtools/svparser.hxx> 99 #include <cppuhelper/weakref.hxx> 100 #include <sal/log.hxx> 101 102 #include <unotools/streamwrap.hxx> 103 104 #include <osl/file.hxx> 105 106 #include <comphelper/storagehelper.hxx> 107 #include <unotools/mediadescriptor.hxx> 108 #include <comphelper/docpasswordhelper.hxx> 109 #include <tools/datetime.hxx> 110 #include <unotools/pathoptions.hxx> 111 #include <svtools/asynclink.hxx> 112 #include <ucbhelper/commandenvironment.hxx> 113 #include <unotools/ucbstreamhelper.hxx> 114 #include <unotools/ucbhelper.hxx> 115 #include <unotools/progresshandlerwrap.hxx> 116 #include <ucbhelper/content.hxx> 117 #include <ucbhelper/interactionrequest.hxx> 118 #include <sot/stg.hxx> 119 #include <sot/storage.hxx> 120 #include <unotools/saveopt.hxx> 121 #include <svl/documentlockfile.hxx> 122 #include <svl/msodocumentlockfile.hxx> 123 #include <com/sun/star/document/DocumentRevisionListPersistence.hpp> 124 125 #include <helper.hxx> 126 #include <sfx2/request.hxx> 127 #include <sfx2/app.hxx> 128 #include <sfx2/frame.hxx> 129 #include <sfx2/fcontnr.hxx> 130 #include <sfx2/docfilt.hxx> 131 #include <sfx2/objsh.hxx> 132 #include <sfx2/docfac.hxx> 133 #include <sfx2/sfxsids.hrc> 134 #include <sfx2/sfxuno.hxx> 135 #include <openflag.hxx> 136 #include <officecfg/Office/Common.hxx> 137 #include <comphelper/propertysequence.hxx> 138 #include <vcl/weld.hxx> 139 #include <vcl/svapp.hxx> 140 #include <tools/diagnose_ex.h> 141 #include <unotools/fltrcfg.hxx> 142 143 #include <com/sun/star/io/WrongFormatException.hpp> 144 145 #include <memory> 146 147 using namespace ::com::sun::star; 148 using namespace ::com::sun::star::graphic; 149 using namespace ::com::sun::star::uno; 150 using namespace ::com::sun::star::ucb; 151 using namespace ::com::sun::star::beans; 152 using namespace ::com::sun::star::io; 153 using namespace ::com::sun::star::security; 154 155 namespace { 156 157 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT 158 159 bool IsSystemFileLockingUsed() 160 { 161 #if HAVE_FEATURE_MACOSX_SANDBOX 162 return true; 163 #else 164 return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get(); 165 #endif 166 } 167 168 169 bool IsOOoLockFileUsed() 170 { 171 #if HAVE_FEATURE_MACOSX_SANDBOX 172 return false; 173 #else 174 return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get(); 175 #endif 176 } 177 178 bool IsLockingUsed() 179 { 180 return officecfg::Office::Common::Misc::UseLocking::get(); 181 } 182 183 #endif 184 185 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT 186 bool IsWebDAVLockingUsed() 187 { 188 return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get(); 189 } 190 #endif 191 192 /// Gets default attributes of a file:// URL. 193 sal_uInt64 GetDefaultFileAttributes(const OUString& rURL) 194 { 195 sal_uInt64 nRet = 0; 196 197 if (!comphelper::isFileUrl(rURL)) 198 return nRet; 199 200 // Make sure the file exists (and create it if not). 201 osl::File aFile(rURL); 202 osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create); 203 if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST) 204 return nRet; 205 206 aFile.close(); 207 208 osl::DirectoryItem aItem; 209 if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None) 210 return nRet; 211 212 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes); 213 if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None) 214 return nRet; 215 216 nRet = aStatus.getAttributes(); 217 return nRet; 218 } 219 220 /// Determines if rURL is safe to move or not. 221 bool IsFileMovable(const INetURLObject& rURL) 222 { 223 #ifdef MACOSX 224 (void)rURL; 225 // Hide extension macOS-specific file property would be lost. 226 return false; 227 #else 228 229 if (rURL.GetProtocol() != INetProtocol::File) 230 // Not a file:// URL. 231 return false; 232 233 #ifdef UNX 234 OUString sPath = rURL.getFSysPath(FSysStyle::Unix); 235 if (sPath.isEmpty()) 236 return false; 237 238 struct stat buf; 239 if (lstat(sPath.toUtf8().getStr(), &buf) != 0) 240 return false; 241 242 // Hardlink or symlink: osl::File::move() doesn't play with these nicely. 243 if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode)) 244 return false; 245 #elif defined _WIN32 246 if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE))) 247 return false; 248 #endif 249 250 return true; 251 #endif 252 } 253 254 } // anonymous namespace 255 256 class SfxMedium_Impl 257 { 258 public: 259 StreamMode m_nStorOpenMode; 260 ErrCode m_eError; 261 262 ::ucbhelper::Content aContent; 263 bool bUpdatePickList:1; 264 bool bIsTemp:1; 265 bool bDownloadDone:1; 266 bool bIsStorage:1; 267 bool bUseInteractionHandler:1; 268 bool bAllowDefaultIntHdl:1; 269 bool bDisposeStorage:1; 270 bool bStorageBasedOnInStream:1; 271 bool m_bSalvageMode:1; 272 bool m_bVersionsAlreadyLoaded:1; 273 bool m_bLocked:1; 274 bool m_bMSOLockFileCreated : 1; 275 bool m_bDisableUnlockWebDAV:1; 276 bool m_bGotDateTime:1; 277 bool m_bRemoveBackup:1; 278 bool m_bOriginallyReadOnly:1; 279 bool m_bOriginallyLoadedReadOnly:1; 280 bool m_bTriedStorage:1; 281 bool m_bRemote:1; 282 bool m_bInputStreamIsReadOnly:1; 283 bool m_bInCheckIn:1; 284 bool m_bDisableFileSync = false; 285 286 OUString m_aName; 287 OUString m_aLogicName; 288 OUString m_aLongName; 289 290 mutable std::unique_ptr<SfxItemSet> m_pSet; 291 mutable std::unique_ptr<INetURLObject> m_pURLObj; 292 293 std::shared_ptr<const SfxFilter> m_pFilter; 294 std::shared_ptr<const SfxFilter> m_pCustomFilter; 295 296 std::unique_ptr<SvStream> m_pInStream; 297 std::unique_ptr<SvStream> m_pOutStream; 298 299 OUString aOrigURL; 300 DateTime aExpireTime; 301 SfxFrameWeakRef wLoadTargetFrame; 302 SvKeyValueIteratorRef xAttributes; 303 304 svtools::AsynchronLink aDoneLink; 305 306 uno::Sequence < util::RevisionTag > aVersions; 307 308 std::unique_ptr<::utl::TempFile> pTempFile; 309 310 uno::Reference<embed::XStorage> xStorage; 311 uno::Reference<embed::XStorage> m_xZipStorage; 312 uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom; 313 uno::Reference<io::XInputStream> xInputStream; 314 uno::Reference<io::XStream> xStream; 315 uno::Reference<io::XStream> m_xLockingStream; 316 uno::Reference<task::XInteractionHandler> xInteraction; 317 318 ErrCode nLastStorageError; 319 320 OUString m_aBackupURL; 321 322 // the following member is changed and makes sense only during saving 323 // TODO/LATER: in future the signature state should be controlled by the medium not by the document 324 // in this case the member will hold this information 325 SignatureState m_nSignatureState; 326 327 bool m_bHasEmbeddedObjects = false; 328 329 util::DateTime m_aDateTime; 330 331 explicit SfxMedium_Impl(); 332 ~SfxMedium_Impl(); 333 SfxMedium_Impl(const SfxMedium_Impl&) = delete; 334 SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete; 335 336 OUString getFilterMimeType() const 337 { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); } 338 }; 339 340 341 SfxMedium_Impl::SfxMedium_Impl() : 342 m_nStorOpenMode(SFX_STREAM_READWRITE), 343 m_eError(ERRCODE_NONE), 344 bUpdatePickList(true), 345 bIsTemp( false ), 346 bDownloadDone( true ), 347 bIsStorage( false ), 348 bUseInteractionHandler( true ), 349 bAllowDefaultIntHdl( false ), 350 bDisposeStorage( false ), 351 bStorageBasedOnInStream( false ), 352 m_bSalvageMode( false ), 353 m_bVersionsAlreadyLoaded( false ), 354 m_bLocked( false ), 355 m_bMSOLockFileCreated( false ), 356 m_bDisableUnlockWebDAV( false ), 357 m_bGotDateTime( false ), 358 m_bRemoveBackup( false ), 359 m_bOriginallyReadOnly(false), 360 m_bOriginallyLoadedReadOnly(false), 361 m_bTriedStorage(false), 362 m_bRemote(false), 363 m_bInputStreamIsReadOnly(false), 364 m_bInCheckIn(false), 365 aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ), 366 nLastStorageError( ERRCODE_NONE ), 367 m_nSignatureState( SignatureState::NOSIGNATURES ) 368 { 369 aDoneLink.CreateMutex(); 370 } 371 372 373 SfxMedium_Impl::~SfxMedium_Impl() 374 { 375 aDoneLink.ClearPendingCall(); 376 377 pTempFile.reset(); 378 m_pSet.reset(); 379 m_pURLObj.reset(); 380 } 381 382 void SfxMedium::ResetError() 383 { 384 pImpl->m_eError = ERRCODE_NONE; 385 if( pImpl->m_pInStream ) 386 pImpl->m_pInStream->ResetError(); 387 if( pImpl->m_pOutStream ) 388 pImpl->m_pOutStream->ResetError(); 389 } 390 391 ErrCode const & SfxMedium::GetLastStorageCreationState() const 392 { 393 return pImpl->nLastStorageError; 394 } 395 396 void SfxMedium::SetError(ErrCode nError) 397 { 398 pImpl->m_eError = nError; 399 } 400 401 ErrCode SfxMedium::GetErrorCode() const 402 { 403 ErrCode lError = pImpl->m_eError; 404 if(!lError && pImpl->m_pInStream) 405 lError = pImpl->m_pInStream->GetErrorCode(); 406 if(!lError && pImpl->m_pOutStream) 407 lError = pImpl->m_pOutStream->GetErrorCode(); 408 return lError; 409 } 410 411 void SfxMedium::CheckFileDate( const util::DateTime& aInitDate ) 412 { 413 GetInitFileDate( true ); 414 if ( pImpl->m_aDateTime.Seconds == aInitDate.Seconds 415 && pImpl->m_aDateTime.Minutes == aInitDate.Minutes 416 && pImpl->m_aDateTime.Hours == aInitDate.Hours 417 && pImpl->m_aDateTime.Day == aInitDate.Day 418 && pImpl->m_aDateTime.Month == aInitDate.Month 419 && pImpl->m_aDateTime.Year == aInitDate.Year ) 420 return; 421 422 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler(); 423 424 if ( !xHandler.is() ) 425 return; 426 427 try 428 { 429 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny( 430 document::ChangedByOthersRequest() ) ); 431 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( 3 ); 432 aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() ); 433 aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() ); 434 xInteractionRequestImpl->setContinuations( aContinuations ); 435 436 xHandler->handle( xInteractionRequestImpl.get() ); 437 438 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection(); 439 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() ) 440 { 441 SetError(ERRCODE_ABORT); 442 } 443 } 444 catch ( const uno::Exception& ) 445 {} 446 } 447 448 bool SfxMedium::DocNeedsFileDateCheck() const 449 { 450 return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File || 451 GetURLObject().isAnyKnownWebDAVScheme() ) ); 452 } 453 454 util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue ) 455 { 456 if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() ) 457 { 458 try 459 { 460 // add a default css::ucb::XCommandEnvironment 461 // in order to have the WebDAV UCP provider manage http/https authentication correctly 462 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), 463 utl::UCBContentHelper::getDefaultCommandEnvironment(), 464 comphelper::getProcessComponentContext() ); 465 466 aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime; 467 pImpl->m_bGotDateTime = true; 468 } 469 catch ( const css::uno::Exception& ) 470 { 471 } 472 } 473 474 return pImpl->m_aDateTime; 475 } 476 477 478 Reference < XContent > SfxMedium::GetContent() const 479 { 480 if ( !pImpl->aContent.get().is() ) 481 { 482 Reference < css::ucb::XContent > xContent; 483 484 // tdf#95144 add a default css::ucb::XCommandEnvironment 485 // in order to have the WebDAV UCP provider manage https protocol certificates correctly 486 css:: uno::Reference< task::XInteractionHandler > xIH( 487 css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) ); 488 489 css::uno::Reference< css::ucb::XProgressHandler > xProgress; 490 ::ucbhelper::CommandEnvironment* pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress ); 491 492 Reference < css::ucb::XCommandEnvironment > xEnv( static_cast< css::ucb::XCommandEnvironment* >(pCommandEnv), css::uno::UNO_QUERY ); 493 494 const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false); 495 if ( pItem ) 496 pItem->GetValue() >>= xContent; 497 498 if ( xContent.is() ) 499 { 500 try 501 { 502 pImpl->aContent = ::ucbhelper::Content( xContent, xEnv, comphelper::getProcessComponentContext() ); 503 } 504 catch ( const Exception& ) 505 { 506 } 507 } 508 else 509 { 510 // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used."); 511 OUString aURL; 512 if ( !pImpl->m_aName.isEmpty() ) 513 osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL ); 514 else if ( !pImpl->m_aLogicName.isEmpty() ) 515 aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ); 516 if (!aURL.isEmpty() ) 517 (void)::ucbhelper::Content::create( aURL, xEnv, comphelper::getProcessComponentContext(), pImpl->aContent ); 518 } 519 } 520 521 return pImpl->aContent.get(); 522 } 523 524 OUString SfxMedium::GetBaseURL( bool bForSaving ) 525 { 526 OUString aBaseURL; 527 const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL); 528 if ( pBaseURLItem ) 529 aBaseURL = pBaseURLItem->GetValue(); 530 else if (!utl::ConfigManager::IsFuzzing() && GetContent().is()) 531 { 532 try 533 { 534 Any aAny = pImpl->aContent.getPropertyValue("BaseURI"); 535 aAny >>= aBaseURL; 536 } 537 catch ( const css::uno::Exception& ) 538 { 539 } 540 541 if ( aBaseURL.isEmpty() ) 542 aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ); 543 } 544 545 if ( bForSaving ) 546 { 547 SvtSaveOptions aOpt; 548 bool bIsRemote = IsRemote(); 549 if( (bIsRemote && !aOpt.IsSaveRelINet()) || (!pImpl->m_bRemote && !aOpt.IsSaveRelFSys()) ) 550 return OUString(); 551 } 552 553 return aBaseURL; 554 } 555 556 bool SfxMedium::IsSkipImages() const 557 { 558 const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS); 559 return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages"; 560 } 561 562 SvStream* SfxMedium::GetInStream() 563 { 564 if ( pImpl->m_pInStream ) 565 return pImpl->m_pInStream.get(); 566 567 if ( pImpl->pTempFile ) 568 { 569 pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) ); 570 571 pImpl->m_eError = pImpl->m_pInStream->GetError(); 572 573 if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE) 574 && ! pImpl->m_pInStream->IsWritable() ) 575 { 576 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED; 577 pImpl->m_pInStream.reset(); 578 } 579 else 580 return pImpl->m_pInStream.get(); 581 } 582 583 GetMedium_Impl(); 584 585 if ( GetError() ) 586 return nullptr; 587 588 return pImpl->m_pInStream.get(); 589 } 590 591 592 void SfxMedium::CloseInStream() 593 { 594 CloseInStream_Impl(); 595 } 596 597 void SfxMedium::CloseInStream_Impl(bool bInDestruction) 598 { 599 // if there is a storage based on the InStream, we have to 600 // close the storage, too, because otherwise the storage 601 // would use an invalid ( deleted ) stream. 602 if ( pImpl->m_pInStream && pImpl->xStorage.is() ) 603 { 604 if ( pImpl->bStorageBasedOnInStream ) 605 CloseStorage(); 606 } 607 608 if ( pImpl->m_pInStream && !GetContent().is() && !bInDestruction ) 609 { 610 CreateTempFile(); 611 return; 612 } 613 614 pImpl->m_pInStream.reset(); 615 if ( pImpl->m_pSet ) 616 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM ); 617 618 CloseZipStorage_Impl(); 619 pImpl->xInputStream.clear(); 620 621 if ( !pImpl->m_pOutStream ) 622 { 623 // output part of the stream is not used so the whole stream can be closed 624 // TODO/LATER: is it correct? 625 pImpl->xStream.clear(); 626 if ( pImpl->m_pSet ) 627 pImpl->m_pSet->ClearItem( SID_STREAM ); 628 } 629 } 630 631 632 SvStream* SfxMedium::GetOutStream() 633 { 634 if ( !pImpl->m_pOutStream ) 635 { 636 // Create a temp. file if there is none because we always 637 // need one. 638 CreateTempFile( false ); 639 640 if ( pImpl->pTempFile ) 641 { 642 // On windows we try to re-use XOutStream from xStream if that exists; 643 // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION 644 // TODO: this is a horrible hack that should probably be removed, 645 // somebody needs to investigate this more thoroughly... 646 if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is()) 647 { 648 assert(pImpl->xStream->getOutputStream().is()); // need that... 649 pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream( 650 pImpl->xStream, false); 651 } 652 else 653 { 654 // On Unix don't try to re-use XOutStream from xStream if that exists; 655 // it causes fdo#59022 (fails opening files via SMB on Linux) 656 pImpl->m_pOutStream.reset( new SvFileStream( 657 pImpl->m_aName, StreamMode::STD_READWRITE) ); 658 } 659 CloseStorage(); 660 } 661 } 662 663 return pImpl->m_pOutStream.get(); 664 } 665 666 667 void SfxMedium::CloseOutStream() 668 { 669 CloseOutStream_Impl(); 670 } 671 672 void SfxMedium::CloseOutStream_Impl() 673 { 674 if ( pImpl->m_pOutStream ) 675 { 676 // if there is a storage based on the OutStream, we have to 677 // close the storage, too, because otherwise the storage 678 // would use an invalid ( deleted ) stream. 679 //TODO/MBA: how to deal with this?! 680 //maybe we need a new flag when the storage was created from the outstream 681 if ( pImpl->xStorage.is() ) 682 { 683 CloseStorage(); 684 } 685 686 pImpl->m_pOutStream.reset(); 687 } 688 689 if ( !pImpl->m_pInStream ) 690 { 691 // input part of the stream is not used so the whole stream can be closed 692 // TODO/LATER: is it correct? 693 pImpl->xStream.clear(); 694 if ( pImpl->m_pSet ) 695 pImpl->m_pSet->ClearItem( SID_STREAM ); 696 } 697 } 698 699 700 const OUString& SfxMedium::GetPhysicalName() const 701 { 702 if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() ) 703 const_cast<SfxMedium*>(this)->CreateFileStream(); 704 705 // return the name then 706 return pImpl->m_aName; 707 } 708 709 710 void SfxMedium::CreateFileStream() 711 { 712 // force synchron 713 if( pImpl->m_pInStream ) 714 { 715 SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes(); 716 if( pBytes ) 717 pBytes->SetSynchronMode(); 718 } 719 720 GetInStream(); 721 if( pImpl->m_pInStream ) 722 { 723 CreateTempFile( false ); 724 pImpl->bIsTemp = true; 725 CloseInStream_Impl(); 726 } 727 } 728 729 730 bool SfxMedium::Commit() 731 { 732 if( pImpl->xStorage.is() ) 733 StorageCommit_Impl(); 734 else if( pImpl->m_pOutStream ) 735 pImpl->m_pOutStream->Flush(); 736 else if( pImpl->m_pInStream ) 737 pImpl->m_pInStream->Flush(); 738 739 if ( GetError() == ERRCODE_NONE ) 740 { 741 // does something only in case there is a temporary file ( means aName points to different location than aLogicName ) 742 Transfer_Impl(); 743 } 744 745 bool bResult = ( GetError() == ERRCODE_NONE ); 746 747 if ( bResult && DocNeedsFileDateCheck() ) 748 GetInitFileDate( true ); 749 750 // remove truncation mode from the flags 751 pImpl->m_nStorOpenMode &= ~StreamMode::TRUNC; 752 return bResult; 753 } 754 755 756 bool SfxMedium::IsStorage() 757 { 758 if ( pImpl->xStorage.is() ) 759 return true; 760 761 if ( pImpl->m_bTriedStorage ) 762 return pImpl->bIsStorage; 763 764 if ( pImpl->pTempFile ) 765 { 766 OUString aURL; 767 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL ) 768 != osl::FileBase::E_None ) 769 { 770 SAL_WARN( "sfx.doc", "Physical name '" << pImpl->m_aName << "' not convertible to file URL"); 771 } 772 pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL); 773 if ( !pImpl->bIsStorage ) 774 pImpl->m_bTriedStorage = true; 775 } 776 else if ( GetInStream() ) 777 { 778 pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() ); 779 if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage ) 780 pImpl->m_bTriedStorage = true; 781 } 782 783 return pImpl->bIsStorage; 784 } 785 786 787 bool SfxMedium::IsPreview_Impl() const 788 { 789 bool bPreview = false; 790 const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false); 791 if ( pPreview ) 792 bPreview = pPreview->GetValue(); 793 else 794 { 795 const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false); 796 if ( pFlags ) 797 { 798 OUString aFileFlags = pFlags->GetValue(); 799 aFileFlags = aFileFlags.toAsciiUpperCase(); 800 if ( -1 != aFileFlags.indexOf( 'B' ) ) 801 bPreview = true; 802 } 803 } 804 805 return bPreview; 806 } 807 808 809 void SfxMedium::StorageBackup_Impl() 810 { 811 ::ucbhelper::Content aOriginalContent; 812 Reference< css::ucb::XCommandEnvironment > xDummyEnv; 813 814 bool bBasedOnOriginalFile = ( !pImpl->pTempFile && !( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode ) 815 && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty() 816 && GetURLObject().GetProtocol() == INetProtocol::File 817 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ); 818 819 if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty() 820 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) ) 821 { 822 DoInternalBackup_Impl( aOriginalContent ); 823 if( pImpl->m_aBackupURL.isEmpty() ) 824 SetError(ERRCODE_SFX_CANTCREATEBACKUP); 825 } 826 } 827 828 829 OUString const & SfxMedium::GetBackup_Impl() 830 { 831 if ( pImpl->m_aBackupURL.isEmpty() ) 832 StorageBackup_Impl(); 833 834 return pImpl->m_aBackupURL; 835 } 836 837 838 uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage() 839 { 840 if ( GetError() ) 841 return uno::Reference< embed::XStorage >(); 842 843 // if the medium was constructed with a Storage: use this one, not a temp. storage 844 // if a temporary storage already exists: use it 845 if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) ) 846 return pImpl->xStorage; 847 848 // if necessary close stream that was used for reading 849 if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() ) 850 CloseInStream(); 851 852 DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" ); 853 854 // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location; 855 // in future it should be stored directly and then copied to the temporary location, since in this case no 856 // file attributes have to be preserved and system copying mechanics could be used instead of streaming. 857 CreateTempFileNoCopy(); 858 859 return GetStorage(); 860 } 861 862 863 void SfxMedium::SetEncryptionDataToStorage_Impl() 864 { 865 // in case media-descriptor contains password it should be used on opening 866 if ( !pImpl->xStorage.is() || !pImpl->m_pSet ) 867 return; 868 869 uno::Sequence< beans::NamedValue > aEncryptionData; 870 if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) ) 871 return; 872 873 // replace the password with encryption data 874 pImpl->m_pSet->ClearItem( SID_PASSWORD ); 875 pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) ); 876 877 try 878 { 879 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData ); 880 } 881 catch( const uno::Exception& ) 882 { 883 SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" ); 884 // TODO/LATER: set the error code in case of problem 885 // SetError(ERRCODE_IO_GENERAL); 886 } 887 } 888 889 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT 890 891 // FIXME: Hmm actually lock files should be used for sftp: documents 892 // even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock 893 // files for *local* documents is unnecessary in that case. But 894 // actually, the checks for sftp: here are just wishful thinking; I 895 // don't this there is any support for actually editing documents 896 // behind sftp: URLs anyway. 897 898 // Sure, there could perhaps be a 3rd-party extension that brings UCB 899 // the potential to handle files behind sftp:. But there could also be 900 // an extension that handles some arbitrary foobar: scheme *and* it 901 // could be that lock files would be the correct thing to use for 902 // foobar: documents, too. But the hardcoded test below won't know 903 // that. Clearly the knowledge whether lock files should be used or 904 // not for some URL scheme belongs in UCB, not here. 905 906 namespace 907 { 908 909 OUString tryMSOwnerFiles(const OUString& sDocURL) 910 { 911 svt::MSODocumentLockFile aMSOLockFile(sDocURL); 912 LockFileEntry aData; 913 try 914 { 915 aData = aMSOLockFile.GetLockData(); 916 } 917 catch( const uno::Exception& ) 918 { 919 return OUString(); 920 } 921 922 OUString sUserData = aData[LockFileComponent::OOOUSERNAME]; 923 924 if (!sUserData.isEmpty()) 925 sUserData += " (MS Office)"; // Mention the used office suite 926 927 return sUserData; 928 } 929 930 OUString tryForeignLockfiles(const OUString& sDocURL) 931 { 932 OUString sUserData = tryMSOwnerFiles(sDocURL); 933 // here we can test for empty result, and add other known applications' lockfile testing 934 return sUserData.trim(); 935 } 936 } 937 938 SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry& aData, 939 bool bIsLoading, bool bOwnLock, 940 bool bHandleSysLocked) 941 { 942 ShowLockResult nResult = ShowLockResult::NoLock; 943 944 // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled 945 if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked ) 946 bOwnLock=true; 947 948 // show the interaction regarding the document opening 949 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler(); 950 951 if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) ) 952 { 953 OUString aDocumentURL 954 = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset); 955 OUString aInfo; 956 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl; 957 958 sal_Int32 nContinuations = 3; 959 960 if ( bOwnLock ) 961 { 962 aInfo = aData[LockFileComponent::EDITTIME]; 963 964 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny( 965 document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) ); 966 } 967 else 968 { 969 // Use a fourth continuation in case there's no filesystem lock: 970 // "Ignore lock file and open/replace the document" 971 if (!bHandleSysLocked) 972 nContinuations = 4; 973 974 if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() ) 975 aInfo = aData[LockFileComponent::OOOUSERNAME]; 976 else 977 aInfo = aData[LockFileComponent::SYSUSERNAME]; 978 979 if (aInfo.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme()) 980 // Try to get name of user who has locked the file using other applications 981 aInfo = tryForeignLockfiles( 982 GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE)); 983 984 if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() ) 985 aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )"; 986 987 if (!bIsLoading) // so, !bHandleSysLocked 988 { 989 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( 990 document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo))); 991 // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned. 992 } 993 else /*logically therefore bIsLoading is set */ 994 { 995 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny( 996 document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) ); 997 } 998 } 999 1000 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations); 1001 aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() ); 1002 aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() ); 1003 aContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() ); 1004 if (nContinuations > 3) 1005 { 1006 // We use InteractionRetry to reflect that user wants to 1007 // ignore the (stale?) alien lock file and open/overwrite the document 1008 aContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get()); 1009 } 1010 xInteractionRequestImpl->setContinuations( aContinuations ); 1011 1012 xHandler->handle( xInteractionRequestImpl.get() ); 1013 1014 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection(); 1015 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() ) 1016 { 1017 SetError(ERRCODE_ABORT); 1018 } 1019 else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() ) 1020 { 1021 // own lock on loading, user has selected to ignore the lock 1022 // own lock on saving, user has selected to ignore the lock 1023 // alien lock on loading, user has selected to edit a copy of document 1024 // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location 1025 if ( !bOwnLock ) // bIsLoading implied from outermost condition 1026 { 1027 // means that a copy of the document should be opened 1028 GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) ); 1029 } 1030 else 1031 nResult = ShowLockResult::Succeeded; 1032 } 1033 else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is()) 1034 { 1035 // User decided to ignore the alien (stale?) lock file without filesystem lock 1036 nResult = ShowLockResult::Succeeded; 1037 } 1038 else // if ( XSelected == aContinuations[1] ) 1039 { 1040 // own lock on loading, user has selected to open readonly 1041 // own lock on saving, user has selected to open readonly 1042 // alien lock on loading, user has selected to retry saving 1043 // TODO/LATER: alien lock on saving, user has selected to retry saving 1044 1045 if ( bIsLoading ) 1046 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 1047 else 1048 nResult = ShowLockResult::Try; 1049 } 1050 } 1051 else 1052 { 1053 if ( bIsLoading ) 1054 { 1055 // if no interaction handler is provided the default answer is open readonly 1056 // that usually happens in case the document is loaded per API 1057 // so the document must be opened readonly for backward compatibility 1058 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 1059 } 1060 else 1061 SetError(ERRCODE_IO_ACCESSDENIED); 1062 1063 } 1064 1065 return nResult; 1066 } 1067 1068 1069 bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg) 1070 { 1071 // system file locking is not active, ask user whether he wants to open the document without any locking 1072 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler(); 1073 1074 if (xHandler.is()) 1075 { 1076 ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl; 1077 1078 switch (nWhichDlg) 1079 { 1080 case MessageDlg::LockFileIgnore: 1081 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() )); 1082 break; 1083 case MessageDlg::LockFileCorrupt: 1084 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() )); 1085 break; 1086 } 1087 1088 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(2); 1089 aContinuations[0] = new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get()); 1090 aContinuations[1] = new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get()); 1091 xIgnoreRequestImpl->setContinuations(aContinuations); 1092 1093 xHandler->handle(xIgnoreRequestImpl.get()); 1094 1095 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection(); 1096 bool bReadOnly = uno::Reference< task::XInteractionApprove >(xSelected.get(), uno::UNO_QUERY).is(); 1097 1098 if (bReadOnly) 1099 { 1100 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true)); 1101 } 1102 else 1103 { 1104 SetError(ERRCODE_ABORT); 1105 } 1106 1107 return bReadOnly; 1108 } 1109 1110 return false; 1111 } 1112 1113 namespace 1114 { 1115 bool isSuitableProtocolForLocking(const OUString & rLogicName) 1116 { 1117 INetURLObject aUrl( rLogicName ); 1118 INetProtocol eProt = aUrl.GetProtocol(); 1119 #if !HAVE_FEATURE_MACOSX_SANDBOX 1120 if (eProt == INetProtocol::File) { 1121 return true; 1122 } 1123 #endif 1124 return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp; 1125 } 1126 } 1127 1128 #endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT 1129 1130 // sets SID_DOC_READONLY if the document cannot be opened for editing 1131 // if user cancel the loading the ERROR_ABORT is set 1132 SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bNoUI, 1133 bool bTryIgnoreLockFile, 1134 LockFileEntry* pLockData) 1135 { 1136 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT 1137 (void) bLoading; 1138 (void) bNoUI; 1139 (void) bTryIgnoreLockFile; 1140 (void) pLockData; 1141 return LockFileResult::Succeeded; 1142 #else 1143 LockFileResult eResult = LockFileResult::Failed; 1144 1145 // check if path scheme is http:// or https:// 1146 // may be this is better if used always, in Android and iOS as well? 1147 // if this code should be always there, remember to move the relevant code in UnlockFile method as well ! 1148 1149 if ( GetURLObject().isAnyKnownWebDAVScheme() ) 1150 { 1151 // do nothing if WebDAV locking is disabled 1152 if (!IsWebDAVLockingUsed()) 1153 return LockFileResult::Succeeded; 1154 1155 try 1156 { 1157 bool bResult = pImpl->m_bLocked; 1158 bool bIsTemplate = false; 1159 // so, this is webdav stuff... 1160 if ( !bResult ) 1161 { 1162 // no read-write access is necessary on loading if the document is explicitly opened as copy 1163 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false); 1164 bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() ); 1165 } 1166 1167 if ( !bIsTemplate && !bResult && !IsReadOnly() ) 1168 { 1169 ShowLockResult bUIStatus = ShowLockResult::NoLock; 1170 do 1171 { 1172 if( !bResult ) 1173 { 1174 uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true ); 1175 Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( 1176 xCHandler, Reference< css::ucb::XProgressHandler >() ); 1177 1178 ucbhelper::Content aContentToLock( 1179 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), 1180 xComEnv, comphelper::getProcessComponentContext() ); 1181 1182 try 1183 { 1184 aContentToLock.lock(); 1185 bResult = true; 1186 } 1187 catch ( ucb::InteractiveLockingLockedException& ) 1188 { 1189 // received when the resource is already locked 1190 if (!bNoUI || pLockData) 1191 { 1192 // get the lock owner, using a special ucb.webdav property 1193 // the owner property retrieved here is what the other principal send the server 1194 // when activating the lock. 1195 // See http://tools.ietf.org/html/rfc4918#section-14.17 for details 1196 LockFileEntry aLockData; 1197 aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user"; 1198 // This solution works right when the LO user name and the WebDAV user 1199 // name are the same. 1200 // A better thing to do would be to obtain the 'real' WebDAV user name, 1201 // but that's not possible from a WebDAV UCP provider client. 1202 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry(); 1203 // use the current LO user name as the system name 1204 aLockData[LockFileComponent::SYSUSERNAME] 1205 = aOwnData[LockFileComponent::SYSUSERNAME]; 1206 1207 uno::Sequence<css::ucb::Lock> aLocks; 1208 // getting the property, send a PROPFIND to the server over the net 1209 if (aContentToLock.getPropertyValue("DAV:lockdiscovery") >>= aLocks) 1210 { 1211 // got at least a lock, show the owner of the first lock returned 1212 css::ucb::Lock aLock = aLocks[0]; 1213 OUString aOwner; 1214 if (aLock.Owner >>= aOwner) 1215 { 1216 // we need to display the WebDAV user name owning the lock, not the local one 1217 aLockData[LockFileComponent::OOOUSERNAME] = aOwner; 1218 } 1219 } 1220 1221 if (!bNoUI) 1222 { 1223 bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false, 1224 true); 1225 } 1226 1227 if (pLockData) 1228 { 1229 std::copy(aLockData.begin(), aLockData.end(), pLockData->begin()); 1230 } 1231 } 1232 } 1233 catch( ucb::InteractiveNetworkWriteException& ) 1234 { 1235 // This catch it's not really needed, here just for the sake of documentation on the behaviour. 1236 // This is the most likely reason: 1237 // - the remote site is a WebDAV with special configuration: read/only for read operations 1238 // and read/write for write operations, the user is not allowed to lock/write and 1239 // she cancelled the credentials request. 1240 // this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic 1241 // management that takes part in cancelCommandExecution() 1242 // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly 1243 // since it mostly happens on read/only part of webdav, this can be the most correct 1244 // exception available 1245 } 1246 catch( uno::Exception& ) 1247 {} 1248 } 1249 } while( !bResult && bUIStatus == ShowLockResult::Try ); 1250 } 1251 1252 pImpl->m_bLocked = bResult; 1253 1254 if ( !bResult && GetError() == ERRCODE_NONE ) 1255 { 1256 // the error should be set in case it is storing process 1257 // or the document has been opened for editing explicitly 1258 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false); 1259 1260 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) ) 1261 SetError(ERRCODE_IO_ACCESSDENIED); 1262 else 1263 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 1264 } 1265 1266 // when the file is locked, get the current file date 1267 if ( bResult && DocNeedsFileDateCheck() ) 1268 GetInitFileDate( true ); 1269 1270 if ( bResult ) 1271 eResult = LockFileResult::Succeeded; 1272 } 1273 catch ( const uno::Exception& ) 1274 { 1275 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" ); 1276 } 1277 return eResult; 1278 } 1279 1280 if (!IsLockingUsed()) 1281 return LockFileResult::Succeeded; 1282 if (GetURLObject().HasError()) 1283 return eResult; 1284 1285 try 1286 { 1287 if ( pImpl->m_bLocked && bLoading 1288 && GetURLObject().GetProtocol() == INetProtocol::File ) 1289 { 1290 // if the document is already locked the system locking might be temporarily off after storing 1291 // check whether the system file locking should be taken again 1292 GetLockingStream_Impl(); 1293 } 1294 1295 bool bResult = pImpl->m_bLocked; 1296 1297 if ( !bResult ) 1298 { 1299 // no read-write access is necessary on loading if the document is explicitly opened as copy 1300 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false); 1301 bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() ); 1302 } 1303 1304 if ( !bResult && !IsReadOnly() ) 1305 { 1306 bool bContentReadonly = false; 1307 if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File ) 1308 { 1309 // let the original document be opened to check the possibility to open it for editing 1310 // and to let the writable stream stay open to hold the lock on the document 1311 GetLockingStream_Impl(); 1312 } 1313 1314 // "IsReadOnly" property does not allow to detect whether the file is readonly always 1315 // so we try always to open the file for editing 1316 // the file is readonly only in case the read-write stream can not be opened 1317 if ( bLoading && !pImpl->m_xLockingStream.is() ) 1318 { 1319 try 1320 { 1321 // MediaDescriptor does this check also, the duplication should be avoided in future 1322 Reference< css::ucb::XCommandEnvironment > xDummyEnv; 1323 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() ); 1324 aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly; 1325 } 1326 catch( const uno::Exception& ) {} 1327 } 1328 1329 // do further checks only if the file not readonly in fs 1330 if ( !bContentReadonly ) 1331 { 1332 // the special file locking should be used only for suitable URLs 1333 if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) ) 1334 { 1335 1336 // in case of storing the document should request the output before locking 1337 if ( bLoading ) 1338 { 1339 // let the stream be opened to check the system file locking 1340 GetMedium_Impl(); 1341 if (GetError() != ERRCODE_NONE) { 1342 return eResult; 1343 } 1344 } 1345 1346 ShowLockResult bUIStatus = ShowLockResult::NoLock; 1347 1348 // check whether system file locking has been used, the default value is false 1349 bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed(); 1350 1351 // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem 1352 // if system lock is used the writeable stream should be available 1353 bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream ); 1354 1355 // The file is attempted to get locked for the duration of lockfile creation on save 1356 std::unique_ptr<osl::File> pFileLock; 1357 if (!bLoading && bUseSystemLock && pImpl->pTempFile) 1358 { 1359 INetURLObject aDest(GetURLObject()); 1360 OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE)); 1361 1362 if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment()) 1363 { 1364 pFileLock = std::make_unique<osl::File>(aDestURL); 1365 auto rc = pFileLock->open(osl_File_OpenFlag_Write); 1366 if (rc == osl::FileBase::E_ACCES) 1367 bHandleSysLocked = true; 1368 } 1369 } 1370 1371 do 1372 { 1373 try 1374 { 1375 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName ); 1376 1377 std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile; 1378 const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); 1379 if (rOpt.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName)) 1380 { 1381 pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName)); 1382 pImpl->m_bMSOLockFileCreated = true; 1383 } 1384 1385 bool bIoErr = false; 1386 1387 if (!bHandleSysLocked) 1388 { 1389 try 1390 { 1391 bResult = aLockFile.CreateOwnLockFile(); 1392 if(pMSOLockFile) 1393 bResult &= pMSOLockFile->CreateOwnLockFile(); 1394 } 1395 catch (const uno::Exception&) 1396 { 1397 if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL( 1398 INetURLObject::DecodeMechanism::NONE))) 1399 { 1400 // This is a path that redirects to a WebDAV resource; 1401 // so failure creating lockfile is not an error here. 1402 bResult = true; 1403 } 1404 else if (bLoading && !bNoUI) 1405 { 1406 bIoErr = true; 1407 ShowLockFileProblemDialog(MessageDlg::LockFileIgnore); 1408 bResult = true; // always delete the defect lock-file 1409 } 1410 } 1411 1412 // in case OOo locking is turned off the lock file is still written if possible 1413 // but it is ignored while deciding whether the document should be opened for editing or not 1414 if (!bResult && !IsOOoLockFileUsed() && !bIoErr) 1415 { 1416 bResult = true; 1417 // take the ownership over the lock file 1418 aLockFile.OverwriteOwnLockFile(); 1419 1420 if(pMSOLockFile) 1421 pMSOLockFile->OverwriteOwnLockFile(); 1422 } 1423 } 1424 1425 if ( !bResult ) 1426 { 1427 LockFileEntry aData; 1428 try 1429 { 1430 aData = aLockFile.GetLockData(); 1431 } 1432 catch (const io::WrongFormatException&) 1433 { 1434 // we get empty or corrupt data 1435 // info to the user 1436 if (!bIoErr && bLoading && !bNoUI ) 1437 bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt); 1438 1439 // not show the Lock Document Dialog 1440 bIoErr = true; 1441 } 1442 catch( const uno::Exception& ) 1443 { 1444 // show the Lock Document Dialog, when locked from other app 1445 bIoErr = !bHandleSysLocked; 1446 } 1447 1448 bool bOwnLock = false; 1449 1450 if (!bHandleSysLocked) 1451 { 1452 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry(); 1453 bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME]; 1454 1455 if (bOwnLock 1456 && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST] 1457 && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL]) 1458 { 1459 // this is own lock from the same installation, it could remain because of crash 1460 bResult = true; 1461 } 1462 } 1463 1464 if ( !bResult && !bIoErr) 1465 { 1466 if (!bNoUI) 1467 bUIStatus = ShowLockedDocumentDialog( 1468 aData, bLoading, bOwnLock, bHandleSysLocked); 1469 else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked) 1470 bUIStatus = ShowLockResult::Succeeded; 1471 1472 if ( bUIStatus == ShowLockResult::Succeeded ) 1473 { 1474 // take the ownership over the lock file 1475 bResult = aLockFile.OverwriteOwnLockFile(); 1476 1477 if(pMSOLockFile) 1478 pMSOLockFile->OverwriteOwnLockFile(); 1479 } 1480 else if (bLoading && !bHandleSysLocked) 1481 eResult = LockFileResult::FailedLockFile; 1482 1483 if (!bResult && pLockData) 1484 { 1485 std::copy(aData.begin(), aData.end(), pLockData->begin()); 1486 } 1487 } 1488 } 1489 } 1490 catch( const uno::Exception& ) 1491 { 1492 } 1493 } while( !bResult && bUIStatus == ShowLockResult::Try ); 1494 1495 pImpl->m_bLocked = bResult; 1496 } 1497 else 1498 { 1499 // this is no file URL, check whether the file is readonly 1500 bResult = !bContentReadonly; 1501 } 1502 } 1503 } 1504 1505 if ( !bResult && GetError() == ERRCODE_NONE ) 1506 { 1507 // the error should be set in case it is storing process 1508 // or the document has been opened for editing explicitly 1509 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false); 1510 1511 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) ) 1512 SetError(ERRCODE_IO_ACCESSDENIED); 1513 else 1514 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 1515 } 1516 1517 // when the file is locked, get the current file date 1518 if ( bResult && DocNeedsFileDateCheck() ) 1519 GetInitFileDate( true ); 1520 1521 if ( bResult ) 1522 eResult = LockFileResult::Succeeded; 1523 } 1524 catch( const uno::Exception& ) 1525 { 1526 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" ); 1527 } 1528 1529 return eResult; 1530 #endif 1531 } 1532 1533 1534 uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile ) 1535 { 1536 if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage ) 1537 return pImpl->xStorage; 1538 1539 uno::Sequence< uno::Any > aArgs( 2 ); 1540 1541 // the medium should be retrieved before temporary file creation 1542 // to let the MediaDescriptor be filled with the streams 1543 GetMedium_Impl(); 1544 1545 if ( bCreateTempFile ) 1546 CreateTempFile( false ); 1547 1548 GetMedium_Impl(); 1549 1550 if ( GetError() ) 1551 return pImpl->xStorage; 1552 1553 const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false); 1554 if ( pRepairItem && pRepairItem->GetValue() ) 1555 { 1556 // the storage should be created for repairing mode 1557 CreateTempFile( false ); 1558 GetMedium_Impl(); 1559 1560 Reference< css::ucb::XProgressHandler > xProgressHandler; 1561 Reference< css::task::XStatusIndicator > xStatusIndicator; 1562 1563 const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false); 1564 if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) ) 1565 xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) ); 1566 1567 uno::Sequence< beans::PropertyValue > aAddProps( 2 ); 1568 aAddProps[0].Name = "RepairPackage"; 1569 aAddProps[0].Value <<= true; 1570 aAddProps[1].Name = "StatusIndicator"; 1571 aAddProps[1].Value <<= xProgressHandler; 1572 1573 // the first arguments will be filled later 1574 aArgs.realloc( 3 ); 1575 aArgs[2] <<= aAddProps; 1576 } 1577 1578 if ( pImpl->xStream.is() ) 1579 { 1580 // since the storage is based on temporary stream we open it always read-write 1581 aArgs[0] <<= pImpl->xStream; 1582 aArgs[1] <<= embed::ElementModes::READWRITE; 1583 pImpl->bStorageBasedOnInStream = true; 1584 if (pImpl->m_bDisableFileSync) 1585 { 1586 // Forward NoFileSync to the storage factory. 1587 aArgs.realloc(3); 1588 uno::Sequence<beans::PropertyValue> aProperties( 1589 comphelper::InitPropertySequence({ { "NoFileSync", uno::makeAny(true) } })); 1590 aArgs[2] <<= aProperties; 1591 } 1592 } 1593 else if ( pImpl->xInputStream.is() ) 1594 { 1595 // since the storage is based on temporary stream we open it always read-write 1596 aArgs[0] <<= pImpl->xInputStream; 1597 aArgs[1] <<= embed::ElementModes::READ; 1598 pImpl->bStorageBasedOnInStream = true; 1599 } 1600 else 1601 { 1602 CloseStreams_Impl(); 1603 aArgs[0] <<= pImpl->m_aName; 1604 aArgs[1] <<= embed::ElementModes::READ; 1605 pImpl->bStorageBasedOnInStream = false; 1606 } 1607 1608 try 1609 { 1610 pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ), 1611 uno::UNO_QUERY ); 1612 } 1613 catch( const uno::Exception& ) 1614 { 1615 // impossibility to create the storage is no error 1616 } 1617 1618 if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE ) 1619 { 1620 pImpl->xStorage = nullptr; 1621 if ( pImpl->m_pInStream ) 1622 pImpl->m_pInStream->Seek(0); 1623 return uno::Reference< embed::XStorage >(); 1624 } 1625 1626 pImpl->m_bTriedStorage = true; 1627 1628 // TODO/LATER: Get versionlist on demand 1629 if ( pImpl->xStorage.is() ) 1630 { 1631 SetEncryptionDataToStorage_Impl(); 1632 GetVersionList(); 1633 } 1634 1635 const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false); 1636 1637 bool bResetStorage = false; 1638 if ( pVersion && pVersion->GetValue() ) 1639 { 1640 // Read all available versions 1641 if ( pImpl->aVersions.hasElements() ) 1642 { 1643 // Search for the version fits the comment 1644 // The versions are numbered starting with 1, versions with 1645 // negative versions numbers are counted backwards from the 1646 // current version 1647 short nVersion = pVersion->GetValue(); 1648 if ( nVersion<0 ) 1649 nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion; 1650 else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block 1651 nVersion--; 1652 1653 util::RevisionTag& rTag = pImpl->aVersions[nVersion]; 1654 { 1655 // Open SubStorage for all versions 1656 uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions", 1657 embed::ElementModes::READ ); 1658 1659 DBG_ASSERT( xSub.is(), "Version list, but no Versions!" ); 1660 1661 // There the version is stored as packed Stream 1662 uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ ); 1663 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr )); 1664 if ( pStream && pStream->GetError() == ERRCODE_NONE ) 1665 { 1666 // Unpack Stream in TempDir 1667 ::utl::TempFile aTempFile; 1668 const OUString& aTmpName = aTempFile.GetURL(); 1669 SvFileStream aTmpStream( aTmpName, SFX_STREAM_READWRITE ); 1670 1671 pStream->ReadStream( aTmpStream ); 1672 pStream.reset(); 1673 aTmpStream.Close(); 1674 1675 // Open data as Storage 1676 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY; 1677 pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ ); 1678 pImpl->bStorageBasedOnInStream = false; 1679 OUString aTemp; 1680 osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp ); 1681 SetPhysicalName_Impl( aTemp ); 1682 1683 pImpl->bIsTemp = true; 1684 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 1685 // TODO/MBA 1686 pImpl->aVersions.realloc(0); 1687 } 1688 else 1689 bResetStorage = true; 1690 } 1691 } 1692 else 1693 bResetStorage = true; 1694 } 1695 1696 if ( bResetStorage ) 1697 { 1698 pImpl->xStorage.clear(); 1699 if ( pImpl->m_pInStream ) 1700 pImpl->m_pInStream->Seek( 0 ); 1701 } 1702 1703 pImpl->bIsStorage = pImpl->xStorage.is(); 1704 return pImpl->xStorage; 1705 } 1706 1707 1708 uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly ) 1709 { 1710 if ( !GetError() && !pImpl->m_xZipStorage.is() ) 1711 { 1712 GetMedium_Impl(); 1713 1714 try 1715 { 1716 // we can not sign document if there is no stream 1717 // should it be possible at all? 1718 if ( !bReadOnly && pImpl->xStream.is() ) 1719 { 1720 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream ); 1721 } 1722 else if ( pImpl->xInputStream.is() ) 1723 { 1724 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream ); 1725 } 1726 } 1727 catch( const uno::Exception& ) 1728 { 1729 SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" ); 1730 } 1731 1732 if ( GetError() ) // do not remove warnings 1733 ResetError(); 1734 } 1735 1736 return pImpl->m_xZipStorage; 1737 } 1738 1739 1740 void SfxMedium::CloseZipStorage_Impl() 1741 { 1742 if ( pImpl->m_xZipStorage.is() ) 1743 { 1744 try { 1745 pImpl->m_xZipStorage->dispose(); 1746 } catch( const uno::Exception& ) 1747 {} 1748 1749 pImpl->m_xZipStorage.clear(); 1750 } 1751 } 1752 1753 void SfxMedium::CloseStorage() 1754 { 1755 if ( pImpl->xStorage.is() ) 1756 { 1757 uno::Reference < lang::XComponent > xComp = pImpl->xStorage; 1758 // in the salvage mode the medium does not own the storage 1759 if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode ) 1760 { 1761 try { 1762 xComp->dispose(); 1763 } catch( const uno::Exception& ) 1764 { 1765 SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" ); 1766 } 1767 } 1768 1769 pImpl->xStorage.clear(); 1770 pImpl->bStorageBasedOnInStream = false; 1771 } 1772 1773 pImpl->m_bTriedStorage = false; 1774 pImpl->bIsStorage = false; 1775 } 1776 1777 void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage ) 1778 { 1779 pImpl->bDisposeStorage = bDisposeStorage; 1780 } 1781 1782 bool SfxMedium::WillDisposeStorageOnClose_Impl() 1783 { 1784 return pImpl->bDisposeStorage; 1785 } 1786 1787 StreamMode SfxMedium::GetOpenMode() const 1788 { 1789 return pImpl->m_nStorOpenMode; 1790 } 1791 1792 void SfxMedium::SetOpenMode( StreamMode nStorOpen, 1793 bool bDontClose ) 1794 { 1795 if ( pImpl->m_nStorOpenMode != nStorOpen ) 1796 { 1797 pImpl->m_nStorOpenMode = nStorOpen; 1798 1799 if( !bDontClose ) 1800 { 1801 if ( pImpl->xStorage.is() ) 1802 CloseStorage(); 1803 1804 CloseStreams_Impl(); 1805 } 1806 } 1807 } 1808 1809 1810 bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent, 1811 const Reference< css::ucb::XCommandEnvironment >& xComEnv ) 1812 { 1813 try 1814 { 1815 ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() ); 1816 1817 Reference< XInputStream > aOrigInput = aTransactCont.openStream(); 1818 aOriginalContent.writeStream( aOrigInput, true ); 1819 return true; 1820 } 1821 catch( const Exception& ) 1822 { 1823 // in case of failure here the backup file should not be removed 1824 // TODO/LATER: a message should be used to let user know about the backup 1825 pImpl->m_bRemoveBackup = false; 1826 // TODO/LATER: needs a specific error code 1827 pImpl->m_eError = ERRCODE_IO_GENERAL; 1828 } 1829 1830 return false; 1831 } 1832 1833 1834 bool SfxMedium::StorageCommit_Impl() 1835 { 1836 bool bResult = false; 1837 Reference< css::ucb::XCommandEnvironment > xDummyEnv; 1838 ::ucbhelper::Content aOriginalContent; 1839 1840 if ( pImpl->xStorage.is() ) 1841 { 1842 if ( !GetError() ) 1843 { 1844 uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY ); 1845 if ( xTrans.is() ) 1846 { 1847 try 1848 { 1849 xTrans->commit(); 1850 CloseZipStorage_Impl(); 1851 bResult = true; 1852 } 1853 catch ( const embed::UseBackupException& aBackupExc ) 1854 { 1855 // since the temporary file is created always now, the scenario is close to be impossible 1856 if ( !pImpl->pTempFile ) 1857 { 1858 OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" ); 1859 if ( !pImpl->m_aBackupURL.isEmpty() 1860 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), 1861 xDummyEnv, comphelper::getProcessComponentContext(), 1862 aOriginalContent ) ) 1863 { 1864 // use backup to restore the file 1865 // the storage has already disconnected from original location 1866 CloseAndReleaseStreams_Impl(); 1867 if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) ) 1868 { 1869 // connect the medium to the temporary file of the storage 1870 pImpl->aContent = ::ucbhelper::Content(); 1871 pImpl->m_aName = aBackupExc.TemporaryFileURL; 1872 OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" ); 1873 } 1874 } 1875 } 1876 1877 if (!GetError()) 1878 SetError(ERRCODE_IO_GENERAL); 1879 } 1880 catch ( const uno::Exception& ) 1881 { 1882 //TODO/LATER: improve error handling 1883 SetError(ERRCODE_IO_GENERAL); 1884 } 1885 } 1886 } 1887 } 1888 1889 return bResult; 1890 } 1891 1892 1893 void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource, 1894 const INetURLObject& aDest, 1895 const Reference< css::ucb::XCommandEnvironment >& xComEnv ) 1896 { 1897 bool bResult = false; 1898 Reference< css::ucb::XCommandEnvironment > xDummyEnv; 1899 ::ucbhelper::Content aOriginalContent; 1900 1901 try 1902 { 1903 aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() ); 1904 } 1905 catch ( const css::ucb::CommandAbortedException& ) 1906 { 1907 pImpl->m_eError = ERRCODE_ABORT; 1908 } 1909 catch ( const css::ucb::CommandFailedException& ) 1910 { 1911 pImpl->m_eError = ERRCODE_ABORT; 1912 } 1913 catch (const css::ucb::ContentCreationException& ex) 1914 { 1915 pImpl->m_eError = ERRCODE_IO_GENERAL; 1916 if ( 1917 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) || 1918 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED) 1919 ) 1920 { 1921 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH; 1922 } 1923 } 1924 catch (const css::uno::Exception&) 1925 { 1926 pImpl->m_eError = ERRCODE_IO_GENERAL; 1927 } 1928 1929 if( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) 1930 return; 1931 1932 if ( pImpl->xStorage.is() ) 1933 CloseStorage(); 1934 1935 CloseStreams_Impl(); 1936 1937 ::ucbhelper::Content aTempCont; 1938 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) ) 1939 { 1940 bool bTransactStarted = false; 1941 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false); 1942 bool bOverWrite = !pOverWrite || pOverWrite->GetValue(); 1943 1944 try 1945 { 1946 OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE); 1947 OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE); 1948 1949 sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL); 1950 if (IsFileMovable(aDest) 1951 && osl::File::replace(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None) 1952 { 1953 if (nAttributes) 1954 // Adjust attributes, source might be created with 1955 // the osl_File_OpenFlag_Private flag. 1956 osl::File::setAttributes(aDestMainURL, nAttributes); 1957 bResult = true; 1958 } 1959 else 1960 { 1961 if (bOverWrite && ::utl::UCBContentHelper::IsDocument(aDestMainURL)) 1962 { 1963 if( pImpl->m_aBackupURL.isEmpty() ) 1964 DoInternalBackup_Impl( aOriginalContent ); 1965 1966 if( !pImpl->m_aBackupURL.isEmpty() ) 1967 { 1968 Reference< XInputStream > aTempInput = aTempCont.openStream(); 1969 bTransactStarted = true; 1970 aOriginalContent.setPropertyValue( "Size", uno::makeAny( sal_Int64(0) ) ); 1971 aOriginalContent.writeStream( aTempInput, bOverWrite ); 1972 bResult = true; 1973 } 1974 else 1975 { 1976 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP; 1977 } 1978 } 1979 else 1980 { 1981 Reference< XInputStream > aTempInput = aTempCont.openStream(); 1982 aOriginalContent.writeStream( aTempInput, bOverWrite ); 1983 bResult = true; 1984 } 1985 } 1986 } 1987 catch ( const css::ucb::CommandAbortedException& ) 1988 { 1989 pImpl->m_eError = ERRCODE_ABORT; 1990 } 1991 catch ( const css::ucb::CommandFailedException& ) 1992 { 1993 pImpl->m_eError = ERRCODE_ABORT; 1994 } 1995 catch ( const css::ucb::InteractiveIOException& r ) 1996 { 1997 if ( r.Code == IOErrorCode_ACCESS_DENIED ) 1998 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED; 1999 else if ( r.Code == IOErrorCode_NOT_EXISTING ) 2000 pImpl->m_eError = ERRCODE_IO_NOTEXISTS; 2001 else if ( r.Code == IOErrorCode_CANT_READ ) 2002 pImpl->m_eError = ERRCODE_IO_CANTREAD; 2003 else 2004 pImpl->m_eError = ERRCODE_IO_GENERAL; 2005 } 2006 catch ( const css::uno::Exception& ) 2007 { 2008 pImpl->m_eError = ERRCODE_IO_GENERAL; 2009 } 2010 2011 if ( bResult ) 2012 { 2013 if ( pImpl->pTempFile ) 2014 { 2015 pImpl->pTempFile->EnableKillingFile(); 2016 pImpl->pTempFile.reset(); 2017 } 2018 } 2019 else if ( bTransactStarted ) 2020 { 2021 UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ); 2022 } 2023 } 2024 else 2025 pImpl->m_eError = ERRCODE_IO_CANTREAD; 2026 } 2027 2028 2029 bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet ) 2030 { 2031 if ( GetError() ) 2032 return false; 2033 2034 // if the document had no password it should be stored without password 2035 // if the document had password it should be stored with the same password 2036 // otherwise the stream copying can not be done 2037 const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false); 2038 const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false); 2039 if ( ( !pNewPassItem && !pOldPassItem ) 2040 || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) ) 2041 { 2042 // the filter must be the same 2043 const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false); 2044 const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false); 2045 if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() ) 2046 { 2047 // get the input stream and copy it 2048 // in case of success return true 2049 uno::Reference< io::XInputStream > xInStream = GetInputStream(); 2050 2051 ResetError(); 2052 if ( xInStream.is() ) 2053 { 2054 try 2055 { 2056 uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY ); 2057 sal_Int64 nPos = 0; 2058 if ( xSeek.is() ) 2059 { 2060 nPos = xSeek->getPosition(); 2061 xSeek->seek( 0 ); 2062 } 2063 2064 uno::Reference < css::ucb::XCommandEnvironment > xEnv; 2065 ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() ); 2066 2067 InsertCommandArgument aInsertArg; 2068 aInsertArg.Data = xInStream; 2069 const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false); 2070 if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite 2071 aInsertArg.ReplaceExisting = false; 2072 else 2073 aInsertArg.ReplaceExisting = true; // default is overwrite existing files 2074 2075 Any aCmdArg; 2076 aCmdArg <<= aInsertArg; 2077 aTargetContent.executeCommand( "insert", 2078 aCmdArg ); 2079 2080 if ( xSeek.is() ) 2081 xSeek->seek( nPos ); 2082 2083 return true; 2084 } 2085 catch( const uno::Exception& ) 2086 {} 2087 } 2088 } 2089 } 2090 2091 return false; 2092 } 2093 2094 2095 void SfxMedium::Transfer_Impl() 2096 { 2097 // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item 2098 OUString aNameURL; 2099 if ( pImpl->pTempFile ) 2100 aNameURL = pImpl->pTempFile->GetURL(); 2101 else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode ) 2102 { 2103 // makes sense only in case logic name is set 2104 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL ) 2105 != osl::FileBase::E_None ) 2106 SAL_WARN( "sfx.doc", "The medium name is not convertible!" ); 2107 } 2108 2109 if ( aNameURL.isEmpty() || ( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) ) 2110 return; 2111 2112 SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" ); 2113 2114 Reference < css::ucb::XCommandEnvironment > xEnv; 2115 Reference< XOutputStream > rOutStream; 2116 2117 // in case an output stream is provided from outside and the URL is correct 2118 // commit to the stream 2119 if (pImpl->m_aLogicName.startsWith("private:stream")) 2120 { 2121 // TODO/LATER: support storing to SID_STREAM 2122 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false); 2123 if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) ) 2124 { 2125 if ( pImpl->xStorage.is() ) 2126 CloseStorage(); 2127 2128 CloseStreams_Impl(); 2129 2130 INetURLObject aSource( aNameURL ); 2131 ::ucbhelper::Content aTempCont; 2132 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) ) 2133 { 2134 try 2135 { 2136 sal_Int32 nRead; 2137 sal_Int32 nBufferSize = 32767; 2138 Sequence < sal_Int8 > aSequence ( nBufferSize ); 2139 Reference< XInputStream > aTempInput = aTempCont.openStream(); 2140 2141 do 2142 { 2143 nRead = aTempInput->readBytes ( aSequence, nBufferSize ); 2144 if ( nRead < nBufferSize ) 2145 { 2146 Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead ); 2147 rOutStream->writeBytes ( aTempBuf ); 2148 } 2149 else 2150 rOutStream->writeBytes ( aSequence ); 2151 } 2152 while ( nRead == nBufferSize ); 2153 2154 // remove temporary file 2155 if ( pImpl->pTempFile ) 2156 { 2157 pImpl->pTempFile->EnableKillingFile(); 2158 pImpl->pTempFile.reset(); 2159 } 2160 } 2161 catch( const Exception& ) 2162 {} 2163 } 2164 } 2165 else 2166 { 2167 SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" ); 2168 SetError(ERRCODE_IO_GENERAL); 2169 } 2170 2171 // free the reference 2172 if ( pImpl->m_pSet ) 2173 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM ); 2174 2175 return; 2176 } 2177 2178 GetContent(); 2179 if ( !pImpl->aContent.get().is() ) 2180 { 2181 pImpl->m_eError = ERRCODE_IO_NOTEXISTS; 2182 return; 2183 } 2184 2185 INetURLObject aDest( GetURLObject() ); 2186 2187 // source is the temp file written so far 2188 INetURLObject aSource( aNameURL ); 2189 2190 // a special case, an interaction handler should be used for 2191 // authentication in case it is available 2192 Reference< css::ucb::XCommandEnvironment > xComEnv; 2193 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler(); 2194 if (xInteractionHandler.is()) 2195 xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, 2196 Reference< css::ucb::XProgressHandler >() ); 2197 2198 OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); 2199 2200 if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() ) 2201 { 2202 TransactedTransferForFS_Impl( aSource, aDest, xComEnv ); 2203 2204 if (!pImpl->m_bDisableFileSync) 2205 { 2206 // Hideous - no clean way to do this, so we re-open the file just to fsync it 2207 osl::File aFile( aDestURL ); 2208 if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None ) 2209 { 2210 aFile.sync(); 2211 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" ); 2212 aFile.close(); 2213 } 2214 } 2215 } 2216 else 2217 { 2218 // create content for the parent folder and call transfer on that content with the source content 2219 // and the destination file name as parameters 2220 ::ucbhelper::Content aSourceContent; 2221 ::ucbhelper::Content aTransferContent; 2222 2223 ::ucbhelper::Content aDestContent; 2224 (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent ); 2225 // For checkin, we need the object URL, not the parent folder: 2226 if ( !IsInCheckIn( ) ) 2227 { 2228 // Get the parent URL from the XChild if possible: why would the URL necessarily have 2229 // a hierarchical path? It's not always the case for CMIS. 2230 Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY ); 2231 OUString sParentUrl; 2232 if ( xChild.is( ) ) 2233 { 2234 Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY ); 2235 if ( xParent.is( ) ) 2236 { 2237 sParentUrl = xParent->getIdentifier( )->getContentIdentifier(); 2238 } 2239 } 2240 2241 if ( sParentUrl.isEmpty() ) 2242 aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ); 2243 // adjust to above aDest.removeSegment() 2244 else 2245 aDestURL = sParentUrl; 2246 } 2247 2248 // LongName wasn't defined anywhere, only used here... get the Title instead 2249 // as it's less probably empty 2250 OUString aFileName; 2251 Any aAny = aDestContent.getPropertyValue("Title"); 2252 aAny >>= aFileName; 2253 aAny = aDestContent.getPropertyValue( "ObjectId" ); 2254 OUString sObjectId; 2255 aAny >>= sObjectId; 2256 if ( aFileName.isEmpty() ) 2257 aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); 2258 2259 try 2260 { 2261 aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() ); 2262 } 2263 catch (const css::ucb::ContentCreationException& ex) 2264 { 2265 pImpl->m_eError = ERRCODE_IO_GENERAL; 2266 if ( 2267 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) || 2268 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED) 2269 ) 2270 { 2271 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH; 2272 } 2273 } 2274 catch (const css::uno::Exception&) 2275 { 2276 pImpl->m_eError = ERRCODE_IO_GENERAL; 2277 } 2278 2279 if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) 2280 { 2281 // free resources, otherwise the transfer may fail 2282 if ( pImpl->xStorage.is() ) 2283 CloseStorage(); 2284 2285 CloseStreams_Impl(); 2286 2287 (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ); 2288 2289 // check for external parameters that may customize the handling of NameClash situations 2290 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false); 2291 sal_Int32 nNameClash; 2292 if ( pOverWrite && !pOverWrite->GetValue() ) 2293 // argument says: never overwrite 2294 nNameClash = NameClash::ERROR; 2295 else 2296 // default is overwrite existing files 2297 nNameClash = NameClash::OVERWRITE; 2298 2299 try 2300 { 2301 OUString aMimeType = pImpl->getFilterMimeType(); 2302 ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy; 2303 bool bMajor = false; 2304 OUString sComment; 2305 if ( IsInCheckIn( ) ) 2306 { 2307 eOperation = ::ucbhelper::InsertOperation::Checkin; 2308 const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false); 2309 bMajor = pMajor && pMajor->GetValue( ); 2310 const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false); 2311 if ( pComments ) 2312 sComment = pComments->GetValue( ); 2313 } 2314 OUString sResultURL; 2315 aTransferContent.transferContent( 2316 aSourceContent, eOperation, 2317 aFileName, nNameClash, aMimeType, bMajor, sComment, 2318 &sResultURL, sObjectId ); 2319 2320 if ( !sResultURL.isEmpty( ) ) // Likely to happen only for checkin 2321 SwitchDocumentToFile( sResultURL ); 2322 try 2323 { 2324 if ( GetURLObject().isAnyKnownWebDAVScheme() && 2325 eOperation == ::ucbhelper::InsertOperation::Copy ) 2326 { 2327 // tdf#95272 try to re-issue a lock command when a new file is created. 2328 // This may be needed because some WebDAV servers fail to implement the 2329 // 'LOCK on unallocated reference', see issue comment: 2330 // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8> 2331 // and specification at: 2332 // <http://tools.ietf.org/html/rfc4918#section-7.3> 2333 // If the WebDAV resource is already locked by this LO instance, nothing will 2334 // happen, e.g. the LOCK method will not be sent to the server. 2335 ::ucbhelper::Content aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() ); 2336 aLockContent.lock(); 2337 } 2338 } 2339 catch ( css::uno::Exception & ) 2340 { 2341 TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" ); 2342 } 2343 } 2344 catch ( const css::ucb::CommandAbortedException& ) 2345 { 2346 pImpl->m_eError = ERRCODE_ABORT; 2347 } 2348 catch ( const css::ucb::CommandFailedException& ) 2349 { 2350 pImpl->m_eError = ERRCODE_ABORT; 2351 } 2352 catch ( const css::ucb::InteractiveIOException& r ) 2353 { 2354 if ( r.Code == IOErrorCode_ACCESS_DENIED ) 2355 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED; 2356 else if ( r.Code == IOErrorCode_NOT_EXISTING ) 2357 pImpl->m_eError = ERRCODE_IO_NOTEXISTS; 2358 else if ( r.Code == IOErrorCode_CANT_READ ) 2359 pImpl->m_eError = ERRCODE_IO_CANTREAD; 2360 else 2361 pImpl->m_eError = ERRCODE_IO_GENERAL; 2362 } 2363 catch ( const css::uno::Exception& ) 2364 { 2365 pImpl->m_eError = ERRCODE_IO_GENERAL; 2366 } 2367 2368 // do not switch from temporary file in case of nonfile protocol 2369 } 2370 } 2371 2372 if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile ) 2373 { 2374 // without a TempFile the physical and logical name should be the same after successful transfer 2375 if (osl::FileBase::getSystemPathFromFileURL( 2376 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName ) 2377 != osl::FileBase::E_None) 2378 { 2379 pImpl->m_aName.clear(); 2380 } 2381 pImpl->m_bSalvageMode = false; 2382 } 2383 } 2384 2385 2386 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent, 2387 const OUString& aPrefix, 2388 const OUString& aExtension, 2389 const OUString& aDestDir ) 2390 { 2391 if ( !pImpl->m_aBackupURL.isEmpty() ) 2392 return; // the backup was done already 2393 2394 ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir ); 2395 2396 INetURLObject aBackObj( aTransactTemp.GetURL() ); 2397 OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); 2398 2399 Reference < css::ucb::XCommandEnvironment > xDummyEnv; 2400 ::ucbhelper::Content aBackupCont; 2401 if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) ) 2402 { 2403 try 2404 { 2405 OUString sMimeType = pImpl->getFilterMimeType(); 2406 aBackupCont.transferContent( aOriginalContent, 2407 ::ucbhelper::InsertOperation::Copy, 2408 aBackupName, 2409 NameClash::OVERWRITE, 2410 sMimeType ); 2411 pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); 2412 pImpl->m_bRemoveBackup = true; 2413 } 2414 catch( const Exception& ) 2415 {} 2416 } 2417 2418 if ( pImpl->m_aBackupURL.isEmpty() ) 2419 aTransactTemp.EnableKillingFile(); 2420 } 2421 2422 2423 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent ) 2424 { 2425 if ( !pImpl->m_aBackupURL.isEmpty() ) 2426 return; // the backup was done already 2427 2428 OUString aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, 2429 true, 2430 INetURLObject::DecodeMechanism::NONE ); 2431 2432 sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' ); 2433 OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen ); 2434 OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen ); 2435 OUString aBakDir = SvtPathOptions().GetBackupPath(); 2436 2437 // create content for the parent folder ( = backup folder ) 2438 ::ucbhelper::Content aContent; 2439 Reference < css::ucb::XCommandEnvironment > xEnv; 2440 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) ) 2441 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir ); 2442 2443 if ( !pImpl->m_aBackupURL.isEmpty() ) 2444 return; 2445 2446 // the copying to the backup catalog failed ( for example because 2447 // of using an encrypted partition as target catalog ) 2448 // since the user did not specify to make backup explicitly 2449 // office should try to make backup in another place, 2450 // target catalog does not look bad for this case ( and looks 2451 // to be the only way for encrypted partitions ) 2452 2453 INetURLObject aDest = GetURLObject(); 2454 if ( aDest.removeSegment() ) 2455 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); 2456 } 2457 2458 2459 void SfxMedium::DoBackup_Impl() 2460 { 2461 // source file name is the logical name of this medium 2462 INetURLObject aSource( GetURLObject() ); 2463 2464 // there is nothing to backup in case source file does not exist 2465 if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) 2466 return; 2467 2468 bool bSuccess = false; 2469 2470 // get path for backups 2471 OUString aBakDir = SvtPathOptions().GetBackupPath(); 2472 if( !aBakDir.isEmpty() ) 2473 { 2474 // create content for the parent folder ( = backup folder ) 2475 ::ucbhelper::Content aContent; 2476 Reference < css::ucb::XCommandEnvironment > xEnv; 2477 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) ) 2478 { 2479 // save as ".bak" file 2480 INetURLObject aDest( aBakDir ); 2481 aDest.insertName( aSource.getName() ); 2482 aDest.setExtension( "bak" ); 2483 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); 2484 2485 // create a content for the source file 2486 ::ucbhelper::Content aSourceContent; 2487 if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) ) 2488 { 2489 try 2490 { 2491 // do the transfer ( copy source file to backup dir ) 2492 OUString sMimeType = pImpl->getFilterMimeType(); 2493 aContent.transferContent( aSourceContent, 2494 ::ucbhelper::InsertOperation::Copy, 2495 aFileName, 2496 NameClash::OVERWRITE, 2497 sMimeType ); 2498 pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ); 2499 pImpl->m_bRemoveBackup = false; 2500 bSuccess = true; 2501 } 2502 catch ( const css::uno::Exception& ) 2503 { 2504 } 2505 } 2506 } 2507 } 2508 2509 if ( !bSuccess ) 2510 { 2511 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP; 2512 } 2513 } 2514 2515 2516 void SfxMedium::ClearBackup_Impl() 2517 { 2518 if( pImpl->m_bRemoveBackup ) 2519 { 2520 // currently a document is always stored in a new medium, 2521 // thus if a backup can not be removed the backup URL should not be cleaned 2522 if ( !pImpl->m_aBackupURL.isEmpty() ) 2523 { 2524 if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) ) 2525 { 2526 pImpl->m_bRemoveBackup = false; 2527 pImpl->m_aBackupURL.clear(); 2528 } 2529 else 2530 { 2531 2532 SAL_WARN( "sfx.doc", "Couldn't remove backup file!"); 2533 } 2534 } 2535 } 2536 else 2537 pImpl->m_aBackupURL.clear(); 2538 } 2539 2540 2541 void SfxMedium::GetLockingStream_Impl() 2542 { 2543 if ( GetURLObject().GetProtocol() != INetProtocol::File 2544 || pImpl->m_xLockingStream.is() ) 2545 return; 2546 2547 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false); 2548 if ( pWriteStreamItem ) 2549 pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream; 2550 2551 if ( pImpl->m_xLockingStream.is() ) 2552 return; 2553 2554 // open the original document 2555 uno::Sequence< beans::PropertyValue > xProps; 2556 TransformItems( SID_OPENDOC, *GetItemSet(), xProps ); 2557 utl::MediaDescriptor aMedium( xProps ); 2558 2559 aMedium.addInputStreamOwnLock(); 2560 2561 uno::Reference< io::XInputStream > xInputStream; 2562 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->m_xLockingStream; 2563 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= xInputStream; 2564 2565 if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() ) 2566 { 2567 // the medium is still based on the original file, it makes sense to initialize the streams 2568 if ( pImpl->m_xLockingStream.is() ) 2569 pImpl->xStream = pImpl->m_xLockingStream; 2570 2571 if ( xInputStream.is() ) 2572 pImpl->xInputStream = xInputStream; 2573 2574 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() ) 2575 pImpl->xInputStream = pImpl->xStream->getInputStream(); 2576 } 2577 } 2578 2579 2580 void SfxMedium::GetMedium_Impl() 2581 { 2582 if ( pImpl->m_pInStream 2583 && (!pImpl->bIsTemp || pImpl->xInputStream.is() || pImpl->m_xInputStreamToLoadFrom.is() || pImpl->xStream.is() || pImpl->m_xLockingStream.is() ) ) 2584 return; 2585 2586 pImpl->bDownloadDone = false; 2587 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler(); 2588 2589 //TODO/MBA: need support for SID_STREAM 2590 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false); 2591 const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false); 2592 if ( pWriteStreamItem ) 2593 { 2594 pWriteStreamItem->GetValue() >>= pImpl->xStream; 2595 2596 if ( pInStreamItem ) 2597 pInStreamItem->GetValue() >>= pImpl->xInputStream; 2598 2599 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() ) 2600 pImpl->xInputStream = pImpl->xStream->getInputStream(); 2601 } 2602 else if ( pInStreamItem ) 2603 { 2604 pInStreamItem->GetValue() >>= pImpl->xInputStream; 2605 } 2606 else 2607 { 2608 uno::Sequence < beans::PropertyValue > xProps; 2609 OUString aFileName; 2610 if (!pImpl->m_aName.isEmpty()) 2611 { 2612 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName ) 2613 != osl::FileBase::E_None ) 2614 { 2615 SAL_WARN( "sfx.doc", "Physical name not convertible!"); 2616 } 2617 } 2618 else 2619 aFileName = GetName(); 2620 2621 // in case the temporary file exists the streams should be initialized from it, 2622 // but the original MediaDescriptor should not be changed 2623 bool bFromTempFile = ( pImpl->pTempFile != nullptr ); 2624 2625 if ( !bFromTempFile ) 2626 { 2627 GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) ); 2628 if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) ) 2629 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 2630 if (xInteractionHandler.is()) 2631 GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny(xInteractionHandler) ) ); 2632 } 2633 2634 if ( pImpl->m_xInputStreamToLoadFrom.is() ) 2635 { 2636 pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom; 2637 if (pImpl->m_bInputStreamIsReadOnly) 2638 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); 2639 } 2640 else 2641 { 2642 TransformItems( SID_OPENDOC, *GetItemSet(), xProps ); 2643 utl::MediaDescriptor aMedium( xProps ); 2644 2645 if ( pImpl->m_xLockingStream.is() && !bFromTempFile ) 2646 { 2647 // the medium is not based on the temporary file, so the original stream can be used 2648 pImpl->xStream = pImpl->m_xLockingStream; 2649 } 2650 else 2651 { 2652 if ( bFromTempFile ) 2653 { 2654 aMedium[utl::MediaDescriptor::PROP_URL()] <<= aFileName; 2655 aMedium.erase( utl::MediaDescriptor::PROP_READONLY() ); 2656 aMedium.addInputStream(); 2657 } 2658 else if ( GetURLObject().GetProtocol() == INetProtocol::File ) 2659 { 2660 // use the special locking approach only for file URLs 2661 aMedium.addInputStreamOwnLock(); 2662 } 2663 else 2664 { 2665 // add a check for protocol, if it's http or https or provide webdav then add 2666 // the interaction handler to be used by the authentication dialog 2667 if ( GetURLObject().isAnyKnownWebDAVScheme() ) 2668 { 2669 aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER()] <<= GetInteractionHandler( true ); 2670 } 2671 aMedium.addInputStream(); 2672 } 2673 // the ReadOnly property set in aMedium is ignored 2674 // the check is done in LockOrigFileOnDemand() for file and non-file URLs 2675 2676 //TODO/MBA: what happens if property is not there?! 2677 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->xStream; 2678 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= pImpl->xInputStream; 2679 } 2680 2681 GetContent(); 2682 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() ) 2683 pImpl->xInputStream = pImpl->xStream->getInputStream(); 2684 } 2685 2686 if ( !bFromTempFile ) 2687 { 2688 //TODO/MBA: need support for SID_STREAM 2689 if ( pImpl->xStream.is() ) 2690 GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, makeAny( pImpl->xStream ) ) ); 2691 2692 GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, makeAny( pImpl->xInputStream ) ) ); 2693 } 2694 } 2695 2696 //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor 2697 if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() ) 2698 SetError(ERRCODE_IO_ACCESSDENIED); 2699 2700 if ( !GetError() && !pImpl->m_pInStream ) 2701 { 2702 if ( pImpl->xStream.is() ) 2703 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream ); 2704 else if ( pImpl->xInputStream.is() ) 2705 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream ); 2706 } 2707 2708 pImpl->bDownloadDone = true; 2709 pImpl->aDoneLink.ClearPendingCall(); 2710 ErrCode nError = GetError(); 2711 pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) ); 2712 } 2713 2714 bool SfxMedium::IsRemote() const 2715 { 2716 return pImpl->m_bRemote; 2717 } 2718 2719 void SfxMedium::SetUpdatePickList(bool bVal) 2720 { 2721 pImpl->bUpdatePickList = bVal; 2722 } 2723 2724 bool SfxMedium::IsUpdatePickList() const 2725 { 2726 return pImpl->bUpdatePickList; 2727 } 2728 2729 void SfxMedium::SetLongName(const OUString &rName) 2730 { 2731 pImpl->m_aLongName = rName; 2732 } 2733 2734 const OUString& SfxMedium::GetLongName() const 2735 { 2736 return pImpl->m_aLongName; 2737 } 2738 2739 void SfxMedium::SetDoneLink( const Link<void*,void>& rLink ) 2740 { 2741 pImpl->aDoneLink = rLink; 2742 } 2743 2744 void SfxMedium::Download( const Link<void*,void>& aLink ) 2745 { 2746 SetDoneLink( aLink ); 2747 GetInStream(); 2748 if ( pImpl->m_pInStream && !aLink.IsSet() ) 2749 { 2750 while( !pImpl->bDownloadDone ) 2751 Application::Yield(); 2752 } 2753 } 2754 2755 2756 void SfxMedium::Init_Impl() 2757 /* [Description] 2758 Includes a valid:: sun:: com:: star:: util:: URL (If a file name was 2759 previously in there) in the logical name and if available sets the 2760 physical name as the file name. 2761 */ 2762 2763 { 2764 Reference< XOutputStream > rOutStream; 2765 2766 // TODO/LATER: handle lifetime of storages 2767 pImpl->bDisposeStorage = false; 2768 2769 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false); 2770 if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() ) 2771 { 2772 pSalvageItem = nullptr; 2773 pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE ); 2774 } 2775 2776 if (!pImpl->m_aLogicName.isEmpty()) 2777 { 2778 INetURLObject aUrl( pImpl->m_aLogicName ); 2779 INetProtocol eProt = aUrl.GetProtocol(); 2780 if ( eProt == INetProtocol::NotValid ) 2781 { 2782 SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" ); 2783 } 2784 else 2785 { 2786 if ( aUrl.HasMark() ) 2787 { 2788 pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE ); 2789 GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) ); 2790 } 2791 2792 // try to convert the URL into a physical name - but never change a physical name 2793 // physical name may be set if the logical name is changed after construction 2794 if ( pImpl->m_aName.isEmpty() ) 2795 osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName ); 2796 else 2797 { 2798 DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" ); 2799 } 2800 } 2801 } 2802 2803 if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() ) 2804 { 2805 pImpl->m_aLogicName = pSalvageItem->GetValue(); 2806 pImpl->m_pURLObj.reset(); 2807 pImpl->m_bSalvageMode = true; 2808 } 2809 2810 // in case output stream is by mistake here 2811 // clear the reference 2812 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false); 2813 if( pOutStreamItem 2814 && ( !( pOutStreamItem->GetValue() >>= rOutStream ) 2815 || !pImpl->m_aLogicName.startsWith("private:stream")) ) 2816 { 2817 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM ); 2818 SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" ); 2819 } 2820 2821 if (!pImpl->m_aLogicName.isEmpty()) 2822 { 2823 // if the logic name is set it should be set in MediaDescriptor as well 2824 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false); 2825 if ( !pFileNameItem ) 2826 { 2827 // let the ItemSet be created if necessary 2828 GetItemSet()->Put( 2829 SfxStringItem( 2830 SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ); 2831 } 2832 } 2833 2834 SetIsRemote_Impl(); 2835 2836 osl::DirectoryItem item; 2837 if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) { 2838 osl::FileStatus stat(osl_FileStatus_Mask_Attributes); 2839 if (item.getFileStatus(stat) == osl::FileBase::E_None 2840 && stat.isValid(osl_FileStatus_Mask_Attributes)) 2841 { 2842 if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0) { 2843 pImpl->m_bOriginallyReadOnly = true; 2844 } 2845 } 2846 } 2847 } 2848 2849 2850 SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl) 2851 { 2852 Init_Impl(); 2853 } 2854 2855 2856 void SfxMedium::UseInteractionHandler( bool bUse ) 2857 { 2858 pImpl->bAllowDefaultIntHdl = bUse; 2859 } 2860 2861 2862 css::uno::Reference< css::task::XInteractionHandler > 2863 SfxMedium::GetInteractionHandler( bool bGetAlways ) 2864 { 2865 // if interaction isn't allowed explicitly ... return empty reference! 2866 if ( !bGetAlways && !pImpl->bUseInteractionHandler ) 2867 return css::uno::Reference< css::task::XInteractionHandler >(); 2868 2869 // search a possible existing handler inside cached item set 2870 if ( pImpl->m_pSet ) 2871 { 2872 css::uno::Reference< css::task::XInteractionHandler > xHandler; 2873 const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false); 2874 if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() ) 2875 return xHandler; 2876 } 2877 2878 // if default interaction isn't allowed explicitly ... return empty reference! 2879 if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl ) 2880 return css::uno::Reference< css::task::XInteractionHandler >(); 2881 2882 // otherwise return cached default handler ... if it exist. 2883 if ( pImpl->xInteraction.is() ) 2884 return pImpl->xInteraction; 2885 2886 // create default handler and cache it! 2887 Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); 2888 pImpl->xInteraction.set( 2889 task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW ); 2890 return pImpl->xInteraction; 2891 } 2892 2893 2894 void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter ) 2895 { 2896 pImpl->m_pFilter = pFilter; 2897 } 2898 2899 const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const 2900 { 2901 return pImpl->m_pFilter; 2902 } 2903 2904 sal_uInt32 SfxMedium::CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter ) 2905 { 2906 sal_uInt32 nHash = 0; 2907 2908 if ( !aPasswd.isEmpty() ) 2909 { 2910 if ( bWriter ) 2911 { 2912 nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd ); 2913 } 2914 else 2915 { 2916 rtl_TextEncoding nEncoding = osl_getThreadTextEncoding(); 2917 nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding ); 2918 } 2919 } 2920 2921 return nHash; 2922 } 2923 2924 2925 void SfxMedium::Close(bool bInDestruction) 2926 { 2927 if ( pImpl->xStorage.is() ) 2928 { 2929 CloseStorage(); 2930 } 2931 2932 CloseStreams_Impl(bInDestruction); 2933 2934 UnlockFile( false ); 2935 } 2936 2937 void SfxMedium::CloseAndRelease() 2938 { 2939 if ( pImpl->xStorage.is() ) 2940 { 2941 CloseStorage(); 2942 } 2943 2944 CloseAndReleaseStreams_Impl(); 2945 2946 UnlockFile( true ); 2947 } 2948 2949 void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV ) 2950 { 2951 pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV; 2952 } 2953 2954 void SfxMedium::DisableFileSync(bool bDisableFileSync) 2955 { 2956 pImpl->m_bDisableFileSync = bDisableFileSync; 2957 } 2958 2959 void SfxMedium::UnlockFile( bool bReleaseLockStream ) 2960 { 2961 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT 2962 (void) bReleaseLockStream; 2963 #else 2964 // check if webdav 2965 if ( GetURLObject().isAnyKnownWebDAVScheme() ) 2966 { 2967 // do nothing if WebDAV locking if disabled 2968 // (shouldn't happen because we already skipped locking, 2969 // see LockOrigFileOnDemand, but just in case ...) 2970 if (!IsWebDAVLockingUsed()) 2971 return; 2972 2973 if ( pImpl->m_bLocked ) 2974 { 2975 // an interaction handler should be used for authentication, if needed 2976 try { 2977 uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true ); 2978 uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler, 2979 Reference< css::ucb::XProgressHandler >() ); 2980 ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext()); 2981 pImpl->m_bLocked = false; 2982 //check if WebDAV unlock was explicitly disabled 2983 if ( !pImpl->m_bDisableUnlockWebDAV ) 2984 aContentToUnlock.unlock(); 2985 } 2986 catch ( uno::Exception& ) 2987 { 2988 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" ); 2989 } 2990 } 2991 return; 2992 } 2993 2994 if ( pImpl->m_xLockingStream.is() ) 2995 { 2996 if ( bReleaseLockStream ) 2997 { 2998 try 2999 { 3000 uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream(); 3001 uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream(); 3002 if ( xInStream.is() ) 3003 xInStream->closeInput(); 3004 if ( xOutStream.is() ) 3005 xOutStream->closeOutput(); 3006 } 3007 catch( const uno::Exception& ) 3008 {} 3009 } 3010 3011 pImpl->m_xLockingStream.clear(); 3012 } 3013 3014 if ( !pImpl->m_bLocked ) 3015 return; 3016 3017 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName ); 3018 3019 try 3020 { 3021 pImpl->m_bLocked = false; 3022 // TODO/LATER: A warning could be shown in case the file is not the own one 3023 aLockFile.RemoveFile(); 3024 } 3025 catch( const io::WrongFormatException& ) 3026 { 3027 try 3028 { 3029 // erase the empty or corrupt file 3030 aLockFile.RemoveFileDirectly(); 3031 } 3032 catch( const uno::Exception& ) 3033 {} 3034 } 3035 catch( const uno::Exception& ) 3036 {} 3037 3038 if(pImpl->m_bMSOLockFileCreated) 3039 { 3040 ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName ); 3041 3042 try 3043 { 3044 pImpl->m_bLocked = false; 3045 // TODO/LATER: A warning could be shown in case the file is not the own one 3046 aMSOLockFile.RemoveFile(); 3047 } 3048 catch( const io::WrongFormatException& ) 3049 { 3050 try 3051 { 3052 // erase the empty or corrupt file 3053 aMSOLockFile.RemoveFileDirectly(); 3054 } 3055 catch( const uno::Exception& ) 3056 {} 3057 } 3058 catch( const uno::Exception& ) 3059 {} 3060 pImpl->m_bMSOLockFileCreated = false; 3061 } 3062 #endif 3063 } 3064 3065 void SfxMedium::CloseAndReleaseStreams_Impl() 3066 { 3067 CloseZipStorage_Impl(); 3068 3069 uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream; 3070 uno::Reference< io::XOutputStream > xOutToClose; 3071 if ( pImpl->xStream.is() ) 3072 { 3073 xOutToClose = pImpl->xStream->getOutputStream(); 3074 3075 // if the locking stream is closed here the related member should be cleaned 3076 if ( pImpl->xStream == pImpl->m_xLockingStream ) 3077 pImpl->m_xLockingStream.clear(); 3078 } 3079 3080 // The probably existing SvStream wrappers should be closed first 3081 CloseStreams_Impl(); 3082 3083 // in case of salvage mode the storage is based on the streams 3084 if ( pImpl->m_bSalvageMode ) 3085 return; 3086 3087 try 3088 { 3089 if ( xInToClose.is() ) 3090 xInToClose->closeInput(); 3091 if ( xOutToClose.is() ) 3092 xOutToClose->closeOutput(); 3093 } 3094 catch ( const uno::Exception& ) 3095 { 3096 } 3097 } 3098 3099 3100 void SfxMedium::CloseStreams_Impl(bool bInDestruction) 3101 { 3102 CloseInStream_Impl(bInDestruction); 3103 CloseOutStream_Impl(); 3104 3105 if ( pImpl->m_pSet ) 3106 pImpl->m_pSet->ClearItem( SID_CONTENT ); 3107 3108 pImpl->aContent = ::ucbhelper::Content(); 3109 } 3110 3111 3112 void SfxMedium::SetIsRemote_Impl() 3113 { 3114 INetURLObject aObj( GetName() ); 3115 switch( aObj.GetProtocol() ) 3116 { 3117 case INetProtocol::Ftp: 3118 case INetProtocol::Http: 3119 case INetProtocol::Https: 3120 pImpl->m_bRemote = true; 3121 break; 3122 default: 3123 pImpl->m_bRemote = GetName().startsWith("private:msgid"); 3124 break; 3125 } 3126 3127 // As files that are written to the remote transmission must also be able 3128 // to be read. 3129 if (pImpl->m_bRemote) 3130 pImpl->m_nStorOpenMode |= StreamMode::READ; 3131 } 3132 3133 3134 void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL ) 3135 { 3136 if (pImpl->aOrigURL.isEmpty()) 3137 pImpl->aOrigURL = pImpl->m_aLogicName; 3138 if( bSetOrigURL ) 3139 pImpl->aOrigURL = aNameP; 3140 pImpl->m_aLogicName = aNameP; 3141 pImpl->m_pURLObj.reset(); 3142 pImpl->aContent = ::ucbhelper::Content(); 3143 Init_Impl(); 3144 } 3145 3146 3147 const OUString& SfxMedium::GetOrigURL() const 3148 { 3149 return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL; 3150 } 3151 3152 3153 void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP ) 3154 { 3155 if ( rNameP != pImpl->m_aName ) 3156 { 3157 pImpl->pTempFile.reset(); 3158 3159 if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() ) 3160 pImpl->aContent = ::ucbhelper::Content(); 3161 3162 pImpl->m_aName = rNameP; 3163 pImpl->m_bTriedStorage = false; 3164 pImpl->bIsStorage = false; 3165 } 3166 } 3167 3168 3169 void SfxMedium::ReOpen() 3170 { 3171 bool bUseInteractionHandler = pImpl->bUseInteractionHandler; 3172 pImpl->bUseInteractionHandler = false; 3173 GetMedium_Impl(); 3174 pImpl->bUseInteractionHandler = bUseInteractionHandler; 3175 } 3176 3177 3178 void SfxMedium::CompleteReOpen() 3179 { 3180 // do not use temporary file for reopen and in case of success throw the temporary file away 3181 bool bUseInteractionHandler = pImpl->bUseInteractionHandler; 3182 pImpl->bUseInteractionHandler = false; 3183 3184 std::unique_ptr<::utl::TempFile> pTmpFile; 3185 if ( pImpl->pTempFile ) 3186 { 3187 pTmpFile = std::move(pImpl->pTempFile); 3188 pImpl->m_aName.clear(); 3189 } 3190 3191 GetMedium_Impl(); 3192 3193 if ( GetError() ) 3194 { 3195 if ( pImpl->pTempFile ) 3196 { 3197 pImpl->pTempFile->EnableKillingFile(); 3198 pImpl->pTempFile.reset(); 3199 } 3200 pImpl->pTempFile = std::move( pTmpFile ); 3201 if ( pImpl->pTempFile ) 3202 pImpl->m_aName = pImpl->pTempFile->GetFileName(); 3203 } 3204 else if (pTmpFile) 3205 { 3206 pTmpFile->EnableKillingFile(); 3207 pTmpFile.reset(); 3208 } 3209 3210 pImpl->bUseInteractionHandler = bUseInteractionHandler; 3211 } 3212 3213 SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, std::unique_ptr<SfxItemSet> pInSet) : 3214 pImpl(new SfxMedium_Impl) 3215 { 3216 pImpl->m_pSet = std::move( pInSet ); 3217 pImpl->m_pFilter = std::move(pFilter); 3218 pImpl->m_aLogicName = rName; 3219 pImpl->m_nStorOpenMode = nOpenMode; 3220 Init_Impl(); 3221 } 3222 3223 SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, std::unique_ptr<SfxItemSet> pInSet) : 3224 pImpl(new SfxMedium_Impl) 3225 { 3226 pImpl->m_pSet = std::move(pInSet); 3227 SfxItemSet * s = GetItemSet(); 3228 if (s->GetItem(SID_REFERER) == nullptr) { 3229 s->Put(SfxStringItem(SID_REFERER, rReferer)); 3230 } 3231 pImpl->m_pFilter = std::move(pFilter); 3232 pImpl->m_aLogicName = rName; 3233 pImpl->m_nStorOpenMode = nOpenMode; 3234 Init_Impl(); 3235 } 3236 3237 SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) : 3238 pImpl(new SfxMedium_Impl) 3239 { 3240 SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() ); 3241 pImpl->m_pSet.reset( pParams ); 3242 TransformParameters( SID_OPENDOC, aArgs, *pParams ); 3243 3244 OUString aFilterProvider, aFilterName; 3245 { 3246 const SfxPoolItem* pItem = nullptr; 3247 if (pImpl->m_pSet->HasItem(SID_FILTER_PROVIDER, &pItem)) 3248 aFilterProvider = static_cast<const SfxStringItem*>(pItem)->GetValue(); 3249 3250 if (pImpl->m_pSet->HasItem(SID_FILTER_NAME, &pItem)) 3251 aFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue(); 3252 } 3253 3254 if (aFilterProvider.isEmpty()) 3255 { 3256 // This is a conventional filter type. 3257 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName ); 3258 } 3259 else 3260 { 3261 // This filter is from an external provider such as orcus. 3262 pImpl->m_pCustomFilter.reset(new SfxFilter(aFilterProvider, aFilterName)); 3263 pImpl->m_pFilter = pImpl->m_pCustomFilter; 3264 } 3265 3266 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false); 3267 if( pSalvageItem ) 3268 { 3269 // QUESTION: there is some treatment of Salvage in Init_Impl; align! 3270 if ( !pSalvageItem->GetValue().isEmpty() ) 3271 { 3272 // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file 3273 // that must be copied here 3274 3275 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false); 3276 if (!pFileNameItem) throw uno::RuntimeException(); 3277 OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() ); 3278 if ( !aNewTempFileURL.isEmpty() ) 3279 { 3280 pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) ); 3281 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM ); 3282 pImpl->m_pSet->ClearItem( SID_STREAM ); 3283 pImpl->m_pSet->ClearItem( SID_CONTENT ); 3284 } 3285 else 3286 { 3287 SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" ); 3288 } 3289 } 3290 } 3291 3292 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false); 3293 if ( pReadOnlyItem && pReadOnlyItem->GetValue() ) 3294 pImpl->m_bOriginallyLoadedReadOnly = true; 3295 3296 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false); 3297 if (!pFileNameItem) throw uno::RuntimeException(); 3298 pImpl->m_aLogicName = pFileNameItem->GetValue(); 3299 pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly 3300 ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE; 3301 Init_Impl(); 3302 } 3303 3304 3305 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const SfxItemSet* p ) : 3306 pImpl(new SfxMedium_Impl) 3307 { 3308 OUString aType = SfxFilter::GetTypeFromStorage(rStor); 3309 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType ); 3310 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" ); 3311 3312 Init_Impl(); 3313 pImpl->xStorage = rStor; 3314 pImpl->bDisposeStorage = false; 3315 3316 // always take BaseURL first, could be overwritten by ItemSet 3317 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) ); 3318 if ( p ) 3319 GetItemSet()->Put( *p ); 3320 } 3321 3322 3323 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const SfxItemSet* p ) : 3324 pImpl(new SfxMedium_Impl) 3325 { 3326 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName ); 3327 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" ); 3328 3329 Init_Impl(); 3330 pImpl->xStorage = rStor; 3331 pImpl->bDisposeStorage = false; 3332 3333 // always take BaseURL first, could be overwritten by ItemSet 3334 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) ); 3335 if ( p ) 3336 GetItemSet()->Put( *p ); 3337 } 3338 3339 3340 SfxMedium::~SfxMedium() 3341 { 3342 // if there is a requirement to clean the backup this is the last possibility to do it 3343 ClearBackup_Impl(); 3344 3345 Close(/*bInDestruction*/true); 3346 3347 if( !pImpl->bIsTemp || pImpl->m_aName.isEmpty() ) 3348 return; 3349 3350 OUString aTemp; 3351 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp ) 3352 != osl::FileBase::E_None ) 3353 { 3354 SAL_WARN( "sfx.doc", "Physical name not convertible!"); 3355 } 3356 3357 if ( !::utl::UCBContentHelper::Kill( aTemp ) ) 3358 { 3359 SAL_WARN( "sfx.doc", "Couldn't remove temporary file!"); 3360 } 3361 } 3362 3363 const OUString& SfxMedium::GetName() const 3364 { 3365 return pImpl->m_aLogicName; 3366 } 3367 3368 const INetURLObject& SfxMedium::GetURLObject() const 3369 { 3370 if (!pImpl->m_pURLObj) 3371 { 3372 pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) ); 3373 pImpl->m_pURLObj->SetMark(""); 3374 } 3375 3376 return *pImpl->m_pURLObj; 3377 } 3378 3379 void SfxMedium::SetExpired_Impl( const DateTime& rDateTime ) 3380 { 3381 pImpl->aExpireTime = rDateTime; 3382 } 3383 3384 3385 bool SfxMedium::IsExpired() const 3386 { 3387 return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM ); 3388 } 3389 3390 3391 SfxFrame* SfxMedium::GetLoadTargetFrame() const 3392 { 3393 return pImpl->wLoadTargetFrame; 3394 } 3395 3396 void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly ) 3397 { 3398 pImpl->m_xInputStreamToLoadFrom = xInputStream; 3399 pImpl->m_bInputStreamIsReadOnly = bIsReadOnly; 3400 } 3401 3402 void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame ) 3403 { 3404 pImpl->wLoadTargetFrame = pFrame; 3405 } 3406 3407 3408 void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor ) 3409 { 3410 pImpl->xStorage = rStor; 3411 } 3412 3413 3414 SfxItemSet* SfxMedium::GetItemSet() const 3415 { 3416 // this method *must* return an ItemSet, returning NULL can cause crashes 3417 if (!pImpl->m_pSet) 3418 pImpl->m_pSet.reset( new SfxAllItemSet( SfxGetpApp()->GetPool() ) ); 3419 return pImpl->m_pSet.get(); 3420 } 3421 3422 3423 SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl() 3424 { 3425 if( !pImpl->xAttributes.is() ) 3426 { 3427 pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator ); 3428 3429 if ( GetContent().is() ) 3430 { 3431 try 3432 { 3433 Any aAny = pImpl->aContent.getPropertyValue("MediaType"); 3434 OUString aContentType; 3435 aAny >>= aContentType; 3436 3437 pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) ); 3438 } 3439 catch ( const css::uno::Exception& ) 3440 { 3441 } 3442 } 3443 } 3444 3445 return pImpl->xAttributes.get(); 3446 } 3447 3448 css::uno::Reference< css::io::XInputStream > const & SfxMedium::GetInputStream() 3449 { 3450 if ( !pImpl->xInputStream.is() ) 3451 GetMedium_Impl(); 3452 return pImpl->xInputStream; 3453 } 3454 3455 const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload ) 3456 { 3457 // if the medium has no name, then this medium should represent a new document and can have no version info 3458 if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.hasElements() && 3459 ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() ) 3460 { 3461 uno::Reference < document::XDocumentRevisionListPersistence > xReader = 3462 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() ); 3463 try 3464 { 3465 pImpl->aVersions = xReader->load( GetStorage() ); 3466 } 3467 catch ( const uno::Exception& ) 3468 { 3469 } 3470 } 3471 3472 if ( !pImpl->m_bVersionsAlreadyLoaded ) 3473 pImpl->m_bVersionsAlreadyLoaded = true; 3474 3475 return pImpl->aVersions; 3476 } 3477 3478 uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage ) 3479 { 3480 uno::Reference < document::XDocumentRevisionListPersistence > xReader = 3481 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() ); 3482 try 3483 { 3484 return xReader->load( xStorage ); 3485 } 3486 catch ( const uno::Exception& ) 3487 { 3488 } 3489 3490 return uno::Sequence < util::RevisionTag >(); 3491 } 3492 3493 void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision ) 3494 { 3495 if ( !GetStorage().is() ) 3496 return; 3497 3498 // To determine a unique name for the stream 3499 std::vector<sal_uInt32> aLongs; 3500 sal_Int32 nLength = pImpl->aVersions.getLength(); 3501 for ( const auto& rVersion : std::as_const(pImpl->aVersions) ) 3502 { 3503 sal_uInt32 nVer = static_cast<sal_uInt32>( rVersion.Identifier.copy(7).toInt32()); 3504 size_t n; 3505 for ( n=0; n<aLongs.size(); ++n ) 3506 if ( nVer<aLongs[n] ) 3507 break; 3508 3509 aLongs.insert( aLongs.begin()+n, nVer ); 3510 } 3511 3512 std::vector<sal_uInt32>::size_type nKey; 3513 for ( nKey=0; nKey<aLongs.size(); ++nKey ) 3514 if ( aLongs[nKey] > nKey+1 ) 3515 break; 3516 3517 OUString aRevName = "Version" + OUString::number( nKey + 1 ); 3518 pImpl->aVersions.realloc( nLength+1 ); 3519 rRevision.Identifier = aRevName; 3520 pImpl->aVersions[nLength] = rRevision; 3521 } 3522 3523 void SfxMedium::RemoveVersion_Impl( const OUString& rName ) 3524 { 3525 if ( !pImpl->aVersions.hasElements() ) 3526 return; 3527 3528 auto pVersion = std::find_if(pImpl->aVersions.begin(), pImpl->aVersions.end(), 3529 [&rName](const auto& rVersion) { return rVersion.Identifier == rName; }); 3530 if (pVersion != pImpl->aVersions.end()) 3531 { 3532 auto nIndex = static_cast<sal_Int32>(std::distance(pImpl->aVersions.begin(), pVersion)); 3533 comphelper::removeElementAt(pImpl->aVersions, nIndex); 3534 } 3535 } 3536 3537 bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium ) 3538 { 3539 if ( rMedium.pImpl->aVersions.hasElements() ) 3540 { 3541 pImpl->aVersions = rMedium.pImpl->aVersions; 3542 return true; 3543 } 3544 3545 return false; 3546 } 3547 3548 void SfxMedium::SaveVersionList_Impl() 3549 { 3550 if ( !GetStorage().is() ) 3551 return; 3552 3553 if ( !pImpl->aVersions.hasElements() ) 3554 return; 3555 3556 uno::Reference < document::XDocumentRevisionListPersistence > xWriter = 3557 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() ); 3558 try 3559 { 3560 xWriter->store( GetStorage(), pImpl->aVersions ); 3561 } 3562 catch ( const uno::Exception& ) 3563 { 3564 } 3565 } 3566 3567 bool SfxMedium::IsReadOnly() const 3568 { 3569 // a) ReadOnly filter can't produce read/write contents! 3570 bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY); 3571 3572 // b) if filter allow read/write contents .. check open mode of the storage 3573 if (!bReadOnly) 3574 bReadOnly = !( GetOpenMode() & StreamMode::WRITE ); 3575 3576 // c) the API can force the readonly state! 3577 if (!bReadOnly) 3578 { 3579 const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false); 3580 if (pItem) 3581 bReadOnly = pItem->GetValue(); 3582 } 3583 3584 return bReadOnly; 3585 } 3586 3587 bool SfxMedium::IsOriginallyReadOnly() const 3588 { 3589 return pImpl->m_bOriginallyReadOnly; 3590 } 3591 3592 bool SfxMedium::IsOriginallyLoadedReadOnly() const 3593 { 3594 return pImpl->m_bOriginallyLoadedReadOnly; 3595 } 3596 3597 bool SfxMedium::SetWritableForUserOnly( const OUString& aURL ) 3598 { 3599 // UCB does not allow to allow write access only for the user, 3600 // use osl API 3601 bool bResult = false; 3602 3603 ::osl::DirectoryItem aDirItem; 3604 if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None ) 3605 { 3606 ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes ); 3607 if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None 3608 && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) ) 3609 { 3610 sal_uInt64 nAttributes = aFileStatus.getAttributes(); 3611 3612 nAttributes &= ~(osl_File_Attribute_OwnWrite | 3613 osl_File_Attribute_GrpWrite | 3614 osl_File_Attribute_OthWrite | 3615 osl_File_Attribute_ReadOnly); 3616 nAttributes |= (osl_File_Attribute_OwnWrite | 3617 osl_File_Attribute_OwnRead); 3618 3619 bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None ); 3620 } 3621 } 3622 3623 return bResult; 3624 } 3625 3626 namespace 3627 { 3628 /// Get the parent directory of a temporary file for output purposes. 3629 OUString GetLogicBase(std::unique_ptr<SfxMedium_Impl> const & pImpl) 3630 { 3631 OUString aLogicBase; 3632 3633 // In a sandboxed environment we don't want to attempt to create temporary files in the same 3634 // directory where the user has selected an output file to be stored. The sandboxed process has 3635 // permission only to create the specifically named output file in that directory. 3636 #if !HAVE_FEATURE_MACOSX_SANDBOX 3637 3638 if (comphelper::isFileUrl(pImpl->m_aLogicName) && !pImpl->m_pInStream) 3639 { 3640 // Try to create the temp file in the same directory when storing. 3641 sal_Int32 nOffset = pImpl->m_aLogicName.lastIndexOf("/"); 3642 if (nOffset != -1) 3643 aLogicBase = pImpl->m_aLogicName.copy(0, nOffset); 3644 if (aLogicBase == "file://") 3645 // Doesn't make sense. 3646 aLogicBase.clear(); 3647 } 3648 3649 if (pImpl->m_bHasEmbeddedObjects) 3650 // Embedded objects would mean a special base, ignore that. 3651 aLogicBase.clear(); 3652 3653 #endif // !HAVE_FEATURE_MACOSX_SANDBOX 3654 3655 return aLogicBase; 3656 } 3657 } 3658 3659 void SfxMedium::CreateTempFile( bool bReplace ) 3660 { 3661 if ( pImpl->pTempFile ) 3662 { 3663 if ( !bReplace ) 3664 return; 3665 3666 pImpl->pTempFile.reset(); 3667 pImpl->m_aName.clear(); 3668 } 3669 3670 OUString aLogicBase = GetLogicBase(pImpl); 3671 pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) ); 3672 pImpl->pTempFile->EnableKillingFile(); 3673 pImpl->m_aName = pImpl->pTempFile->GetFileName(); 3674 OUString aTmpURL = pImpl->pTempFile->GetURL(); 3675 if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() ) 3676 { 3677 SetError(ERRCODE_IO_CANTWRITE); 3678 return; 3679 } 3680 3681 if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) ) 3682 { 3683 bool bTransferSuccess = false; 3684 3685 if ( GetContent().is() 3686 && GetURLObject().GetProtocol() == INetProtocol::File 3687 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) 3688 { 3689 // if there is already such a document, we should copy it 3690 // if it is a file system use OS copy process 3691 try 3692 { 3693 uno::Reference< css::ucb::XCommandEnvironment > xComEnv; 3694 INetURLObject aTmpURLObj( aTmpURL ); 3695 OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT, 3696 true, 3697 INetURLObject::DecodeMechanism::WithCharset ); 3698 if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() ) 3699 { 3700 ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() ); 3701 OUString sMimeType = pImpl->getFilterMimeType(); 3702 aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType ); 3703 SetWritableForUserOnly( aTmpURL ); 3704 bTransferSuccess = true; 3705 } 3706 } 3707 catch( const uno::Exception& ) 3708 {} 3709 3710 if ( bTransferSuccess ) 3711 { 3712 CloseOutStream(); 3713 CloseInStream(); 3714 } 3715 } 3716 3717 if ( !bTransferSuccess && pImpl->m_pInStream ) 3718 { 3719 // the case when there is no URL-access available or this is a remote protocol 3720 // but there is an input stream 3721 GetOutStream(); 3722 if ( pImpl->m_pOutStream ) 3723 { 3724 std::unique_ptr<char[]> pBuf(new char [8192]); 3725 ErrCode nErr = ERRCODE_NONE; 3726 3727 pImpl->m_pInStream->Seek(0); 3728 pImpl->m_pOutStream->Seek(0); 3729 3730 while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE ) 3731 { 3732 sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf.get(), 8192); 3733 nErr = pImpl->m_pInStream->GetError(); 3734 pImpl->m_pOutStream->WriteBytes( pBuf.get(), nRead ); 3735 } 3736 3737 bTransferSuccess = true; 3738 CloseInStream(); 3739 } 3740 CloseOutStream_Impl(); 3741 } 3742 else 3743 { 3744 // Quite strange design, but currently it is expected that in this case no transfer happens 3745 // TODO/LATER: get rid of this inconsistent part of the call design 3746 bTransferSuccess = true; 3747 CloseInStream(); 3748 } 3749 3750 if ( !bTransferSuccess ) 3751 { 3752 SetError(ERRCODE_IO_CANTWRITE); 3753 return; 3754 } 3755 } 3756 3757 CloseStorage(); 3758 } 3759 3760 3761 void SfxMedium::CreateTempFileNoCopy() 3762 { 3763 // this call always replaces the existing temporary file 3764 pImpl->pTempFile.reset(); 3765 3766 OUString aLogicBase = GetLogicBase(pImpl); 3767 pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) ); 3768 pImpl->pTempFile->EnableKillingFile(); 3769 pImpl->m_aName = pImpl->pTempFile->GetFileName(); 3770 if ( pImpl->m_aName.isEmpty() ) 3771 { 3772 SetError(ERRCODE_IO_CANTWRITE); 3773 return; 3774 } 3775 3776 CloseOutStream_Impl(); 3777 CloseStorage(); 3778 } 3779 3780 bool SfxMedium::SignDocumentContentUsingCertificate(bool bHasValidDocumentSignature, 3781 const Reference<XCertificate>& xCertificate) 3782 { 3783 bool bChanges = false; 3784 3785 if (IsOpen() || GetError()) 3786 { 3787 SAL_WARN("sfx.doc", "The medium must be closed by the signer!"); 3788 return bChanges; 3789 } 3790 3791 // The component should know if there was a valid document signature, since 3792 // it should show a warning in this case 3793 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage())); 3794 uno::Reference< security::XDocumentDigitalSignatures > xSigner( 3795 security::DocumentDigitalSignatures::createWithVersionAndValidSignature( 3796 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) ); 3797 3798 uno::Reference< embed::XStorage > xWriteableZipStor; 3799 3800 // we can reuse the temporary file if there is one already 3801 CreateTempFile( false ); 3802 GetMedium_Impl(); 3803 3804 try 3805 { 3806 if ( !pImpl->xStream.is() ) 3807 throw uno::RuntimeException(); 3808 3809 bool bODF = GetFilter()->IsOwnFormat(); 3810 try 3811 { 3812 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream ); 3813 } 3814 catch (const io::IOException&) 3815 { 3816 if (bODF) 3817 { 3818 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage"); 3819 } 3820 } 3821 3822 if ( !xWriteableZipStor.is() && bODF ) 3823 throw uno::RuntimeException(); 3824 3825 uno::Reference< embed::XStorage > xMetaInf; 3826 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF")) 3827 { 3828 xMetaInf = xWriteableZipStor->openStorageElement( 3829 "META-INF", 3830 embed::ElementModes::READWRITE ); 3831 if ( !xMetaInf.is() ) 3832 throw uno::RuntimeException(); 3833 } 3834 3835 { 3836 if (xMetaInf.is()) 3837 { 3838 // ODF. 3839 uno::Reference< io::XStream > xStream; 3840 if (GetFilter() && GetFilter()->IsOwnFormat()) 3841 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW); 3842 3843 bool bSuccess = xSigner->signDocumentWithCertificate(xCertificate, GetZipStorageToSign_Impl(), xStream); 3844 3845 if (bSuccess) 3846 { 3847 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW ); 3848 xTransact->commit(); 3849 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW ); 3850 xTransact->commit(); 3851 3852 // the temporary file has been written, commit it to the original file 3853 Commit(); 3854 bChanges = true; 3855 } 3856 } 3857 else if (xWriteableZipStor.is()) 3858 { 3859 // OOXML. 3860 uno::Reference<io::XStream> xStream; 3861 3862 // We need read-write to be able to add the signature relation. 3863 bool bSuccess =xSigner->signDocumentWithCertificate( 3864 xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream); 3865 3866 if (bSuccess) 3867 { 3868 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW); 3869 xTransact->commit(); 3870 3871 // the temporary file has been written, commit it to the original file 3872 Commit(); 3873 bChanges = true; 3874 } 3875 } 3876 else 3877 { 3878 // Something not ZIP based: e.g. PDF. 3879 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE)); 3880 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream)); 3881 if (xSigner->signDocumentWithCertificate(xCertificate, uno::Reference<embed::XStorage>(), xStream)) 3882 bChanges = true; 3883 } 3884 } 3885 } 3886 catch ( const uno::Exception& ) 3887 { 3888 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" ); 3889 } 3890 3891 CloseAndRelease(); 3892 3893 ResetError(); 3894 3895 return bChanges; 3896 } 3897 3898 bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent, 3899 bool bSignScriptingContent, 3900 bool bHasValidDocumentSignature, 3901 const OUString& aSignatureLineId, 3902 const Reference<XCertificate>& xCert, 3903 const Reference<XGraphic>& xValidGraphic, 3904 const Reference<XGraphic>& xInvalidGraphic, 3905 const OUString& aComment) 3906 { 3907 bool bChanges = false; 3908 3909 if (IsOpen() || GetError()) 3910 { 3911 SAL_WARN("sfx.doc", "The medium must be closed by the signer!"); 3912 return bChanges; 3913 } 3914 3915 // The component should know if there was a valid document signature, since 3916 // it should show a warning in this case 3917 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage())); 3918 uno::Reference< security::XDocumentDigitalSignatures > xSigner( 3919 security::DocumentDigitalSignatures::createWithVersionAndValidSignature( 3920 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) ); 3921 if (pDialogParent) 3922 xSigner->setParentWindow(pDialogParent->GetXWindow()); 3923 3924 uno::Reference< embed::XStorage > xWriteableZipStor; 3925 3926 // we can reuse the temporary file if there is one already 3927 CreateTempFile( false ); 3928 GetMedium_Impl(); 3929 3930 try 3931 { 3932 if ( !pImpl->xStream.is() ) 3933 throw uno::RuntimeException(); 3934 3935 bool bODF = GetFilter()->IsOwnFormat(); 3936 try 3937 { 3938 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream ); 3939 } 3940 catch (const io::IOException&) 3941 { 3942 if (bODF) 3943 { 3944 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage"); 3945 } 3946 } 3947 3948 if ( !xWriteableZipStor.is() && bODF ) 3949 throw uno::RuntimeException(); 3950 3951 uno::Reference< embed::XStorage > xMetaInf; 3952 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF")) 3953 { 3954 xMetaInf = xWriteableZipStor->openStorageElement( 3955 "META-INF", 3956 embed::ElementModes::READWRITE ); 3957 if ( !xMetaInf.is() ) 3958 throw uno::RuntimeException(); 3959 } 3960 3961 if ( bSignScriptingContent ) 3962 { 3963 // If the signature has already the document signature it will be removed 3964 // after the scripting signature is inserted. 3965 uno::Reference< io::XStream > xStream( 3966 xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(), 3967 embed::ElementModes::READWRITE ), 3968 uno::UNO_SET_THROW ); 3969 3970 if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) ) 3971 { 3972 // remove the document signature if any 3973 OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName(); 3974 if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) ) 3975 xMetaInf->removeElement( aDocSigName ); 3976 3977 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW ); 3978 xTransact->commit(); 3979 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW ); 3980 xTransact->commit(); 3981 3982 // the temporary file has been written, commit it to the original file 3983 Commit(); 3984 bChanges = true; 3985 } 3986 } 3987 else 3988 { 3989 if (xMetaInf.is()) 3990 { 3991 // ODF. 3992 uno::Reference< io::XStream > xStream; 3993 if (GetFilter() && GetFilter()->IsOwnFormat()) 3994 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW); 3995 3996 bool bSuccess = false; 3997 if (xCert.is()) 3998 bSuccess = xSigner->signSignatureLine( 3999 GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert, 4000 xValidGraphic, xInvalidGraphic, aComment); 4001 else 4002 bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(), 4003 xStream); 4004 4005 if (bSuccess) 4006 { 4007 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW ); 4008 xTransact->commit(); 4009 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW ); 4010 xTransact->commit(); 4011 4012 // the temporary file has been written, commit it to the original file 4013 Commit(); 4014 bChanges = true; 4015 } 4016 } 4017 else if (xWriteableZipStor.is()) 4018 { 4019 // OOXML. 4020 uno::Reference<io::XStream> xStream; 4021 4022 bool bSuccess = false; 4023 if (xCert.is()) 4024 { 4025 bSuccess = xSigner->signSignatureLine( 4026 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId, 4027 xCert, xValidGraphic, xInvalidGraphic, aComment); 4028 } 4029 else 4030 { 4031 // We need read-write to be able to add the signature relation. 4032 bSuccess =xSigner->signDocumentContent( 4033 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream); 4034 } 4035 4036 if (bSuccess) 4037 { 4038 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW); 4039 xTransact->commit(); 4040 4041 // the temporary file has been written, commit it to the original file 4042 Commit(); 4043 bChanges = true; 4044 } 4045 } 4046 else 4047 { 4048 // Something not ZIP based: e.g. PDF. 4049 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE)); 4050 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream)); 4051 if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream)) 4052 bChanges = true; 4053 } 4054 } 4055 } 4056 catch ( const uno::Exception& ) 4057 { 4058 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" ); 4059 } 4060 4061 CloseAndRelease(); 4062 4063 ResetError(); 4064 4065 return bChanges; 4066 } 4067 4068 4069 SignatureState SfxMedium::GetCachedSignatureState_Impl() const 4070 { 4071 return pImpl->m_nSignatureState; 4072 } 4073 4074 4075 void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState ) 4076 { 4077 pImpl->m_nSignatureState = nState; 4078 } 4079 4080 void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects) 4081 { 4082 pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects; 4083 } 4084 4085 bool SfxMedium::HasStorage_Impl() const 4086 { 4087 return pImpl->xStorage.is(); 4088 } 4089 4090 bool SfxMedium::IsOpen() const 4091 { 4092 return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is(); 4093 } 4094 4095 OUString SfxMedium::CreateTempCopyWithExt( const OUString& aURL ) 4096 { 4097 OUString aResult; 4098 4099 if ( !aURL.isEmpty() ) 4100 { 4101 sal_Int32 nPrefixLen = aURL.lastIndexOf( '.' ); 4102 OUString aExt = ( nPrefixLen == -1 ) ? OUString() : aURL.copy( nPrefixLen ); 4103 4104 OUString aNewTempFileURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL(); 4105 if ( !aNewTempFileURL.isEmpty() ) 4106 { 4107 INetURLObject aSource( aURL ); 4108 INetURLObject aDest( aNewTempFileURL ); 4109 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, 4110 true, 4111 INetURLObject::DecodeMechanism::WithCharset ); 4112 if ( !aFileName.isEmpty() && aDest.removeSegment() ) 4113 { 4114 try 4115 { 4116 uno::Reference< css::ucb::XCommandEnvironment > xComEnv; 4117 ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() ); 4118 ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() ); 4119 aTargetContent.transferContent( aSourceContent, 4120 ::ucbhelper::InsertOperation::Copy, 4121 aFileName, 4122 NameClash::OVERWRITE ); 4123 aResult = aNewTempFileURL; 4124 } 4125 catch( const uno::Exception& ) 4126 {} 4127 } 4128 } 4129 } 4130 4131 return aResult; 4132 } 4133 4134 bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort) 4135 { 4136 bool bResult = false; 4137 4138 if ( xHandler.is() ) 4139 { 4140 try 4141 { 4142 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 ); 4143 4144 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove ); 4145 aContinuations[ 0 ] = pApprove.get(); 4146 4147 if ( bAllowAbort ) 4148 { 4149 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort ); 4150 aContinuations[ 1 ] = pAbort.get(); 4151 } 4152 4153 xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations)); 4154 bResult = pApprove->wasSelected(); 4155 } 4156 catch( const Exception& ) 4157 { 4158 } 4159 } 4160 4161 return bResult; 4162 } 4163 4164 OUString SfxMedium::SwitchDocumentToTempFile() 4165 { 4166 // the method returns empty string in case of failure 4167 OUString aResult; 4168 OUString aOrigURL = pImpl->m_aLogicName; 4169 4170 if ( !aOrigURL.isEmpty() ) 4171 { 4172 sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' ); 4173 OUString const aExt = (nPrefixLen == -1) 4174 ? OUString() 4175 : aOrigURL.copy(nPrefixLen); 4176 OUString aNewURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL(); 4177 4178 // TODO/LATER: In future the aLogicName should be set to shared folder URL 4179 // and a temporary file should be created. Transport_Impl should be impossible then. 4180 if ( !aNewURL.isEmpty() ) 4181 { 4182 uno::Reference< embed::XStorage > xStorage = GetStorage(); 4183 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY ); 4184 4185 if ( xOptStorage.is() ) 4186 { 4187 // TODO/LATER: reuse the pImpl->pTempFile if it already exists 4188 CanDisposeStorage_Impl( false ); 4189 Close(); 4190 SetPhysicalName_Impl( OUString() ); 4191 SetName( aNewURL ); 4192 4193 // remove the readonly state 4194 bool bWasReadonly = false; 4195 pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE; 4196 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false); 4197 if ( pReadOnlyItem && pReadOnlyItem->GetValue() ) 4198 bWasReadonly = true; 4199 GetItemSet()->ClearItem( SID_DOC_READONLY ); 4200 4201 GetMedium_Impl(); 4202 LockOrigFileOnDemand( false, false ); 4203 CreateTempFile(); 4204 GetMedium_Impl(); 4205 4206 if ( pImpl->xStream.is() ) 4207 { 4208 try 4209 { 4210 xOptStorage->writeAndAttachToStream( pImpl->xStream ); 4211 pImpl->xStorage = xStorage; 4212 aResult = aNewURL; 4213 } 4214 catch( const uno::Exception& ) 4215 {} 4216 } 4217 4218 if ( aResult.isEmpty() ) 4219 { 4220 Close(); 4221 SetPhysicalName_Impl( OUString() ); 4222 SetName( aOrigURL ); 4223 if ( bWasReadonly ) 4224 { 4225 // set the readonly state back 4226 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY; 4227 GetItemSet()->Put( SfxBoolItem(SID_DOC_READONLY, true)); 4228 } 4229 GetMedium_Impl(); 4230 pImpl->xStorage = xStorage; 4231 } 4232 } 4233 } 4234 } 4235 4236 return aResult; 4237 } 4238 4239 bool SfxMedium::SwitchDocumentToFile( const OUString& aURL ) 4240 { 4241 // the method is only for storage based documents 4242 bool bResult = false; 4243 OUString aOrigURL = pImpl->m_aLogicName; 4244 4245 if ( !aURL.isEmpty() && !aOrigURL.isEmpty() ) 4246 { 4247 uno::Reference< embed::XStorage > xStorage = GetStorage(); 4248 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY ); 4249 4250 // TODO/LATER: reuse the pImpl->pTempFile if it already exists 4251 CanDisposeStorage_Impl( false ); 4252 Close(); 4253 SetPhysicalName_Impl( OUString() ); 4254 SetName( aURL ); 4255 4256 // open the temporary file based document 4257 GetMedium_Impl(); 4258 LockOrigFileOnDemand( false, false ); 4259 CreateTempFile(); 4260 GetMedium_Impl(); 4261 4262 if ( pImpl->xStream.is() ) 4263 { 4264 try 4265 { 4266 uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW ); 4267 xTruncate->truncate(); 4268 if ( xOptStorage.is() ) 4269 xOptStorage->writeAndAttachToStream( pImpl->xStream ); 4270 pImpl->xStorage = xStorage; 4271 bResult = true; 4272 } 4273 catch( const uno::Exception& ) 4274 {} 4275 } 4276 4277 if ( !bResult ) 4278 { 4279 Close(); 4280 SetPhysicalName_Impl( OUString() ); 4281 SetName( aOrigURL ); 4282 GetMedium_Impl(); 4283 pImpl->xStorage = xStorage; 4284 } 4285 } 4286 4287 return bResult; 4288 } 4289 4290 void SfxMedium::SetInCheckIn( bool bInCheckIn ) 4291 { 4292 pImpl->m_bInCheckIn = bInCheckIn; 4293 } 4294 4295 bool SfxMedium::IsInCheckIn( ) const 4296 { 4297 return pImpl->m_bInCheckIn; 4298 } 4299 4300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 4301
