[Libreoffice-commits] core.git: include/sfx2 sal/osl sfx2/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Tue Dec 4 10:31:43 UTC 2018


 include/sfx2/docfile.hxx    |    3 -
 sal/osl/w32/file.cxx        |    2 
 sfx2/source/doc/docfile.cxx |  108 ++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 108 insertions(+), 5 deletions(-)

New commits:
commit 607b80ca3cddc239a35580470944a438ce144fc8
Author:     Mike Kaganski <mike.kaganski at collabora.com>
AuthorDate: Tue Dec 4 10:00:51 2018 +0300
Commit:     Mike Kaganski <mike.kaganski at collabora.com>
CommitDate: Tue Dec 4 11:31:21 2018 +0100

    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 at collabora.com>

diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index 7022d51743d9..2f7d9454c1e5 100644
--- a/include/sfx2/docfile.hxx
+++ b/include/sfx2/docfile.hxx
@@ -295,7 +295,8 @@ public:
 
 private:
     enum class ShowLockResult { NoLock, Succeeded, Try };
-    ShowLockResult      ShowLockedDocumentDialog(const LockFileEntry& aData, bool bIsLoading, bool bOwnLock, bool bHandleSysLocked);
+    ShowLockResult ShowLockedDocumentDialog(const OUString& aDocURL, const LockFileEntry& aData,
+                                            bool bIsLoading, bool bOwnLock, bool bHandleSysLocked);
     enum class MessageDlg { LockFileIgnore, LockFileCorrupt };
     bool                ShowLockFileProblemDialog(MessageDlg nWhichDlg);
 
diff --git a/sal/osl/w32/file.cxx b/sal/osl/w32/file.cxx
index 1bc898eba4e4..0c3f3d9621fe 100644
--- a/sal/osl/w32/file.cxx
+++ b/sal/osl/w32/file.cxx
@@ -653,7 +653,7 @@ oslFileError SAL_CALL osl_openFile(
         dwShare  |= FILE_SHARE_WRITE;
 
     if (uFlags & osl_File_OpenFlag_NoLock)
-        dwShare  |= FILE_SHARE_WRITE;
+        dwShare  |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
 
     if (uFlags & osl_File_OpenFlag_Create)
         dwCreation |= CREATE_NEW;
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index 4fc08b20e74d..00bb49a24c5f 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -892,8 +892,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();
 
-SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEntry& aData, bool bIsLoading, bool bOwnLock, bool bHandleSysLocked )
+        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();
+}
+
+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;
 
@@ -931,6 +1027,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] + " )";
 
@@ -1164,7 +1264,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& )
@@ -1386,7 +1486,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;
 


More information about the Libreoffice-commits mailing list