summaryrefslogtreecommitdiff
path: root/sfx2
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2018-12-04 10:00:51 +0300
committerAndras Timar <andras.timar@collabora.com>2018-12-29 20:14:46 +0100
commit6365cb0e5e1316bf0b27d22952ec63d72ef7bf5b (patch)
treee284b8f029d88ae8d9693d3b7225470056f37587 /sfx2
parentfb4dd9667551a0ee162584e1cf1c10fd3451ea0b (diff)
tdf#34171: check foreign lockfiles to tell who has locked document
MS Office (Word/Excel/PowerPoint) lockfiles are supported now. Note that Excel does *not* create lockfiles for pre-OOXML files. This changes osl_openFile implementation on Windows, to treat osl_File_OpenFlag_NoLock to also include FILE_SHARE_DELETE flag for CreateFileW. This is required to allow opening files created with FILE_FLAG_DELETE_ON_CLOSE flag, such as Excel's owner files. The shange should be consistent with the overall meaning of the osl_File_OpenFlag_NoLock to not impose any locking constraints to the file being opened. Change-Id: I7b99012f4bd60ab3821fb91d5166a286031b7e93 Reviewed-on: https://gerrit.libreoffice.org/64496 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com> (cherry picked from commit 607b80ca3cddc239a35580470944a438ce144fc8) Reviewed-on: https://gerrit.libreoffice.org/65085 Tested-by: Mike Kaganski <mike.kaganski@collabora.com> Reviewed-by: Andras Timar <andras.timar@collabora.com>
Diffstat (limited to 'sfx2')
-rw-r--r--sfx2/source/doc/docfile.cxx108
1 files changed, 105 insertions, 3 deletions
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index 97431ff3ed10..e8efa9e672a7 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -833,8 +833,104 @@ void SfxMedium::SetEncryptionDataToStorage_Impl()
// that. Clearly the knowledge whether lock files should be used or
// not for some URL scheme belongs in UCB, not here.
+namespace
+{
+OUString tryMSOwnerFile(const INetURLObject& aLockfileURL)
+{
+ try
+ {
+ static osl::Mutex aMutex;
+ osl::MutexGuard aGuard(aMutex);
+ css::uno::Reference<css::ucb::XCommandEnvironment> xEnv;
+ ucbhelper::Content aSourceContent(
+ aLockfileURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), xEnv,
+ comphelper::getProcessComponentContext());
+
+ // Excel creates Owner Files with FILE_FLAG_DELETE_ON_CLOSE, so we need to open it with
+ // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE share mode
+ css::uno::Reference<css::io::XInputStream> xStream = aSourceContent.openStreamNoLock();
+ if (!xStream)
+ return OUString();
+
+ const sal_Int32 nBufLen = 256;
+ css::uno::Sequence<sal_Int8> aBuf(nBufLen);
+ const sal_Int32 nRead = xStream->readBytes(aBuf, nBufLen);
+ xStream->closeInput();
+ if (nRead >= 162)
+ {
+ // Reverse engineering of MS Office Owner Files format (MS Office 2016 tested).
+ // It starts with a single byte with name length, after which characters of username go
+ // in current Windows 8-bit codepage.
+ // For Word lockfiles, the name is followed by zero bytes up to position 54.
+ // For PowerPoint lockfiles, the name is followed by a single zero byte, and then 0x20
+ // bytes up to position 55.
+ // For Excel lockfiles, the name is followed by 0x20 bytes up to position 55.
+ // At those positions in each type of lockfile, a name length 2-byte word goes, followed
+ // by UTF-16-LE-encoded copy of username. Spaces or some garbage follow up to the end of
+ // the lockfile (total 162 bytes for Word, 165 bytes for Excel/PowerPoint).
+ // Apparently MS Office does not allow username to be longer than 52 characters (trying
+ // to enter more in its options dialog results in error messages stating this limit).
+ const int nACPLen = aBuf[0];
+ if (nACPLen > 0 && nACPLen <= 52) // skip wrong format
+ {
+ const sal_Int8* pBuf = aBuf.getConstArray() + 54;
+ int nUTF16Len = *pBuf; // try Word position
+ // If UTF-16 length is 0x20, then ACP length is also less than maximal, which means
+ // that in Word lockfile case, at least two preceeding bytes would be zero. Both
+ // Excel and PowerPoint lockfiles would have at least one of those bytes non-zero.
+ if (nUTF16Len == 0x20 && (*(pBuf - 1) != 0 || *(pBuf - 2) != 0))
+ nUTF16Len = *++pBuf; // use Excel/PowerPoint position
+
+ if (nUTF16Len > 0 && nUTF16Len <= 52) // skip wrong format
+ return OUString(reinterpret_cast<const sal_Unicode*>(pBuf + 2), nUTF16Len);
+ }
+ }
+ }
+ catch (...) {} // we don't ever need to care about any exceptions here
+
+ return OUString();
+}
-SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEntry& aData, bool bIsLoading, bool bOwnLock, bool bHandleSysLocked )
+OUString tryMSOwnerFiles(const OUString& sDocURL)
+{
+ INetURLObject aURL(sDocURL);
+ if (aURL.HasError())
+ return OUString();
+ const OUString sFileName = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ if (sFileName.isEmpty())
+ return OUString();
+ const OUString sFileExt = aURL.GetFileExtension();
+ const sal_Int32 nFileNameLen
+ = sFileName.getLength() - sFileExt.getLength() - (sFileExt.isEmpty() ? 0 : 1);
+ // Word, Excel, PowerPoint all prepend the filename with "~$".
+ aURL.SetName("~$" + sFileName, INetURLObject::EncodeMechanism::All);
+ OUString sUserData = tryMSOwnerFile(aURL);
+ // Additionally, Word strips first chars of the filename: 1 for length 7, 2 for length >=8.
+ if (sUserData.isEmpty() && nFileNameLen > 6)
+ {
+ aURL.SetName("~$" + sFileName.copy((nFileNameLen == 7) ? 1 : 2),
+ INetURLObject::EncodeMechanism::All);
+ sUserData = tryMSOwnerFile(aURL);
+ }
+
+ if (!sUserData.isEmpty())
+ sUserData += " (MS Office)"; // Mention the used office suite
+
+ return sUserData;
+}
+
+OUString tryForeignLockfiles(const OUString& sDocURL)
+{
+ OUString sUserData = tryMSOwnerFiles(sDocURL);
+ // here we can test for empty result, and add other known applications' lockfile testing
+ return sUserData.trim();
+}
+}
+
+SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const OUString& aDocURL,
+ const LockFileEntry& aData,
+ bool bIsLoading, bool bOwnLock,
+ bool bHandleSysLocked)
{
ShowLockResult nResult = ShowLockResult::NoLock;
@@ -872,6 +968,10 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEnt
else
aInfo = aData[LockFileComponent::SYSUSERNAME];
+ if (aInfo.isEmpty() && !aDocURL.isEmpty())
+ // Try to get name of user who has locked the file using other applications
+ aInfo = tryForeignLockfiles(aDocURL);
+
if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
@@ -1101,7 +1201,7 @@ SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand( bool bLoading, bool b
if ( !bResult && !bNoUI )
{
- bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false , true );
+ bUIStatus = ShowLockedDocumentDialog("", aLockData, bLoading, false , true );
}
}
catch( ucb::InteractiveNetworkWriteException& )
@@ -1323,7 +1423,9 @@ SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand( bool bLoading, bool b
if ( !bResult && !bIoErr)
{
if (!bNoUI)
- bUIStatus = ShowLockedDocumentDialog(aData, bLoading, bOwnLock, bHandleSysLocked);
+ bUIStatus = ShowLockedDocumentDialog(
+ pImpl->m_aLogicName, aData, bLoading, bOwnLock,
+ bHandleSysLocked);
else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
bUIStatus = ShowLockResult::Succeeded;