[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.0' - include/svl sfx2/source svl/CppunitTest_svl_lockfiles.mk svl/Library_svl.mk svl/Module_svl.mk svl/qa svl/source

Tamás Zolnai (via logerrit) logerrit at kemper.freedesktop.org
Wed Mar 27 20:48:10 UTC 2019


 include/svl/documentlockfile.hxx         |   39 +
 include/svl/lockfilecommon.hxx           |   21 
 include/svl/msodocumentlockfile.hxx      |   82 +++
 include/svl/sharecontrolfile.hxx         |    2 
 sfx2/source/doc/docfile.cxx              |    2 
 svl/CppunitTest_svl_lockfiles.mk         |   52 ++
 svl/Library_svl.mk                       |    3 
 svl/Module_svl.mk                        |    1 
 svl/qa/unit/lockfiles/test_lockfiles.cxx |  732 +++++++++++++++++++++++++++++++
 svl/source/misc/documentlockfile.cxx     |  140 +++--
 svl/source/misc/lockfilecommon.cxx       |   35 +
 svl/source/misc/msodocumentlockfile.cxx  |  244 ++++++++++
 svl/source/misc/sharecontrolfile.cxx     |    6 
 13 files changed, 1264 insertions(+), 95 deletions(-)

New commits:
commit a2304edb5528700306a35f58607ac9a1753befdc
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Sat Mar 23 15:53:27 2019 +0100
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Wed Mar 27 21:47:44 2019 +0100

    Introduce new lockfile handler for MSO like lockfiles
    
    * Implement writing of MSO lockfiles
    * Grab the already implemented parsing code (tryMSOwnerFile method)
    and put it together into one class
    * Add tests about the generated URL for lockfiles and the lockfile content
    * MSO lockfiles are not written yet by LO, next step is to integrate
    this code into the locking mechanism.
    
    Reviewed-on: https://gerrit.libreoffice.org/69582
    Tested-by: Jenkins
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>
    (cherry picked from commit 5db1e20b8b0942dac2d50f3cd34532bb61147020)
    
    Change-Id: I3b0ed1975cd57dfd006d4e1890b23c307890de5c
    Reviewed-on: https://gerrit.libreoffice.org/69842
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>

diff --git a/include/svl/documentlockfile.hxx b/include/svl/documentlockfile.hxx
index ec94f1f22dde..77b1075ba6ea 100644
--- a/include/svl/documentlockfile.hxx
+++ b/include/svl/documentlockfile.hxx
@@ -32,28 +32,41 @@
 
 namespace svt {
 
-class SVL_DLLPUBLIC DocumentLockFile : public LockFileCommon
+/// Generalized class for LO and MSO lockfile handling.
+class SVL_DLLPUBLIC GenDocumentLockFile : public LockFileCommon
 {
-    // the workaround for automated testing!
-    static bool m_bAllowInteraction;
-
-    css::uno::Reference< css::io::XInputStream > OpenStream();
-
-    void WriteEntryToStream( const LockFileEntry& aEntry, const css::uno::Reference< css::io::XOutputStream >& xStream );
-
 public:
-    DocumentLockFile( const OUString& aOrigURL );
-    ~DocumentLockFile();
+    /// Specify the lockfile URL directly
+    GenDocumentLockFile( const OUString& aURL );
+    /// Let the object generate and own URL based on the original file's URL and a prefix
+    GenDocumentLockFile( const OUString& aOrigURL, const OUString& aPrefix );
+    virtual ~GenDocumentLockFile() override;
 
     bool CreateOwnLockFile();
-    LockFileEntry GetLockData();
     bool OverwriteOwnLockFile();
     /// Delete the Lockfile, if current user is the owner
-    void RemoveFile();
+    virtual void RemoveFile();
     /// Only delete lockfile, disregarding ownership
     void RemoveFileDirectly();
 
-    static bool IsInteractionAllowed() { return m_bAllowInteraction; }
+    virtual LockFileEntry GetLockData() = 0;
+
+protected:
+    virtual void WriteEntryToStream( const LockFileEntry& aEntry, const css::uno::Reference< css::io::XOutputStream >& xStream ) = 0;
+    css::uno::Reference< css::io::XInputStream > OpenStream();
+};
+
+/// Class implementing reading and writing LO lockfiles.
+class SVL_DLLPUBLIC DocumentLockFile : public GenDocumentLockFile
+{
+protected:
+    virtual void WriteEntryToStream( const LockFileEntry& aEntry, const css::uno::Reference< css::io::XOutputStream >& xStream ) override;
+
+public:
+    DocumentLockFile( const OUString& aOrigURL );
+    virtual ~DocumentLockFile() override;
+
+    virtual LockFileEntry GetLockData() override;
 };
 
 }
diff --git a/include/svl/lockfilecommon.hxx b/include/svl/lockfilecommon.hxx
index ee4674840029..f66fb135139c 100644
--- a/include/svl/lockfilecommon.hxx
+++ b/include/svl/lockfilecommon.hxx
@@ -42,18 +42,27 @@ typedef o3tl::enumarray<LockFileComponent,OUString> LockFileEntry;
 
 namespace svt {
 
-// This is a general implementation that is used in document lock file implementation and in sharing control file implementation
+/// This is a general implementation that is used in document lock file implementation and in sharing control file implementation
 class SVL_DLLPUBLIC LockFileCommon
 {
-protected:
-    ::osl::Mutex m_aMutex;
+private:
     OUString m_aURL;
 
-    INetURLObject ResolveLinks( const INetURLObject& aDocURL ) const;
+protected:
+    ::osl::Mutex m_aMutex;
 
 public:
+    /// Specify the lockfile URL directly
+    LockFileCommon( const OUString& aURL );
+    /// Let the object generate and own URL based on the original file's URL and a prefix
     LockFileCommon( const OUString& aOrigURL, const OUString& aPrefix );
-    ~LockFileCommon();
+    virtual ~LockFileCommon();
+
+    const OUString& GetURL() const;
+    void SetURL(const OUString& aURL);
+
+    /// This method generates the URL of the lock file based on the document URL and the specified prefix.
+    virtual OUString GenerateURL( const OUString& aOrigURL, const OUString& aPrefix );
 
     static void ParseList( const css::uno::Sequence< sal_Int8 >& aBuffer, std::vector< LockFileEntry > &rOutput );
     static LockFileEntry ParseEntry( const css::uno::Sequence< sal_Int8 >& aBuffer, sal_Int32& o_nCurPos );
@@ -62,6 +71,8 @@ public:
     static OUString GetOOOUserName();
     static OUString GetCurrentLocalTime();
     static LockFileEntry GenerateOwnEntry();
+
+    INetURLObject ResolveLinks( const INetURLObject& aDocURL ) const;
 };
 
 }
diff --git a/include/svl/msodocumentlockfile.hxx b/include/svl/msodocumentlockfile.hxx
new file mode 100644
index 000000000000..75f03ff16d4b
--- /dev/null
+++ b/include/svl/msodocumentlockfile.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SVL_MSODOCUMENTLOCKFILE_HXX
+#define INCLUDED_SVL_MSODOCUMENTLOCKFILE_HXX
+
+#include <svl/svldllapi.h>
+#include <svl/lockfilecommon.hxx>
+#include <svl/documentlockfile.hxx>
+
+#include <com/sun/star/lang/XComponent.hpp>
+
+namespace com
+{
+namespace sun
+{
+namespace star
+{
+namespace io
+{
+class XInputStream;
+}
+} // namespace star
+} // namespace sun
+} // namespace com
+namespace com
+{
+namespace sun
+{
+namespace star
+{
+namespace io
+{
+class XOutputStream;
+}
+} // namespace star
+} // namespace sun
+} // namespace com
+
+#define MSO_WORD_LOCKFILE_SIZE 162
+#define MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE 165
+#define MSO_USERNAME_MAX_LENGTH 52
+
+namespace svt
+{
+/// Class implementing reading and writing MSO lockfiles.
+class SVL_DLLPUBLIC MSODocumentLockFile : public GenDocumentLockFile
+{
+private:
+    OUString m_sOrigURL;
+
+    bool isWordFormat(const OUString& aOrigURL) const;
+    bool isExcelFormat(const OUString& aOrigURL) const;
+    bool isPowerPointFormat(const OUString& aOrigURL) const;
+
+protected:
+    virtual void
+    WriteEntryToStream(const LockFileEntry& aEntry,
+                       const css::uno::Reference<css::io::XOutputStream>& xStream) override;
+
+public:
+    MSODocumentLockFile(const OUString& aOrigURL);
+    virtual ~MSODocumentLockFile() override;
+
+    /// Need to generate different lock file name for MSO.
+    virtual OUString GenerateURL(const OUString& aOrigURL, const OUString& aPrefix) override;
+
+    virtual LockFileEntry GetLockData() override;
+
+    virtual void RemoveFile() override;
+};
+} // namespace svt
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/svl/sharecontrolfile.hxx b/include/svl/sharecontrolfile.hxx
index 60f478651342..3a2e90ef48d2 100644
--- a/include/svl/sharecontrolfile.hxx
+++ b/include/svl/sharecontrolfile.hxx
@@ -54,7 +54,7 @@ public:
 
     // The constructor will throw exception in case the stream can not be opened
     ShareControlFile( const OUString& aOrigURL );
-    ~ShareControlFile();
+    virtual ~ShareControlFile() override;
 
     std::vector< LockFileEntry > GetUsersData();
     void SetUsersDataAndStore( const std::vector< LockFileEntry >& aUserNames );
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index e8efa9e672a7..44c8a08a8293 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -941,7 +941,7 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const OUString& aD
     // show the interaction regarding the document opening
     uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
 
-    if ( ::svt::DocumentLockFile::IsInteractionAllowed() && xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
+    if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
     {
         OUString aDocumentURL = GetURLObject().GetLastName();
         OUString aInfo;
diff --git a/svl/CppunitTest_svl_lockfiles.mk b/svl/CppunitTest_svl_lockfiles.mk
new file mode 100644
index 000000000000..c93e78b45b8d
--- /dev/null
+++ b/svl/CppunitTest_svl_lockfiles.mk
@@ -0,0 +1,52 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,svl_lockfiles))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,svl_lockfiles))
+
+$(eval $(call gb_CppunitTest_use_api,svl_lockfiles,\
+    udkapi \
+    offapi \
+    oovbaapi \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,svl_lockfiles))
+
+$(eval $(call gb_CppunitTest_use_vcl,svl_lockfiles))
+
+
+$(eval $(call gb_CppunitTest_add_exception_objects,svl_lockfiles, \
+        svl/qa/unit/lockfiles/test_lockfiles \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,svl_lockfiles, \
+    comphelper \
+    cppu \
+    cppuhelper \
+    tl \
+    sal \
+    svl \
+    svt \
+    sw \
+    test \
+    unotest \
+    utl \
+    vcl \
+))
+
+$(eval $(call gb_CppunitTest_use_rdb,svl_lockfiles,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,svl_lockfiles,\
+        officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,svl_lockfiles))
+
+# vim: set noet sw=4 ts=4:
diff --git a/svl/Library_svl.mk b/svl/Library_svl.mk
index de981cdc9315..68429ced7581 100644
--- a/svl/Library_svl.mk
+++ b/svl/Library_svl.mk
@@ -158,7 +158,8 @@ $(eval $(call gb_Library_add_exception_objects,svl,\
     svl/source/misc/PasswordHelper \
     svl/source/misc/adrparse \
     $(if $(filter DESKTOP,$(BUILD_TYPE)),\
-        svl/source/misc/documentlockfile) \
+        svl/source/misc/documentlockfile \
+        svl/source/misc/msodocumentlockfile) \
     svl/source/misc/filenotation \
     svl/source/misc/fstathelper \
     svl/source/misc/getstringresource \
diff --git a/svl/Module_svl.mk b/svl/Module_svl.mk
index d14e184b65a9..2569edb05b64 100644
--- a/svl/Module_svl.mk
+++ b/svl/Module_svl.mk
@@ -34,6 +34,7 @@ $(eval $(call gb_Module_add_check_targets,svl,\
 	CppunitTest_svl_itempool \
 	CppunitTest_svl_items \
 	CppunitTest_svl_lngmisc \
+	CppunitTest_svl_lockfiles \
 	CppunitTest_svl_notify \
 	CppunitTest_svl_qa_cppunit \
 	CppunitTest_svl_urihelper \
diff --git a/svl/qa/unit/lockfiles/test_lockfiles.cxx b/svl/qa/unit/lockfiles/test_lockfiles.cxx
new file mode 100644
index 000000000000..8083c143f461
--- /dev/null
+++ b/svl/qa/unit/lockfiles/test_lockfiles.cxx
@@ -0,0 +1,732 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <test/bootstrapfixture.hxx>
+
+#include <unotest/directories.hxx>
+#include <svl/lockfilecommon.hxx>
+#include <svl/documentlockfile.hxx>
+#include <svl/msodocumentlockfile.hxx>
+#include <unotools/useroptions.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/stream.hxx>
+#include <rtl/strbuf.hxx>
+#include <osl/security.hxx>
+#include <osl/socket.hxx>
+#include <unotools/bootstrap.hxx>
+
+namespace
+{
+class LockfileTest : public test::BootstrapFixture
+{
+public:
+    void testLOLockFileURL();
+    void testLOLockFileContent();
+    void testLOLockFileRT();
+    void testLOLockFileUnicodeUsername();
+    void testLOLockFileOverwrite();
+    void testWordLockFileURL();
+    void testExcelLockFileURL();
+    void testPowerPointLockFileURL();
+    void testWordLockFileContent();
+    void testExcelLockFileContent();
+    void testPowerPointLockFileContent();
+    void testWordLockFileRT();
+    void testExcelLockFileRT();
+    void testPowerPointLockFileRT();
+    void testMSOLockFileLongUserName();
+    void testMSOLockFileUnicodeUsername();
+    void testMSOLockFileOverwrite();
+
+private:
+    CPPUNIT_TEST_SUITE(LockfileTest);
+    CPPUNIT_TEST(testLOLockFileURL);
+    CPPUNIT_TEST(testLOLockFileContent);
+    CPPUNIT_TEST(testLOLockFileRT);
+    CPPUNIT_TEST(testLOLockFileUnicodeUsername);
+    CPPUNIT_TEST(testLOLockFileOverwrite);
+    CPPUNIT_TEST(testWordLockFileURL);
+    CPPUNIT_TEST(testExcelLockFileURL);
+    CPPUNIT_TEST(testPowerPointLockFileURL);
+    CPPUNIT_TEST(testWordLockFileContent);
+    CPPUNIT_TEST(testExcelLockFileContent);
+    CPPUNIT_TEST(testPowerPointLockFileContent);
+    CPPUNIT_TEST(testWordLockFileRT);
+    CPPUNIT_TEST(testExcelLockFileRT);
+    CPPUNIT_TEST(testPowerPointLockFileRT);
+    CPPUNIT_TEST(testMSOLockFileLongUserName);
+    CPPUNIT_TEST(testMSOLockFileUnicodeUsername);
+    CPPUNIT_TEST(testMSOLockFileOverwrite);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+OUString readLockFile(const OUString& aSource)
+{
+    SvFileStream aFileStream(aSource, StreamMode::READ);
+    std::size_t nSize = aFileStream.remainingSize();
+    std::unique_ptr<sal_Int8[]> pBuffer(new sal_Int8[nSize]);
+    aFileStream.ReadBytes(pBuffer.get(), nSize);
+
+    css::uno::Sequence<sal_Int8> aData(pBuffer.get(), nSize);
+    OStringBuffer aResult;
+    for (sal_Int8 nByte : aData)
+    {
+        aResult.append(static_cast<sal_Char>(nByte));
+    }
+    return OStringToOUString(aResult.makeStringAndClear(), RTL_TEXTENCODING_UTF8);
+}
+
+OUString GetLockFileName(const svt::GenDocumentLockFile& rLockFile)
+{
+    INetURLObject aDocURL = rLockFile.ResolveLinks(INetURLObject(rLockFile.GetURL()));
+    return aDocURL.GetName();
+}
+
+void LockfileTest::testLOLockFileURL()
+{
+    // Test the generated file name for LibreOffice lock files
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testLOLockFileURL.odt");
+
+    svt::DocumentLockFile aLockFile(aTestODT);
+    CPPUNIT_ASSERT_EQUAL(OUString(".~lock.testLOLockFileURL.odt%23"), GetLockFileName(aLockFile));
+}
+
+void LockfileTest::testLOLockFileContent()
+{
+    // Test the lockfile generated for the specified ODT document
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testLOLockFileContent.odt");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and check the content
+    svt::DocumentLockFile aLockFile(aTestODT);
+    aLockFile.CreateOwnLockFile();
+    OUString sLockFileContent(readLockFile(aLockFile.GetURL()));
+    aLockFile.RemoveFileDirectly();
+
+    // User name
+    sal_Int32 nFirstChar = 0;
+    sal_Int32 nNextComma = sLockFileContent.indexOf(',', nFirstChar);
+    OUString sUserName;
+    sUserName += aUserOpt.GetFirstName() + " ";
+    sUserName += aUserOpt.GetLastName();
+    CPPUNIT_ASSERT_EQUAL(sUserName, sLockFileContent.copy(nFirstChar, nNextComma - nFirstChar));
+
+    // System user name
+    nFirstChar = nNextComma + 1;
+    nNextComma = sLockFileContent.indexOf(',', nFirstChar);
+    ::osl::Security aSecurity;
+    OUString sSysUserName;
+    aSecurity.getUserName(sSysUserName);
+    CPPUNIT_ASSERT_EQUAL(sSysUserName, sLockFileContent.copy(nFirstChar, nNextComma - nFirstChar));
+
+    // Local host
+    nFirstChar = nNextComma + 1;
+    nNextComma = sLockFileContent.indexOf(',', nFirstChar);
+    CPPUNIT_ASSERT_EQUAL(::osl::SocketAddr::getLocalHostname(),
+                         sLockFileContent.copy(nFirstChar, nNextComma - nFirstChar));
+
+    // Skip date and time because it changes after the lock file was created
+    nFirstChar = nNextComma + 1;
+    nNextComma = sLockFileContent.indexOf(',', nFirstChar);
+
+    // user url
+    nFirstChar = nNextComma + 1;
+    OUString aUserInstDir;
+    ::utl::Bootstrap::locateUserInstallation(aUserInstDir);
+    CPPUNIT_ASSERT_EQUAL(
+        aUserInstDir,
+        sLockFileContent.copy(nFirstChar, sLockFileContent.getLength() - nFirstChar - 1));
+}
+
+void LockfileTest::testLOLockFileRT()
+{
+    // Test the lockfile generated for the specified ODT document
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testLOLockFileRT.odt");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and read it back
+    svt::DocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::SYSUSERNAME],
+                         aRTEntry[LockFileComponent::SYSUSERNAME]);
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::LOCALHOST],
+                         aRTEntry[LockFileComponent::LOCALHOST]);
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::USERURL],
+                         aRTEntry[LockFileComponent::USERURL]);
+    // LockFileComponent::EDITTIME can change
+
+    aLockFile.RemoveFileDirectly();
+}
+
+void LockfileTest::testLOLockFileUnicodeUsername()
+{
+    // Test the lockfile generated for the specified ODT document
+    OUString aTestODT = m_directories.getURLFromSrc(
+        "/svl/qa/unit/lockfiles/data/testLOLockFileUnicodeUsername.odt");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    sal_Unicode vFirstName[] = { 2351, 2676, 3117, 5279 };
+    aUserOpt.SetToken(UserOptToken::FirstName, OUString(vFirstName, 4));
+    sal_Unicode vLastName[] = { 671, 1245, 1422, 1822 };
+    aUserOpt.SetToken(UserOptToken::LastName, OUString(vLastName, 4));
+
+    // Write the lock file and read it back
+    svt::DocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+    CPPUNIT_ASSERT_EQUAL(OUString(aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName()),
+                         aOrigEntry[LockFileComponent::OOOUSERNAME]);
+
+    aLockFile.RemoveFileDirectly();
+}
+
+void LockfileTest::testLOLockFileOverwrite()
+{
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testLOLockFileOverwrite.odt");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and read it back
+    svt::DocumentLockFile aLockFile(aTestODT);
+    aLockFile.CreateOwnLockFile();
+
+    // Change user name
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile2");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Overwrite lockfile
+    svt::DocumentLockFile aLockFile2(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile2.OverwriteOwnLockFile();
+
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::SYSUSERNAME],
+                         aRTEntry[LockFileComponent::SYSUSERNAME]);
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::LOCALHOST],
+                         aRTEntry[LockFileComponent::LOCALHOST]);
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::USERURL],
+                         aRTEntry[LockFileComponent::USERURL]);
+
+    aLockFile2.RemoveFileDirectly();
+}
+
+void LockfileTest::testWordLockFileURL()
+{
+    // Test the generated file name for Word lock files
+
+    // Word specific file format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testWordLockFileURL.docx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$stWordLockFileURL.docx"), GetLockFileName(aLockFile));
+    }
+
+    // Eight character file name (cuts two characters)
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.docx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$345678.docx"), GetLockFileName(aLockFile));
+    }
+
+    // Seven character file name (cuts one character)
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/1234567.docx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$234567.docx"), GetLockFileName(aLockFile));
+    }
+
+    // Six character file name (cuts no character)
+    {
+        OUString aTestFile = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/123456.docx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$123456.docx"), GetLockFileName(aLockFile));
+    }
+
+    // One character file name
+    {
+        OUString aTestFile = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/1.docx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$1.docx"), GetLockFileName(aLockFile));
+    }
+
+    // Test for ODT format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.odt");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$345678.odt"), GetLockFileName(aLockFile));
+    }
+
+    // Test for DOC format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.doc");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$345678.doc"), GetLockFileName(aLockFile));
+    }
+
+    // Test for RTF format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.rtf");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$345678.rtf"), GetLockFileName(aLockFile));
+    }
+}
+
+void LockfileTest::testExcelLockFileURL()
+{
+    // Test the generated file name for Excel lock files
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testExcelLockFileURL.xlsx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$testExcelLockFileURL.xlsx"), GetLockFileName(aLockFile));
+    }
+
+    // Eight character file name
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.xlsx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$12345678.xlsx"), GetLockFileName(aLockFile));
+    }
+
+    // One character file name
+    {
+        OUString aTestFile = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/1.xlsx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$1.xlsx"), GetLockFileName(aLockFile));
+    }
+
+    // Test for ODS format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.ods");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$12345678.ods"), GetLockFileName(aLockFile));
+    }
+}
+
+void LockfileTest::testPowerPointLockFileURL()
+{
+    // Test the generated file name for PowerPoint lock files
+    {
+        OUString aTestFile = m_directories.getURLFromSrc(
+            "/svl/qa/unit/lockfiles/data/testPowerPointLockFileURL.pptx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$testPowerPointLockFileURL.pptx"),
+                             GetLockFileName(aLockFile));
+    }
+
+    // Eight character file name
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.pptx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$12345678.pptx"), GetLockFileName(aLockFile));
+    }
+
+    // One character file name
+    {
+        OUString aTestFile = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/1.pptx");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$1.pptx"), GetLockFileName(aLockFile));
+    }
+
+    // Test for PPT format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.ppt");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$12345678.ppt"), GetLockFileName(aLockFile));
+    }
+
+    // Test for ODP format
+    {
+        OUString aTestFile
+            = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/12345678.odp");
+        svt::MSODocumentLockFile aLockFile(aTestFile);
+        CPPUNIT_ASSERT_EQUAL(OUString("~$12345678.odp"), GetLockFileName(aLockFile));
+    }
+}
+
+void LockfileTest::testWordLockFileContent()
+{
+    // Test the lockfile generated for the specified DOCX document
+    OUString aTestFile
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testWordLockFileContent.docx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and check the content
+    svt::MSODocumentLockFile aLockFile(aTestFile);
+    aLockFile.CreateOwnLockFile();
+    OUString sLockFileContent(readLockFile(aLockFile.GetURL()));
+    aLockFile.RemoveFileDirectly();
+
+    // First character is the size of the user name
+    OUString sUserName;
+    sUserName += aUserOpt.GetFirstName() + " ";
+    sUserName += aUserOpt.GetLastName();
+    int nIndex = 0;
+    CPPUNIT_ASSERT_EQUAL(sUserName.getLength(), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+
+    // Then we have the user name
+    CPPUNIT_ASSERT_EQUAL(sUserName, sLockFileContent.copy(1, sUserName.getLength()));
+
+    // We have some filling 0 bytes after the user name
+    for (nIndex = sUserName.getLength() + 1; nIndex < MSO_USERNAME_MAX_LENGTH + 2; ++nIndex)
+    {
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(0), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+    }
+
+    // Then we have the user name's length again
+    CPPUNIT_ASSERT_EQUAL(sUserName.getLength(), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+
+    // Then we have the user name again with 16 bit coding
+    for (int i = 0; i < sUserName.getLength(); ++i)
+    {
+        CPPUNIT_ASSERT_EQUAL(
+            sUserName[i],
+            static_cast<sal_Unicode>(static_cast<sal_Int16>(sLockFileContent[55 + i * 2])
+                                     + static_cast<sal_Int16>(sLockFileContent[55 + i * 2 + 1])));
+    }
+
+    // We have some filling 0 bytes after the user name
+    for (nIndex += sUserName.getLength() * 2 + 1; nIndex < MSO_WORD_LOCKFILE_SIZE; ++nIndex)
+    {
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(0), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+    }
+
+    // We have a fixed size lock file
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(MSO_WORD_LOCKFILE_SIZE), sLockFileContent.getLength());
+}
+
+void LockfileTest::testExcelLockFileContent()
+{
+    // Test the lockfile generated for the specified XLSX document
+    OUString aTestFile
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testExcelLockFileContent.xlsx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and check the content
+    svt::MSODocumentLockFile aLockFile(aTestFile);
+    aLockFile.CreateOwnLockFile();
+    OUString sLockFileContent(readLockFile(aLockFile.GetURL()));
+    aLockFile.RemoveFileDirectly();
+
+    // First character is the size of the user name
+    OUString sUserName;
+    sUserName += aUserOpt.GetFirstName() + " ";
+    sUserName += aUserOpt.GetLastName();
+    int nIndex = 0;
+    CPPUNIT_ASSERT_EQUAL(sUserName.getLength(), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+
+    // Then we have the user name
+    CPPUNIT_ASSERT_EQUAL(sUserName, sLockFileContent.copy(1, sUserName.getLength()));
+
+    // We have some filling 0x20 bytes after the user name
+    for (nIndex = sUserName.getLength() + 1; nIndex < MSO_USERNAME_MAX_LENGTH + 3; ++nIndex)
+    {
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(0x20), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+    }
+
+    // Then we have the user name's length again
+    CPPUNIT_ASSERT_EQUAL(sUserName.getLength(), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+
+    // Then we have the user name again with 16 bit coding
+    for (int i = 0; i < sUserName.getLength(); ++i)
+    {
+        CPPUNIT_ASSERT_EQUAL(
+            sUserName[i],
+            static_cast<sal_Unicode>(static_cast<sal_Int16>(sLockFileContent[56 + i * 2])
+                                     + static_cast<sal_Int16>(sLockFileContent[56 + i * 2 + 1])));
+    }
+
+    // We have some filling 0 and 0x20 bytes after the user name
+    for (nIndex += sUserName.getLength() * 2 + 2; nIndex < MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE;
+         nIndex += 2)
+    {
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(0x20), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+        if (nIndex + 1 < MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE)
+            CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+                                 static_cast<sal_Int32>(sLockFileContent[nIndex + 1]));
+    }
+
+    // We have a fixed size lock file
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE),
+                         sLockFileContent.getLength());
+}
+
+void LockfileTest::testPowerPointLockFileContent()
+{
+    // Test the lockfile generated for the specified PPTX document
+    OUString aTestFile = m_directories.getURLFromSrc(
+        "/svl/qa/unit/lockfiles/data/testPowerPointLockFileContent.pptx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and check the content
+    svt::MSODocumentLockFile aLockFile(aTestFile);
+    aLockFile.CreateOwnLockFile();
+    OUString sLockFileContent(readLockFile(aLockFile.GetURL()));
+    aLockFile.RemoveFileDirectly();
+
+    // First character is the size of the user name
+    OUString sUserName;
+    sUserName += aUserOpt.GetFirstName() + " ";
+    sUserName += aUserOpt.GetLastName();
+    int nIndex = 0;
+    CPPUNIT_ASSERT_EQUAL(sUserName.getLength(), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+
+    // Then we have the user name
+    CPPUNIT_ASSERT_EQUAL(sUserName, sLockFileContent.copy(1, sUserName.getLength()));
+
+    // We have some filling bytes after the user name
+    nIndex = sUserName.getLength() + 1;
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+    for (nIndex += 1; nIndex < MSO_USERNAME_MAX_LENGTH + 3; ++nIndex)
+    {
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(0x20), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+    }
+
+    // Then we have the user name's length again
+    CPPUNIT_ASSERT_EQUAL(sUserName.getLength(), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+
+    // Then we have the user name again with 16 bit coding
+    for (int i = 0; i < sUserName.getLength(); ++i)
+    {
+        CPPUNIT_ASSERT_EQUAL(
+            sUserName[i],
+            static_cast<sal_Unicode>(static_cast<sal_Int16>(sLockFileContent[56 + i * 2])
+                                     + static_cast<sal_Int16>(sLockFileContent[56 + i * 2 + 1])));
+    }
+
+    // We have some filling 0 and 0x20 bytes after the user name
+    for (nIndex += sUserName.getLength() * 2 + 2; nIndex < MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE;
+         nIndex += 2)
+    {
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(0x20), static_cast<sal_Int32>(sLockFileContent[nIndex]));
+        if (nIndex + 1 < MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE)
+            CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+                                 static_cast<sal_Int32>(sLockFileContent[nIndex + 1]));
+    }
+
+    // We have a fixed size lock file
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE),
+                         sLockFileContent.getLength());
+}
+
+void LockfileTest::testWordLockFileRT()
+{
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testWordLockFileRT.docx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and read it back
+    svt::MSODocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+    aLockFile.RemoveFileDirectly();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+}
+
+void LockfileTest::testExcelLockFileRT()
+{
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testExcelLockFileRT.xlsx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and read it back
+    svt::MSODocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+    aLockFile.RemoveFileDirectly();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+}
+
+void LockfileTest::testPowerPointLockFileRT()
+{
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testPowerPointLockFileRT.pptx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and read it back
+    svt::MSODocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+    aLockFile.RemoveFileDirectly();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+}
+
+void LockfileTest::testMSOLockFileLongUserName()
+{
+    OUString aTestODT = m_directories.getURLFromSrc(
+        "/svl/qa/unit/lockfiles/data/testMSOLockFileLongUserName.docx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName,
+                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+    aUserOpt.SetToken(UserOptToken::LastName,
+                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+
+    // Write the lock file and read it back
+    svt::MSODocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+
+    // Check whether the user name was cut to the maximum length
+    CPPUNIT_ASSERT_EQUAL(
+        aOrigEntry[LockFileComponent::OOOUSERNAME].copy(0, MSO_USERNAME_MAX_LENGTH),
+        aRTEntry[LockFileComponent::OOOUSERNAME]);
+
+    aLockFile.RemoveFileDirectly();
+}
+
+void LockfileTest::testMSOLockFileUnicodeUsername()
+{
+    // Test the lockfile generated for the specified ODT document
+    OUString aTestODT = m_directories.getURLFromSrc(
+        "/svl/qa/unit/lockfiles/data/testMSOLockFileUnicodeUsername.docx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    sal_Unicode vFirstName[] = { 2351, 2676, 3117, 5279 };
+    aUserOpt.SetToken(UserOptToken::FirstName, OUString(vFirstName, 4));
+    sal_Unicode vLastName[] = { 671, 1245, 1422, 1822 };
+    aUserOpt.SetToken(UserOptToken::LastName, OUString(vLastName, 4));
+
+    // Write the lock file and read it back
+    svt::DocumentLockFile aLockFile(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile.CreateOwnLockFile();
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+
+    // Check whether the user name is the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+    CPPUNIT_ASSERT_EQUAL(OUString(aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName()),
+                         aOrigEntry[LockFileComponent::OOOUSERNAME]);
+
+    aLockFile.RemoveFileDirectly();
+}
+
+void LockfileTest::testMSOLockFileOverwrite()
+{
+    OUString aTestODT
+        = m_directories.getURLFromSrc("/svl/qa/unit/lockfiles/data/testMSOLockFileOverwrite.docx");
+
+    // Set user name
+    SvtUserOptions aUserOpt;
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Write the lock file and read it back
+    svt::MSODocumentLockFile aLockFile(aTestODT);
+    aLockFile.CreateOwnLockFile();
+
+    // Change user name
+    aUserOpt.SetToken(UserOptToken::FirstName, "LockFile2");
+    aUserOpt.SetToken(UserOptToken::LastName, "Test");
+
+    // Overwrite lockfile
+    svt::MSODocumentLockFile aLockFile2(aTestODT);
+    LockFileEntry aOrigEntry = svt::LockFileCommon::GenerateOwnEntry();
+    aLockFile2.OverwriteOwnLockFile();
+
+    LockFileEntry aRTEntry = aLockFile.GetLockData();
+
+    // Check whether the lock file attributes are the same
+    CPPUNIT_ASSERT_EQUAL(aOrigEntry[LockFileComponent::OOOUSERNAME],
+                         aRTEntry[LockFileComponent::OOOUSERNAME]);
+
+    aLockFile2.RemoveFileDirectly();
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LockfileTest);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svl/source/misc/documentlockfile.cxx b/svl/source/misc/documentlockfile.cxx
index 2bd806b04b0c..780219b8c115 100644
--- a/svl/source/misc/documentlockfile.cxx
+++ b/svl/source/misc/documentlockfile.cxx
@@ -49,42 +49,34 @@ using namespace ::com::sun::star;
 
 namespace svt {
 
-bool DocumentLockFile::m_bAllowInteraction = true;
+GenDocumentLockFile::GenDocumentLockFile( const OUString& aURL )
+: LockFileCommon( aURL )
+{
+}
 
 
-DocumentLockFile::DocumentLockFile( const OUString& aOrigURL )
-: LockFileCommon( aOrigURL, ".~lock." )
+GenDocumentLockFile::GenDocumentLockFile( const OUString& aOrigURL, const OUString& aPrefix )
+: LockFileCommon( aOrigURL, aPrefix )
 {
 }
 
 
-DocumentLockFile::~DocumentLockFile()
+GenDocumentLockFile::~GenDocumentLockFile()
 {
 }
 
-
-void DocumentLockFile::WriteEntryToStream( const LockFileEntry& aEntry, const uno::Reference< io::XOutputStream >& xOutput )
+uno::Reference< io::XInputStream > GenDocumentLockFile::OpenStream()
 {
     ::osl::MutexGuard aGuard( m_aMutex );
 
-    OUStringBuffer aBuffer;
-
-    for ( LockFileComponent lft : o3tl::enumrange<LockFileComponent>() )
-    {
-        aBuffer.append( EscapeCharacters( aEntry[lft] ) );
-        if ( lft < LockFileComponent::LAST )
-            aBuffer.append( ',' );
-        else
-            aBuffer.append( ';' );
-    }
+    uno::Reference < css::ucb::XCommandEnvironment > xEnv;
+    ::ucbhelper::Content aSourceContent( GetURL(), xEnv, comphelper::getProcessComponentContext() );
 
-    OString aStringData( OUStringToOString( aBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) );
-    uno::Sequence< sal_Int8 > aData( reinterpret_cast<sal_Int8 const *>(aStringData.getStr()), aStringData.getLength() );
-    xOutput->writeBytes( aData );
+    // the file can be opened readonly, no locking will be done
+    return aSourceContent.openStream();
 }
 
-
-bool DocumentLockFile::CreateOwnLockFile()
+bool GenDocumentLockFile::CreateOwnLockFile()
 {
     ::osl::MutexGuard aGuard( m_aMutex );
 
@@ -108,7 +100,7 @@ bool DocumentLockFile::CreateOwnLockFile()
         xSeekable->seek( 0 );
 
         uno::Reference < css::ucb::XCommandEnvironment > xEnv;
-        ::ucbhelper::Content aTargetContent( m_aURL, xEnv, comphelper::getProcessComponentContext() );
+        ::ucbhelper::Content aTargetContent( GetURL(), xEnv, comphelper::getProcessComponentContext() );
 
         ucb::InsertCommandArgument aInsertArg;
         aInsertArg.Data = xInput;
@@ -130,50 +122,13 @@ bool DocumentLockFile::CreateOwnLockFile()
     return true;
 }
 
-
-LockFileEntry DocumentLockFile::GetLockData()
-{
-    ::osl::MutexGuard aGuard( m_aMutex );
-
-    uno::Reference< io::XInputStream > xInput = OpenStream();
-    if ( !xInput.is() )
-        throw uno::RuntimeException();
-
-    const sal_Int32 nBufLen = 32000;
-    uno::Sequence< sal_Int8 > aBuffer( nBufLen );
-
-    sal_Int32 nRead = 0;
-
-    nRead = xInput->readBytes( aBuffer, nBufLen );
-    xInput->closeInput();
-
-    if ( nRead == nBufLen )
-        throw io::WrongFormatException();
-
-    sal_Int32 nCurPos = 0;
-    return ParseEntry( aBuffer, nCurPos );
-}
-
-
-uno::Reference< io::XInputStream > DocumentLockFile::OpenStream()
-{
-    ::osl::MutexGuard aGuard( m_aMutex );
-
-    uno::Reference < css::ucb::XCommandEnvironment > xEnv;
-    ::ucbhelper::Content aSourceContent( m_aURL, xEnv, comphelper::getProcessComponentContext() );
-
-    // the file can be opened readonly, no locking will be done
-    return aSourceContent.openStream();
-}
-
-
-bool DocumentLockFile::OverwriteOwnLockFile()
+bool GenDocumentLockFile::OverwriteOwnLockFile()
 {
     // allows to overwrite the lock file with the current data
     try
     {
         uno::Reference < css::ucb::XCommandEnvironment > xEnv;
-        ::ucbhelper::Content aTargetContent( m_aURL, xEnv, comphelper::getProcessComponentContext() );
+        ::ucbhelper::Content aTargetContent( GetURL(), xEnv, comphelper::getProcessComponentContext() );
 
         LockFileEntry aNewEntry = GenerateOwnEntry();
 
@@ -193,8 +148,7 @@ bool DocumentLockFile::OverwriteOwnLockFile()
     return true;
 }
 
-
-void DocumentLockFile::RemoveFile()
+void GenDocumentLockFile::RemoveFile()
 {
     ::osl::MutexGuard aGuard( m_aMutex );
 
@@ -210,15 +164,71 @@ void DocumentLockFile::RemoveFile()
     RemoveFileDirectly();
 }
 
-void DocumentLockFile::RemoveFileDirectly()
+void GenDocumentLockFile::RemoveFileDirectly()
 {
     uno::Reference < css::ucb::XCommandEnvironment > xEnv;
-    ::ucbhelper::Content aCnt(m_aURL, xEnv, comphelper::getProcessComponentContext());
+    ::ucbhelper::Content aCnt(GetURL(), xEnv, comphelper::getProcessComponentContext());
     aCnt.executeCommand("delete",
         uno::makeAny(true));
 }
 
 
+DocumentLockFile::DocumentLockFile( const OUString& aOrigURL )
+: GenDocumentLockFile( aOrigURL, ".~lock." )
+{
+}
+
+
+DocumentLockFile::~DocumentLockFile()
+{
+}
+
+
+void DocumentLockFile::WriteEntryToStream( const LockFileEntry& aEntry, const uno::Reference< io::XOutputStream >& xOutput )
+{
+    ::osl::MutexGuard aGuard( m_aMutex );
+
+    OUStringBuffer aBuffer;
+
+    for ( LockFileComponent lft : o3tl::enumrange<LockFileComponent>() )
+    {
+        aBuffer.append( EscapeCharacters( aEntry[lft] ) );
+        if ( lft < LockFileComponent::LAST )
+            aBuffer.append( ',' );
+        else
+            aBuffer.append( ';' );
+    }
+
+    OString aStringData( OUStringToOString( aBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) );
+    uno::Sequence< sal_Int8 > aData( reinterpret_cast<sal_Int8 const *>(aStringData.getStr()), aStringData.getLength() );
+    xOutput->writeBytes( aData );
+}
+
+LockFileEntry DocumentLockFile::GetLockData()
+{
+    ::osl::MutexGuard aGuard( m_aMutex );
+
+    uno::Reference< io::XInputStream > xInput = OpenStream();
+    if ( !xInput.is() )
+        throw uno::RuntimeException();
+
+    const sal_Int32 nBufLen = 32000;
+    uno::Sequence< sal_Int8 > aBuffer( nBufLen );
+
+    sal_Int32 nRead = 0;
+
+    nRead = xInput->readBytes( aBuffer, nBufLen );
+    xInput->closeInput();
+
+    if ( nRead == nBufLen )
+        throw io::WrongFormatException();
+
+    sal_Int32 nCurPos = 0;
+    return ParseEntry( aBuffer, nCurPos );
+}
+
+
+
 
 } // namespace svt
 
diff --git a/svl/source/misc/lockfilecommon.cxx b/svl/source/misc/lockfilecommon.cxx
index eb5754eb0476..d98201402cdb 100644
--- a/svl/source/misc/lockfilecommon.cxx
+++ b/svl/source/misc/lockfilecommon.cxx
@@ -56,20 +56,43 @@ using namespace ::com::sun::star;
 namespace svt {
 
 
+LockFileCommon::LockFileCommon( const OUString& aURL )
+{
+    m_aURL = aURL;
+}
+
 LockFileCommon::LockFileCommon( const OUString& aOrigURL, const OUString& aPrefix )
 {
+    m_aURL = GenerateURL(aOrigURL, aPrefix);
+}
+
+
+LockFileCommon::~LockFileCommon()
+{
+}
+
+
+const OUString& LockFileCommon::GetURL() const
+{
+    return m_aURL;
+}
+
+
+void LockFileCommon::SetURL(const OUString& aURL)
+{
+    m_aURL = aURL;
+}
+
+
+OUString LockFileCommon::GenerateURL( const OUString& aOrigURL, const OUString& aPrefix )
+{
     INetURLObject aDocURL = ResolveLinks( INetURLObject( aOrigURL ) );
 
     OUString aShareURLString = aDocURL.GetPartBeforeLastName();
     aShareURLString += aPrefix;
     aShareURLString += aDocURL.GetName();
     aShareURLString += "%23"; // '#'
-    m_aURL = INetURLObject( aShareURLString ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
-}
-
-
-LockFileCommon::~LockFileCommon()
-{
+    return INetURLObject( aShareURLString ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
 }
 
 
diff --git a/svl/source/misc/msodocumentlockfile.cxx b/svl/source/misc/msodocumentlockfile.cxx
new file mode 100644
index 000000000000..938b36d5cd26
--- /dev/null
+++ b/svl/source/misc/msodocumentlockfile.cxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <svl/msodocumentlockfile.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <algorithm>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+namespace svt
+{
+bool MSODocumentLockFile::isWordFormat(const OUString& aOrigURL) const
+{
+    INetURLObject aDocURL = ResolveLinks(INetURLObject(aOrigURL));
+
+    return aDocURL.GetFileExtension().compareToIgnoreAsciiCase("DOC") == 0
+           || aDocURL.GetFileExtension().compareToIgnoreAsciiCase("DOCX") == 0
+           || aDocURL.GetFileExtension().compareToIgnoreAsciiCase("RTF") == 0
+           || aDocURL.GetFileExtension().compareToIgnoreAsciiCase("ODT") == 0;
+}
+
+bool MSODocumentLockFile::isExcelFormat(const OUString& aOrigURL) const
+{
+    INetURLObject aDocURL = ResolveLinks(INetURLObject(aOrigURL));
+
+    return //aDocURL.GetFileExtension().compareToIgnoreAsciiCase("XLS") || // MSO does not create lockfile for XLS
+        aDocURL.GetFileExtension().compareToIgnoreAsciiCase("XLSX") == 0
+        || aDocURL.GetFileExtension().compareToIgnoreAsciiCase("ODS") == 0;
+}
+
+bool MSODocumentLockFile::isPowerPointFormat(const OUString& aOrigURL) const
+{
+    INetURLObject aDocURL = ResolveLinks(INetURLObject(aOrigURL));
+
+    return aDocURL.GetFileExtension().compareToIgnoreAsciiCase("PPTX") == 0
+           || aDocURL.GetFileExtension().compareToIgnoreAsciiCase("PPT") == 0
+           || aDocURL.GetFileExtension().compareToIgnoreAsciiCase("ODP") == 0;
+}
+
+MSODocumentLockFile::MSODocumentLockFile(const OUString& aOrigURL)
+    : GenDocumentLockFile(GenerateURL(aOrigURL, "~$"))
+    , m_sOrigURL(aOrigURL)
+{
+}
+
+MSODocumentLockFile::~MSODocumentLockFile() {}
+
+OUString MSODocumentLockFile::GenerateURL(const OUString& aOrigURL, const OUString& aPrefix)
+{
+    INetURLObject aDocURL = ResolveLinks(INetURLObject(aOrigURL));
+    OUString aURL = aDocURL.GetPartBeforeLastName();
+    aURL += aPrefix;
+
+    // For text documents MSO Word cuts some of the first characters of the file name
+    OUString sFileName = aDocURL.GetName();
+    if (isWordFormat(aOrigURL))
+    {
+        sal_Int32 nFileNameLength
+            = aDocURL.GetName().getLength() - aDocURL.GetFileExtension().getLength() - 1;
+        if (nFileNameLength >= 8)
+            aURL += sFileName.copy(2);
+        else if (nFileNameLength == 7)
+            aURL += sFileName.copy(1);
+        else
+            aURL += sFileName;
+    }
+    else
+    {
+        aURL += sFileName;
+    }
+    return INetURLObject(aURL).GetMainURL(INetURLObject::DecodeMechanism::NONE);
+}
+
+void MSODocumentLockFile::WriteEntryToStream(
+    const LockFileEntry& aEntry, const css::uno::Reference<css::io::XOutputStream>& xOutput)
+{
+    ::osl::MutexGuard aGuard(m_aMutex);
+
+    // Reallocate the date with the right size, different lock file size for different components
+    int nLockFileSize = isWordFormat(m_sOrigURL) ? MSO_WORD_LOCKFILE_SIZE
+                                                 : MSO_EXCEL_AND_POWERPOINT_LOCKFILE_SIZE;
+    css::uno::Sequence<sal_Int8> aData(nLockFileSize);
+
+    // Write out the user name's length as a single byte integer
+    // The maximum length is 52 in MSO, so we'll need to truncate the user name if it's longer
+    OUString aUserName = aEntry[LockFileComponent::OOOUSERNAME];
+    int nIndex = 0;
+    aData[nIndex] = static_cast<sal_Int8>(
+        std::min(aUserName.getLength(), sal_Int32(MSO_USERNAME_MAX_LENGTH)));
+
+    if (aUserName.getLength() > MSO_USERNAME_MAX_LENGTH)
+        aUserName = aUserName.copy(0, MSO_USERNAME_MAX_LENGTH);
+
+    // From the second position write out the user name using one byte characters.
+    nIndex = 1;
+    for (int nChar = 0; nChar < aUserName.getLength(); ++nChar)
+    {
+        aData[nIndex] = static_cast<sal_Int8>(aUserName[nChar]);
+        ++nIndex;
+    }
+
+    // Fill up the remaining bytes with dummy data
+    if (isWordFormat(m_sOrigURL))
+    {
+        while (nIndex < MSO_USERNAME_MAX_LENGTH + 2)
+        {
+            aData[nIndex] = static_cast<sal_Int8>(0);
+            ++nIndex;
+        }
+    }
+    else if (isExcelFormat(m_sOrigURL))
+    {
+        while (nIndex < MSO_USERNAME_MAX_LENGTH + 3)
+        {
+            aData[nIndex] = static_cast<sal_Int8>(0x20);
+            ++nIndex;
+        }
+    }
+    else
+    {
+        aData[nIndex] = static_cast<sal_Int8>(0);
+        ++nIndex;
+
+        while (nIndex < MSO_USERNAME_MAX_LENGTH + 3)
+        {
+            aData[nIndex] = static_cast<sal_Int8>(0x20);
+            ++nIndex;
+        }
+    }
+
+    // At the next position we have the user name's length again, but now as a 2 byte integer
+    aData[nIndex] = static_cast<sal_Int8>(
+        std::min(aUserName.getLength(), sal_Int32(MSO_USERNAME_MAX_LENGTH)));
+    ++nIndex;
+    aData[nIndex] = 0;
+    ++nIndex;
+
+    // And the user name again with unicode characters
+    for (int nChar = 0; nChar < aUserName.getLength(); ++nChar)
+    {
+        aData[nIndex] = static_cast<sal_Int8>(aUserName[nChar] & 0xff);
+        ++nIndex;
+        aData[nIndex] = static_cast<sal_Int8>(aUserName[nChar] >> 8);
+        ++nIndex;
+    }
+
+    // Fill the remaining part with dummy bits
+    if (isWordFormat(m_sOrigURL))
+    {
+        while (nIndex < nLockFileSize)
+        {
+            aData[nIndex] = static_cast<sal_Int8>(0);
+            ++nIndex;
+        }
+    }
+    else
+    {
+        while (nIndex < nLockFileSize)
+        {
+            aData[nIndex] = static_cast<sal_Int8>(0x20);
+            ++nIndex;
+            if (nIndex < nLockFileSize)
+            {
+                aData[nIndex] = static_cast<sal_Int8>(0);
+                ++nIndex;
+            }
+        }
+    }
+
+    xOutput->writeBytes(aData);
+}
+
+LockFileEntry MSODocumentLockFile::GetLockData()
+{
+    ::osl::MutexGuard aGuard(m_aMutex);
+
+    LockFileEntry aResult;
+    css::uno::Reference<css::io::XInputStream> xInput = OpenStream();
+    if (!xInput.is())
+        throw css::uno::RuntimeException();
+
+    const sal_Int32 nBufLen = 256;
+    css::uno::Sequence<sal_Int8> aBuf(nBufLen);
+    const sal_Int32 nRead = xInput->readBytes(aBuf, nBufLen);
+    xInput->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
+                aResult[LockFileComponent::OOOUSERNAME]
+                    = OUString(reinterpret_cast<const sal_Unicode*>(pBuf + 2), nUTF16Len);
+        }
+    }
+    return aResult;
+}
+
+void MSODocumentLockFile::RemoveFile()
+{
+    ::osl::MutexGuard aGuard(m_aMutex);
+
+    // TODO/LATER: the removing is not atomic, is it possible in general to make it atomic?
+    LockFileEntry aNewEntry = GenerateOwnEntry();
+    LockFileEntry aFileData = GetLockData();
+
+    if (aFileData[LockFileComponent::OOOUSERNAME] != aNewEntry[LockFileComponent::OOOUSERNAME])
+        throw css::io::IOException(); // not the owner, access denied
+
+    RemoveFileDirectly();
+}
+
+} // namespace svt
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svl/source/misc/sharecontrolfile.cxx b/svl/source/misc/sharecontrolfile.cxx
index 61a561a82c3d..b7a8c925f3b0 100644
--- a/svl/source/misc/sharecontrolfile.cxx
+++ b/svl/source/misc/sharecontrolfile.cxx
@@ -55,10 +55,10 @@ namespace svt {
 ShareControlFile::ShareControlFile( const OUString& aOrigURL )
 : LockFileCommon( aOrigURL, ".~sharing." )
 {
-    if ( !m_xStream.is() && !m_aURL.isEmpty() )
+    if ( !m_xStream.is() && !GetURL().isEmpty() )
     {
         uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
-        ::ucbhelper::Content aContent = ::ucbhelper::Content( m_aURL, xDummyEnv, comphelper::getProcessComponentContext() );
+        ::ucbhelper::Content aContent = ::ucbhelper::Content( GetURL(), xDummyEnv, comphelper::getProcessComponentContext() );
 
         uno::Reference< ucb::XContentIdentifier > xContId( aContent.get().is() ? aContent.get()->getIdentifier() : nullptr );
         if ( !xContId.is() || xContId->getContentProviderScheme() != "file" )
@@ -329,7 +329,7 @@ void ShareControlFile::RemoveFile()
     Close();
 
     uno::Reference<ucb::XSimpleFileAccess3> xSimpleFileAccess(ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()));
-    xSimpleFileAccess->kill( m_aURL );
+    xSimpleFileAccess->kill( GetURL() );
 }
 
 } // namespace svt


More information about the Libreoffice-commits mailing list