[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.0' - 33 commits - chart2/source comphelper/source dbaccess/Library_dbahsql.mk dbaccess/source desktop/qa desktop/source embeddedobj/source extensions/source include/comphelper include/LibreOfficeKit include/oox include/sfx2 include/vbahelper include/vcl libreofficekit/qa oox/source package/source sc/qa sc/source sd/qa sd/source sfx2/source shell/Executable_senddoc.mk shell/Library_smplmail.mk shell/Module_shell.mk shell/source shell/StaticLibrary_simplemapi.mk solenv/clang-format svtools/source sw/inc sw/qa sw/source vcl/inc vcl/source

Henry Castro hcastro at collabora.com
Mon Mar 26 06:23:46 UTC 2018


 chart2/source/controller/inc/ChartWindow.hxx                      |    5 
 chart2/source/controller/main/ChartWindow.cxx                     |   15 
 comphelper/source/xml/ofopxmlhelper.cxx                           |   32 
 dbaccess/Library_dbahsql.mk                                       |    5 
 dbaccess/source/filter/hsqldb/columndef.hxx                       |   10 
 dbaccess/source/filter/hsqldb/createparser.hxx                    |   10 
 dbaccess/source/filter/hsqldb/fbcreateparser.hxx                  |   10 
 dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx                  |   61 +
 dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx                  |   39 +
 dbaccess/source/filter/hsqldb/hsqlimport.cxx                      |  241 ++++++
 dbaccess/source/filter/hsqldb/hsqlimport.hxx                      |   20 
 dbaccess/source/filter/hsqldb/parseschema.cxx                     |   26 
 dbaccess/source/filter/hsqldb/parseschema.hxx                     |   17 
 dbaccess/source/filter/hsqldb/rowinputbinary.cxx                  |  246 +++++++
 dbaccess/source/filter/hsqldb/rowinputbinary.hxx                  |   46 +
 desktop/qa/desktop_lib/test_desktop_lib.cxx                       |   13 
 desktop/source/lib/init.cxx                                       |   89 --
 embeddedobj/source/commonembedding/embedobj.cxx                   |   34 
 extensions/source/ole/olethread.cxx                               |   28 
 include/LibreOfficeKit/LibreOfficeKit.h                           |   10 
 include/LibreOfficeKit/LibreOfficeKit.hxx                         |    6 
 include/comphelper/ofopxmlhelper.hxx                              |   12 
 include/oox/core/xmlfilterbase.hxx                                |    6 
 include/sfx2/strings.hrc                                          |    3 
 include/vbahelper/vbaaccesshelper.hxx                             |    4 
 include/vcl/IDialogRenderable.hxx                                 |    2 
 include/vcl/ITiledRenderable.hxx                                  |  111 +++
 include/vcl/window.hxx                                            |    7 
 libreofficekit/qa/unit/tiledrendering.cxx                         |    1 
 oox/source/core/xmlfilterbase.cxx                                 |   40 -
 package/source/zippackage/ZipPackage.cxx                          |   12 
 sc/qa/unit/data/xlsx/customxml.xlsx                               |binary
 sc/qa/unit/helper/xpath.cxx                                       |    7 
 sc/qa/unit/helper/xpath.hxx                                       |    6 
 sc/qa/unit/subsequent_export-test.cxx                             |   21 
 sc/qa/unit/tiledrendering/tiledrendering.cxx                      |    3 
 sc/source/filter/excel/excdoc.cxx                                 |    3 
 sc/source/ui/cctrl/cbuttonw.cxx                                   |    4 
 sc/source/ui/inc/gridwin.hxx                                      |    7 
 sc/source/ui/unoobj/docuno.cxx                                    |   47 -
 sc/source/ui/view/gridwin.cxx                                     |   15 
 sc/source/ui/view/tabvwshf.cxx                                    |   28 
 sd/qa/unit/data/pptx/customxml.pptx                               |binary
 sd/qa/unit/export-tests-ooxml1.cxx                                |   23 
 sd/qa/unit/sdmodeltestbase.hxx                                    |   21 
 sd/qa/unit/tiledrendering/tiledrendering.cxx                      |    7 
 sd/source/filter/eppt/pptx-epptooxml.cxx                          |    4 
 sd/source/ui/inc/ViewShell.hxx                                    |    6 
 sd/source/ui/inc/Window.hxx                                       |    6 
 sd/source/ui/unoidl/unomodel.cxx                                  |   38 -
 sd/source/ui/view/sdwindow.cxx                                    |   39 +
 sd/source/ui/view/viewshel.cxx                                    |   39 -
 sfx2/source/dialog/mailmodel.cxx                                  |    4 
 sfx2/source/doc/objserv.cxx                                       |   49 -
 sfx2/source/view/lokcharthelper.cxx                               |   41 -
 shell/Executable_senddoc.mk                                       |    8 
 shell/Library_smplmail.mk                                         |    7 
 shell/Module_shell.mk                                             |    1 
 shell/StaticLibrary_simplemapi.mk                                 |   16 
 shell/source/win32/simplemail/senddoc.cxx                         |  346 ++++++----
 shell/source/win32/simplemail/simplemapi.cxx                      |   88 --
 shell/source/win32/simplemail/simplemapi.hxx                      |   75 --
 shell/source/win32/simplemail/smplmailclient.cxx                  |  124 +++
 shell/source/win32/simplemail/smplmailclient.hxx                  |    7 
 shell/source/win32/simplemail/smplmailsuppl.cxx                   |    3 
 shell/source/win32/simplemail/smplmailsuppl.hxx                   |    2 
 solenv/clang-format/blacklist                                     |    2 
 svtools/source/contnr/treelistbox.cxx                             |    2 
 sw/inc/redline.hxx                                                |    6 
 sw/qa/extras/inc/swmodeltestbase.hxx                              |   11 
 sw/qa/extras/odfimport/data/tdf108482.odt                         |binary
 sw/qa/extras/odfimport/odfimport.cxx                              |   15 
 sw/qa/extras/ooxmlexport/ooxmlexport3.cxx                         |   11 
 sw/qa/extras/tiledrendering/data/removenode_redline_callback.fodt |  306 ++++++++
 sw/qa/extras/tiledrendering/data/splitnode_redline_callback.fodt  |  294 ++++++++
 sw/qa/extras/tiledrendering/tiledrendering.cxx                    |  170 ++++
 sw/qa/extras/uiwriter/uiwriter.cxx                                |    4 
 sw/source/core/doc/docredln.cxx                                   |   12 
 sw/source/core/inc/frame.hxx                                      |    2 
 sw/source/core/inc/txtfrm.hxx                                     |    2 
 sw/source/core/layout/tabfrm.cxx                                  |    3 
 sw/source/core/text/frmform.cxx                                   |   21 
 sw/source/uibase/app/docsh2.cxx                                   |   25 
 sw/source/uibase/inc/edtwin.hxx                                   |    6 
 sw/source/uibase/shells/textsh1.cxx                               |    4 
 sw/source/uibase/uno/unotxdoc.cxx                                 |   34 
 vcl/inc/window.h                                                  |    1 
 vcl/source/helper/errcode.cxx                                     |    2 
 vcl/source/window/cursor.cxx                                      |    4 
 vcl/source/window/mouse.cxx                                       |   24 
 vcl/source/window/window.cxx                                      |    3 
 91 files changed, 2497 insertions(+), 723 deletions(-)

New commits:
commit 6258f1a3c2a1d3f17f1c0c98726224a41253f6d5
Author: Henry Castro <hcastro at collabora.com>
Date:   Sun Mar 11 18:44:58 2018 -0400

    lokdialog: convert the show sheet dialog to async exec
    
    Change-Id: I501d9444ef9798a26b4db06ab51e4fb691144b17
    Reviewed-on: https://gerrit.libreoffice.org/51094
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Henry Castro <hcastro at collabora.com>
    (cherry picked from commit c9c0f257f4abe738203d2294ca86822e200d3ce3)

diff --git a/sc/source/ui/view/tabvwshf.cxx b/sc/source/ui/view/tabvwshf.cxx
index f315b6cbe89f..bcade9c218cc 100644
--- a/sc/source/ui/view/tabvwshf.cxx
+++ b/sc/source/ui/view/tabvwshf.cxx
@@ -134,7 +134,7 @@ void ScTabViewShell::ExecuteTable( SfxRequest& rReq )
                     ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
                     OSL_ENSURE(pFact, "ScAbstractFactory create fail!");
 
-                    ScopedVclPtr<AbstractScShowTabDlg> pDlg(pFact->CreateScShowTabDlg(GetDialogParent()));
+                    VclPtr<AbstractScShowTabDlg> pDlg(pFact->CreateScShowTabDlg(GetDialogParent()));
                     OSL_ENSURE(pDlg, "Dialog create fail!");
 
                     OUString aTabName;
@@ -149,18 +149,24 @@ void ScTabViewShell::ExecuteTable( SfxRequest& rReq )
                         }
                     }
 
-                    if ( pDlg->Execute() == RET_OK )
-                    {
-                        const sal_Int32 nCount = pDlg->GetSelectedEntryCount();
-                        for (sal_Int32 nPos=0; nPos<nCount; ++nPos)
+                    std::shared_ptr<SfxRequest> pReq = std::make_shared<SfxRequest>(rReq);
+                    pDlg->StartExecuteAsync([this, pDlg, pReq](sal_Int32 nResult){
+                        OUString sTable;
+                        std::vector<OUString> sTables;
+                        if (RET_OK == nResult)
                         {
-                            aName = pDlg->GetSelectedEntry(nPos);
-                            rReq.AppendItem( SfxStringItem( FID_TABLE_SHOW, aName ) );
-                            rNames.push_back(aName);
+                            const sal_Int32 nCount = pDlg->GetSelectedEntryCount();
+                            for (sal_Int32 nPos=0; nPos<nCount; ++nPos)
+                            {
+                                sTable = pDlg->GetSelectedEntry(nPos);
+                                pReq->AppendItem( SfxStringItem( FID_TABLE_SHOW, sTable ) );
+                                sTables.push_back(sTable);
+                            }
+                            ShowTable( sTables );
+                            pReq->Done();
                         }
-                        ShowTable( rNames );
-                        rReq.Done();
-                    }
+                    });
+                    rReq.Ignore();
                 }
             }
             break;
commit 91e8b2c3a7dfc7142511b60bb66c531b992d83b7
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Sun Mar 11 11:58:13 2018 +0300

    tdf#108482: ignore repeated headlines' previous instances height
    
    Change-Id: Idae0b99c4171556612fb41bb02f1c48bbfeec4b2
    Reviewed-on: https://gerrit.libreoffice.org/51073
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    (cherry picked from commit 82d9af7f14c808d48fa5db9420c85a8d26c79905)

diff --git a/sw/qa/extras/odfimport/data/tdf108482.odt b/sw/qa/extras/odfimport/data/tdf108482.odt
new file mode 100644
index 000000000000..c3ce2fc4272a
Binary files /dev/null and b/sw/qa/extras/odfimport/data/tdf108482.odt differ
diff --git a/sw/qa/extras/odfimport/odfimport.cxx b/sw/qa/extras/odfimport/odfimport.cxx
index a638dd818574..175fa31c888e 100644
--- a/sw/qa/extras/odfimport/odfimport.cxx
+++ b/sw/qa/extras/odfimport/odfimport.cxx
@@ -887,5 +887,20 @@ DECLARE_ODFIMPORT_TEST(testTdf115079, "tdf115079.odt")
     // This document caused segfault when layouting
 }
 
+DECLARE_ODFIMPORT_TEST(testTdf108482, "tdf108482.odt")
+{
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("The table on second page must have two rows",
+        OUString("2"), parseDump("count(/root/page[2]/body/tab/row)")
+    );
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("The second page table's first row must be the repeated headline",
+        OUString("Header"), parseDump("/root/page[2]/body/tab/row[1]/cell/txt")
+    );
+    // The first (repeated headline) row with vertical text orientation must have non-zero height
+    // (in my tests, it was 1135)
+    CPPUNIT_ASSERT_GREATER(
+        sal_Int32(1000), parseDump("/root/page[2]/body/tab/row[1]/infos/bounds", "height").toInt32()
+    );
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index ec6eb439a8d1..cd8a1167c147 100755
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -4111,6 +4111,9 @@ static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
 // on each following page
 static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow)
 {
+    // We don't need to account for previous instances of repeated headlines
+    if (rRow.IsRepeatedHeadline())
+        return 0;
     SwRectFnSet aRectFnSet(&rRow);
     const SwTableLine* pLine = rRow.GetTabLine();
     const SwTabFrame* pTab = rRow.FindTabFrame();
commit 201f9e90b217470aee6a2f650033414407f0a809
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Mar 9 16:30:05 2018 +0200

    Bin incorrect comments, this is not "org::openoffice" but "ooo::vba"
    
    Change-Id: I044bb3afa1e134dd851dd30f639b475400f4ceea
    Reviewed-on: https://gerrit.libreoffice.org/51006
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Tor Lillqvist <tml at collabora.com>
    (cherry picked from commit cf42f0916d1f30d8939de15bff626cabb6836d35)

diff --git a/include/vbahelper/vbaaccesshelper.hxx b/include/vbahelper/vbaaccesshelper.hxx
index 04460b165fb3..78728f5c07a3 100644
--- a/include/vbahelper/vbaaccesshelper.hxx
+++ b/include/vbahelper/vbaaccesshelper.hxx
@@ -76,8 +76,8 @@ namespace ooo
         // word seems to return an erroneous mime type :-/ "application/msword"  not consistent with the excel one
         inline bool isAlienWordDoc( SfxObjectShell const & rDocShell ) { return isAlienDoc( rDocShell, "application/msword" ); }
 
-    } // openoffice
-} // org
+    }
+}
 
 #endif
 
commit 8eebfa395eaa600908e9f2c41124816d14df7c67
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu Mar 8 12:40:28 2018 +0200

    Make the CoInitializeEx() (non-)error reporting ludicrously verbose
    
    Tell in the SAL_WARN what apartment mode the thread is in.
    
    It's a bit unclear to me why the code insists on calling this
    o2u_attachCurrentThread() function even in cases where it perhaps
    should know that it is in the main thread, which has been initialised
    as STA by the CoInitialize() in InitSalData().
    
    Change-Id: Ia69e67f8b17ee153d3bcf8ae450d5f413dea2e1a
    Reviewed-on: https://gerrit.libreoffice.org/50985
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Tor Lillqvist <tml at collabora.com>
    (cherry picked from commit 2445d97a31d2fb3ee6580444a3541653f9fdb642)

diff --git a/extensions/source/ole/olethread.cxx b/extensions/source/ole/olethread.cxx
index 6bb0c5daab2a..3b35a802a5f8 100644
--- a/extensions/source/ole/olethread.cxx
+++ b/extensions/source/ole/olethread.cxx
@@ -19,6 +19,7 @@
 
 #include "ole2uno.hxx"
 
+#include <comphelper/windowserrorstring.hxx>
 #include <osl/thread.hxx>
 #include <sal/log.hxx>
 
@@ -34,8 +35,31 @@ void o2u_attachCurrentThread()
         if (!SUCCEEDED(hr))
         {   // FIXME: is it a problem that this ends up in STA currently?
             assert(RPC_E_CHANGED_MODE == hr);
-            SAL_INFO("extensions.olebridge",
-                    "CoInitializeEx fail: probably thread is in STA already?");
+            // Let's find out explicitly what aprtment mode we are in.
+            SAL_WARN("extensions.olebridge", "CoInitializeEx failed"
+                     << (hr == RPC_E_CHANGED_MODE ? " (expectedly)" : "")
+                     << ": " << WindowsErrorStringFromHRESULT(hr));
+            APTTYPE nAptType;
+            APTTYPEQUALIFIER nAptTypeQualifier;
+            if (SUCCEEDED(CoGetApartmentType(&nAptType, &nAptTypeQualifier)))
+            {
+                SAL_WARN("extensions.olebridge",
+                         "  Thread is in a "
+                         << (nAptType == APTTYPE_STA ? OUString("single-threaded") :
+                             (nAptType == APTTYPE_MTA ? OUString("multi-threaded") :
+                              (nAptType == APTTYPE_NA ? OUString("neutral") :
+                               (nAptType == APTTYPE_MAINSTA ? OUString("main single-threaded") :
+                                ("unknown (") + OUString::number(nAptType) + ")"))))
+                         << " apartment"
+                         << (nAptTypeQualifier == APTTYPEQUALIFIER_NONE ? OUString() :
+                             (nAptTypeQualifier == APTTYPEQUALIFIER_IMPLICIT_MTA ? OUString(" (implicit)") :
+                              (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MTA ? OUString(" (on MTA)") :
+                               (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_STA ? OUString(" (on STA)") :
+                                (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA ? OUString(" (on implicit MTA)") :
+                                 (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MAINSTA ? OUString(" (on main STA)") :
+                                  (" (with unknown qualifier (" + OUString::number(nAptTypeQualifier) + "))")))))))
+                         << ".");
+            }
         }
         oleThreadData.setData(reinterpret_cast<void*>(true));
     }
commit c1df0d90e0b124df5d99777c51fb3f757026c9f8
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu Mar 8 12:24:48 2018 +0100

    tdf#116283 oox customXml: Don't write the Relationship to DOCX files twice.
    
    Change-Id: Id3da40138e86c142707e377aa897df372aacb704
    Reviewed-on: https://gerrit.libreoffice.org/50947
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>
    (cherry picked from commit bf5c486946f2b0a708a251c8ff614293ae37c6ba)

diff --git a/include/oox/core/xmlfilterbase.hxx b/include/oox/core/xmlfilterbase.hxx
index 75fc03920b79..69586fe020bb 100644
--- a/include/oox/core/xmlfilterbase.hxx
+++ b/include/oox/core/xmlfilterbase.hxx
@@ -226,7 +226,12 @@ public:
      */
     void exportDocumentProperties( const css::uno::Reference< css::document::XDocumentProperties >& xProperties );
 
+    /** Write the customXml entries we are preserving (xlsx and pptx only). */
+    void exportCustomFragments();
+
+    /** Read the document properties and also the customXml entries (xlsx and pptx only). */
     void importDocumentProperties();
+
     static void putPropertiesToDocumentGrabBag(const css::uno::Reference<css::lang::XComponent>& xDstDoc,
                                                const comphelper::SequenceAsHashMap& rProperties);
 
@@ -259,7 +264,6 @@ private:
                             const css::uno::Reference< css::io::XStream >& rxOutStream ) const override;
 
     void importCustomFragments(css::uno::Reference<css::embed::XStorage>& xDocumentStorage);
-    void exportCustomFragments();
 
 private:
     ::std::unique_ptr< XmlFilterBaseImpl > mxImpl;
diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx
index a45c3e6e6aef..873991a8f998 100644
--- a/oox/source/core/xmlfilterbase.cxx
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -842,8 +842,6 @@ void XmlFilterBase::exportDocumentProperties( const Reference< XDocumentProperti
         writeAppProperties( *this, xProperties );
         writeCustomProperties( *this, xProperties );
     }
-
-    exportCustomFragments();
 }
 
 // protected ------------------------------------------------------------------
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 38aa2e10e955..7672b96a9850 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -3044,6 +3044,10 @@ void ScExportTest::testCustomXml()
     xmlDocPtr pRelsDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "customXml/_rels/item1.xml.rels");
     CPPUNIT_ASSERT(pRelsDoc);
 
+    // Check there is a relation to itemProps1.xml.
+    assertXPath(pRelsDoc, "/r:Relationships/r:Relationship", 1);
+    assertXPath(pRelsDoc, "/r:Relationships/r:Relationship[@Id='rId1']", "Target", "itemProps1.xml");
+
     std::shared_ptr<SvStream> pStream = XPathHelper::parseExportStream(pXPathFile, m_xSFactory, "ddp/ddpfile.xen");
     CPPUNIT_ASSERT(pStream);
 }
diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx
index d2dd5d147b0e..bc0d4e3de177 100644
--- a/sc/source/filter/excel/excdoc.cxx
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -830,7 +830,8 @@ void ExcDocument::WriteXml( XclExpXmlStream& rStrm )
     uno::Reference<document::XDocumentPropertiesSupplier> xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
     uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
 
-    rStrm.exportDocumentProperties( xDocProps );
+    rStrm.exportDocumentProperties(xDocProps);
+    rStrm.exportCustomFragments();
 
     sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
     rWorkbook->startElement( XML_workbook,
diff --git a/sd/qa/unit/export-tests-ooxml1.cxx b/sd/qa/unit/export-tests-ooxml1.cxx
index e5076ab1c94d..f5fb54f0626a 100644
--- a/sd/qa/unit/export-tests-ooxml1.cxx
+++ b/sd/qa/unit/export-tests-ooxml1.cxx
@@ -847,8 +847,8 @@ void SdOOXMLExportTest1::testCustomXml()
     CPPUNIT_ASSERT(pRelsDoc);
 
     // Check there is a relation to itemProps1.xml.
-    const OUString sXmlPath = "/rels:Relationships/rels:Relationship[@Id='rId1']";
-    assertXPath(pRelsDoc, OUStringToOString(sXmlPath, RTL_TEXTENCODING_UTF8), "Target", "itemProps1.xml");
+    assertXPath(pRelsDoc, "/rels:Relationships/rels:Relationship", 1);
+    assertXPath(pRelsDoc, "/rels:Relationships/rels:Relationship[@Id='rId1']", "Target", "itemProps1.xml");
 
     std::shared_ptr<SvStream> pStream = parseExportStream(tempFile, "ddp/ddpfile.xen");
     CPPUNIT_ASSERT(pStream);
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx
index 91f11ac4d2d0..c62aee53b521 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -358,9 +358,9 @@ void PowerPointExport::writeDocumentProperties()
     uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
 
     if (xDocProps.is())
-    {
         exportDocumentProperties(xDocProps);
-    }
+
+    exportCustomFragments();
 }
 
 bool PowerPointExport::importDocument() throw()
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
index 0303237aece2..3af85d04c4d8 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
@@ -536,6 +536,17 @@ DECLARE_OOXMLEXPORT_TEST(testCustomXmlGrabBag, "customxml.docx")
     CPPUNIT_ASSERT(CustomXml); // Grab Bag has all the expected elements
 }
 
+DECLARE_OOXMLEXPORT_TEST(testCustomXmlRelationships, "customxml.docx")
+{
+    xmlDocPtr pXmlDoc = parseExport("customXml/_rels/item1.xml.rels");
+    if(!pXmlDoc)
+        return;
+
+    // Check there is a relation to itemProps1.xml.
+    assertXPath(pXmlDoc, "/rels:Relationships/rels:Relationship", 1);
+    assertXPath(pXmlDoc, "/rels:Relationships/rels:Relationship[@Id='rId1']", "Target", "itemProps1.xml");
+}
+
 DECLARE_OOXMLEXPORT_TEST(testFdo69644, "fdo69644.docx")
 {
     // The problem was that the exporter exported the table definition
commit e3029244ccbd75a9e0fd9cab2e8677336b88fff5
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Tue Mar 6 22:43:34 2018 -0500

    oox: preserve the ContentType of custom files
    
    Generic logic to preserve custom files with
    their correct ContentType. Standard default
    file extensions with respective ContentType
    preserved in [Content_Types].xml.
    
    Reviewed-on: https://gerrit.libreoffice.org/50856
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    (cherry picked from commit 8f79f22a8d4b1c2d209c55cd618c24428960088f)
    
    Change-Id: I651ed691e9a4745cd2cb4b3c4d4c5fd7287b66c2

diff --git a/comphelper/source/xml/ofopxmlhelper.cxx b/comphelper/source/xml/ofopxmlhelper.cxx
index d3ce7b0dc65d..bfb3dbdd0fa6 100644
--- a/comphelper/source/xml/ofopxmlhelper.cxx
+++ b/comphelper/source/xml/ofopxmlhelper.cxx
@@ -110,6 +110,38 @@ uno::Sequence< uno::Sequence< beans::StringPair > > ReadContentTypeSequence(
     return ReadSequence_Impl( xInStream, aStringID, CONTENTTYPE_FORMAT, rContext );
 }
 
+OUString GetContentTypeByName(
+                const css::uno::Sequence<css::uno::Sequence<css::beans::StringPair>>& rContentTypes,
+                const OUString& rFilename)
+{
+    if (rContentTypes.getLength() < 2)
+    {
+        return OUString();
+    }
+
+    const uno::Sequence<beans::StringPair>& rDefaults = rContentTypes[0];
+    const uno::Sequence<beans::StringPair>& rOverrides = rContentTypes[1];
+
+    // Find the extension and use it to get the type.
+    const sal_Int32 nDotOffset = rFilename.lastIndexOf('.');
+    const OUString aExt = (nDotOffset >= 0 ? rFilename.copy(nDotOffset + 1) : rFilename); // Skip the dot.
+
+    const std::vector<OUString> aNames = { aExt, "/" + rFilename };
+    for (const OUString& aName : aNames)
+    {
+        const auto it1 = std::find_if(rOverrides.begin(), rOverrides.end(), [&aName](const beans::StringPair& rPair)
+                                                                              { return rPair.First == aName; });
+        if (it1 != rOverrides.end())
+            return it1->Second;
+
+        const auto it2 = std::find_if(rDefaults.begin(), rDefaults.end(), [&aName](const beans::StringPair& rPair)
+                                                                            { return rPair.First == aName; });
+        if (it2 != rDefaults.end())
+            return it2->Second;
+    }
+
+    return OUString();
+}
 
 void WriteRelationsInfoSequence(
         const uno::Reference< io::XOutputStream >& xOutStream,
diff --git a/include/comphelper/ofopxmlhelper.hxx b/include/comphelper/ofopxmlhelper.hxx
index c0215a0d21d3..8c3b62be3956 100644
--- a/include/comphelper/ofopxmlhelper.hxx
+++ b/include/comphelper/ofopxmlhelper.hxx
@@ -56,6 +56,18 @@ namespace OFOPXMLHelper {
         const css::uno::Reference< css::io::XInputStream >& xInStream,
         const css::uno::Reference< css::uno::XComponentContext >& rContext );
 
+    // returns the ContentType for the given name, or empty when not found.
+    // rContentTypes is a sequence containing two entries of type sequence<StringPair>
+    // the first sequence describes "Default" elements, where each element is described
+    // by StringPair object ( First - Extension, Second - ContentType )
+    // the second sequence describes "Override" elements, where each element is described
+    // by StringPair object ( First - PartName, Second - ContentType )
+    // The "Override" sequence is searched first before falling back on "Default".
+    COMPHELPER_DLLPUBLIC
+    OUString
+    GetContentTypeByName(const css::uno::Sequence<css::uno::Sequence<css::beans::StringPair>>& rContentTypes,
+                         const OUString& rFilename);
+
     // writes sequence of elements, where each element is described by sequence of tags,
     // where each tag is described by StringPair ( First - name, Second - value )
     // the first tag of each element sequence must be "Id"
diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx
index 2746492a60ea..a45c3e6e6aef 100644
--- a/oox/source/core/xmlfilterbase.cxx
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -62,6 +62,7 @@
 #include <oox/core/filterdetect.hxx>
 #include <comphelper/storagehelper.hxx>
 #include <comphelper/sequence.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
 
 #include <oox/crypto/DocumentEncryption.hxx>
 #include <tools/date.hxx>
@@ -961,13 +962,6 @@ void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStora
     Reference<XRelationshipAccess> xRelations(xDocumentStorage, UNO_QUERY);
     if (xRelations.is())
     {
-        // These are all the custom types we recognize and can preserve.
-        static const std::set<OUString> sCustomTypes = {
-                            "http://schemas.dell.com/ddp/2016/relationships/xenFile",
-                            "http://schemas.dell.com/ddp/2016/relationships/hmacFile",
-                            "http://schemas.dell.com/ddp/2016/relationships/metadataFile"
-                        };
-
         uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
 
         std::vector<StreamDataSequence> aCustomFragments;
@@ -987,7 +981,8 @@ void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStora
                     sType = aPair.Second;
             }
 
-            if (sCustomTypes.find(sType) != sCustomTypes.end())
+            // Preserve non-standard (i.e. custom) entries.
+            if (!sType.match("http://schemas.openxmlformats.org"))
             {
                 StreamDataSequence aDataSeq;
                 if (importBinaryData(aDataSeq, sTarget))
@@ -1009,7 +1004,7 @@ void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStora
         std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomPropsList;
         //FIXME: Ideally, we should get these the relations, but it seems that is not consistently set.
         // In some cases it's stored in the workbook relationships, which is unexpected. So we discover them directly.
-        for (int i = 1; i < 100; ++i)
+        for (int i = 1; ; ++i)
         {
             Reference<XDocument> xCustDoc = importFragment("customXml/item" + OUString::number(i) + ".xml");
             Reference<XDocument> xCustDocProps = importFragment("customXml/itemProps" + OUString::number(i) + ".xml");
@@ -1026,6 +1021,14 @@ void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStora
         aGrabBagProperties["OOXCustomXml"] <<= comphelper::containerToSequence(aCustomXmlDomList);
         aGrabBagProperties["OOXCustomXmlProps"] <<= comphelper::containerToSequence(aCustomXmlDomPropsList);
 
+        // Save the [Content_Types].xml after parsing.
+        uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo;
+        uno::Reference<io::XInputStream> xInputStream = openInputStream("[Content_Types].xml");
+        if (xInputStream.is())
+            aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, getComponentContext());
+
+        aGrabBagProperties["OOXContentTypes"] <<= aContentTypeInfo;
+
         Reference<XComponent> xModel(getModel(), UNO_QUERY);
         oox::core::XmlFilterBase::putPropertiesToDocumentGrabBag(xModel, aGrabBagProperties);
     }
@@ -1046,6 +1049,7 @@ void XmlFilterBase::exportCustomFragments()
     uno::Sequence<StreamDataSequence> customFragments;
     uno::Sequence<OUString> customFragmentTypes;
     uno::Sequence<OUString> customFragmentTargets;
+    uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypes;
 
     uno::Sequence<beans::PropertyValue> propList;
     xPropSet->getPropertyValue(aName) >>= propList;
@@ -1072,6 +1076,10 @@ void XmlFilterBase::exportCustomFragments()
         {
             propList[nProp].Value >>= customFragmentTargets;
         }
+        else if (propName == "OOXContentTypes")
+        {
+            propList[nProp].Value >>= aContentTypes;
+        }
     }
 
     // Expect customXmlDomPropslist.getLength() == customXmlDomlist.getLength().
@@ -1111,10 +1119,16 @@ void XmlFilterBase::exportCustomFragments()
     for (sal_Int32 j = 0; j < customFragments.getLength(); j++)
     {
         addRelation(customFragmentTypes[j], customFragmentTargets[j]);
-        Reference<XOutputStream> xOutStream = openOutputStream(customFragmentTargets[j]);
+        const OUString aFilename = customFragmentTargets[j];
+        Reference<XOutputStream> xOutStream = openOutputStream(aFilename);
         xOutStream->writeBytes(customFragments[j]);
-        // BinaryXInputStream aInStrm(openOutputStream(customFragmentTargets[j]), true);
-        // aInStrm.copyToStream(xOutputStream);
+        uno::Reference<XPropertySet> xProps(xOutStream, uno::UNO_QUERY);
+        if (xProps.is())
+        {
+            const OUString aType = comphelper::OFOPXMLHelper::GetContentTypeByName(aContentTypes, aFilename);
+            const OUString aContentType = (aType.getLength() ? aType : OUString("application/octet-stream"));
+            xProps->setPropertyValue("MediaType", uno::makeAny(aContentType));
+        }
     }
 }
 
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index 3d2c0ff00e4a..f7464ed8f30f 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -1085,11 +1085,17 @@ void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const vector< uno:
 
     // Convert vector into a uno::Sequence
     // TODO/LATER: use Default entries in future
-    uno::Sequence< beans::StringPair > aDefaultsSequence(aManList.size());
-    // Add at least the application/xml default entry.
+    uno::Sequence< beans::StringPair > aDefaultsSequence(4);
+    // Add at least the standard default entries.
+    sal_Int32 nDefSeqLength = 1;
     aDefaultsSequence[0].First = "xml";
     aDefaultsSequence[0].Second= "application/xml";
-    sal_Int32 nDefSeqLength = 1;
+    aDefaultsSequence[1].First = "rels";
+    aDefaultsSequence[1].Second= "application/vnd.openxmlformats-package.relationships+xml";
+    aDefaultsSequence[2].First = "png";
+    aDefaultsSequence[2].Second= "image/png";
+    aDefaultsSequence[3].First = "jpeg";
+    aDefaultsSequence[3].Second= "image/jpeg";
 
     uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
     sal_Int32 nOverSeqLength = 0;
commit cf256ad5858bea8834cb5ce49e4f76bb0b14d7dd
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Mon Feb 26 07:46:09 2018 -0500

    oox: unit-tests for custom package preservation
    
    Change-Id: I69d0d7d6bdc8804d0e56be19cd86a699200fc85f
    Reviewed-on: https://gerrit.libreoffice.org/50855
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    (cherry picked from commit 4de1c0223ceb76556ff1c20000b4ea95bfc1d2a0)

diff --git a/sc/qa/unit/data/xlsx/customxml.xlsx b/sc/qa/unit/data/xlsx/customxml.xlsx
new file mode 100644
index 000000000000..53619ae4af30
Binary files /dev/null and b/sc/qa/unit/data/xlsx/customxml.xlsx differ
diff --git a/sc/qa/unit/helper/xpath.cxx b/sc/qa/unit/helper/xpath.cxx
index 2d047b60bf61..562facc84f1e 100644
--- a/sc/qa/unit/helper/xpath.cxx
+++ b/sc/qa/unit/helper/xpath.cxx
@@ -25,14 +25,19 @@ xmlDocPtr XPathHelper::parseExport(ScDocShell& rShell, uno::Reference<lang::XMul
     return parseExport(pTempFile, xSFactory, rFile);
 }
 
-xmlDocPtr XPathHelper::parseExport(std::shared_ptr<utl::TempFile> const & pTempFile, uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile)
+std::shared_ptr<SvStream> XPathHelper::parseExportStream(std::shared_ptr<utl::TempFile> const & pTempFile, uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile)
 {
     // Read the XML stream we're interested in.
     uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(xSFactory), pTempFile->GetURL());
     uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(rFile), uno::UNO_QUERY);
     CPPUNIT_ASSERT(xInputStream.is());
     std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+    return pStream;
+}
 
+xmlDocPtr XPathHelper::parseExport(std::shared_ptr<utl::TempFile> const & pTempFile, uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile)
+{
+    std::shared_ptr<SvStream> pStream = parseExportStream(pTempFile, xSFactory, rFile);
     return XmlTestTools::parseXmlStream(pStream.get());
 }
 
diff --git a/sc/qa/unit/helper/xpath.hxx b/sc/qa/unit/helper/xpath.hxx
index 6f8c8aa3c170..ddffd31a4585 100644
--- a/sc/qa/unit/helper/xpath.hxx
+++ b/sc/qa/unit/helper/xpath.hxx
@@ -48,6 +48,12 @@ namespace XPathHelper
             const OUString& rFile, sal_Int32 nFormat);
 
     /**
+     * Tries to parse the specified file in the temp file zip container as a binary file.
+     */
+    SCQAHELPER_DLLPUBLIC std::shared_ptr<SvStream> parseExportStream(std::shared_ptr<utl::TempFile> const & pTempFile,
+            uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile);
+
+    /**
      * Tries to parse the specified file in the temp file zip container as an xml file.
      *
      * Should be used when the same exported file is used for testing different files in
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 122fad288343..38aa2e10e955 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -162,6 +162,7 @@ public:
     void testCeilingFloorXLS();
     void testCeilingFloorODS();
 
+    void testCustomXml();
 
 #if !defined _WIN32
     void testRelativePathsODS();
@@ -268,6 +269,7 @@ public:
     CPPUNIT_TEST(testCeilingFloorODSToXLSX);
     CPPUNIT_TEST(testCeilingFloorXLS);
     CPPUNIT_TEST(testCeilingFloorODS);
+    CPPUNIT_TEST(testCustomXml);
 #if !defined(_WIN32)
     CPPUNIT_TEST(testRelativePathsODS);
 #endif
@@ -3030,6 +3032,21 @@ void ScExportTest::testCeilingFloorODS()
     testCeilingFloor(FORMAT_ODS);
 }
 
+void ScExportTest::testCustomXml()
+{
+    // Load document and export it to a temporary file
+    ScDocShellRef xShell = loadDoc("customxml.", FORMAT_XLSX);
+    CPPUNIT_ASSERT_MESSAGE("Failed to load the document.", xShell.is());
+
+    std::shared_ptr<utl::TempFile> pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_XLSX);
+    xmlDocPtr pXmlDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "customXml/item1.xml");
+    CPPUNIT_ASSERT(pXmlDoc);
+    xmlDocPtr pRelsDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "customXml/_rels/item1.xml.rels");
+    CPPUNIT_ASSERT(pRelsDoc);
+
+    std::shared_ptr<SvStream> pStream = XPathHelper::parseExportStream(pXPathFile, m_xSFactory, "ddp/ddpfile.xen");
+    CPPUNIT_ASSERT(pStream);
+}
 
 #if !defined _WIN32
 void ScExportTest::testRelativePathsODS()
diff --git a/sd/qa/unit/data/pptx/customxml.pptx b/sd/qa/unit/data/pptx/customxml.pptx
new file mode 100644
index 000000000000..ca9e8fe70d8f
Binary files /dev/null and b/sd/qa/unit/data/pptx/customxml.pptx differ
diff --git a/sd/qa/unit/export-tests-ooxml1.cxx b/sd/qa/unit/export-tests-ooxml1.cxx
index a31ff8d350e7..e5076ab1c94d 100644
--- a/sd/qa/unit/export-tests-ooxml1.cxx
+++ b/sd/qa/unit/export-tests-ooxml1.cxx
@@ -101,6 +101,7 @@ public:
     void testParaMarginAndindentation();
     void testTdf111884();
     void testTdf112633();
+    void testCustomXml();
 
     CPPUNIT_TEST_SUITE(SdOOXMLExportTest1);
 
@@ -130,6 +131,7 @@ public:
     CPPUNIT_TEST(testParaMarginAndindentation);
     CPPUNIT_TEST(testTdf111884);
     CPPUNIT_TEST(testTdf112633);
+    CPPUNIT_TEST(testCustomXml);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -831,6 +833,27 @@ void SdOOXMLExportTest1::testTdf112633()
     CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName("ppt/media/hdphoto1.wdp")));
 }
 
+void SdOOXMLExportTest1::testCustomXml()
+{
+    // Load document and export it to a temporary file
+    ::sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/pptx/customxml.pptx"), PPTX);
+    utl::TempFile tempFile;
+    xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
+    xDocShRef->DoClose();
+
+    xmlDocPtr pXmlDoc = parseExport(tempFile, "customXml/item1.xml");
+    CPPUNIT_ASSERT(pXmlDoc);
+    xmlDocPtr pRelsDoc = parseExport(tempFile, "customXml/_rels/item1.xml.rels");
+    CPPUNIT_ASSERT(pRelsDoc);
+
+    // Check there is a relation to itemProps1.xml.
+    const OUString sXmlPath = "/rels:Relationships/rels:Relationship[@Id='rId1']";
+    assertXPath(pRelsDoc, OUStringToOString(sXmlPath, RTL_TEXTENCODING_UTF8), "Target", "itemProps1.xml");
+
+    std::shared_ptr<SvStream> pStream = parseExportStream(tempFile, "ddp/ddpfile.xen");
+    CPPUNIT_ASSERT(pStream);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest1);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sd/qa/unit/sdmodeltestbase.hxx b/sd/qa/unit/sdmodeltestbase.hxx
index 49aa48e2a552..89381f2747bb 100644
--- a/sd/qa/unit/sdmodeltestbase.hxx
+++ b/sd/qa/unit/sdmodeltestbase.hxx
@@ -376,17 +376,22 @@ class SdModelTestBaseXML
 {
 
 public:
-    xmlDocPtr parseExport(utl::TempFile const & rTempFile, OUString const& rStreamName)
+    std::shared_ptr<SvStream> parseExportStream(utl::TempFile const & rTempFile, const OUString& rStreamName)
     {
+        // Read the stream we're interested in.
         OUString const url(rTempFile.GetURL());
-        uno::Reference<packages::zip::XZipFileAccess2> const xZipNames(
-            packages::zip::ZipFileAccess::createWithURL(
-                comphelper::getComponentContext(m_xSFactory), url));
-        uno::Reference<io::XInputStream> const xInputStream(
-            xZipNames->getByName(rStreamName), uno::UNO_QUERY);
-        std::unique_ptr<SvStream> const pStream(
-            utl::UcbStreamHelper::CreateStream(xInputStream, true));
+        uno::Reference<packages::zip::XZipFileAccess2> const xZipNames(packages::zip::ZipFileAccess::createWithURL(
+                                                                        comphelper::getComponentContext(m_xSFactory), url));
+        uno::Reference<io::XInputStream> const xInputStream(xZipNames->getByName(rStreamName), uno::UNO_QUERY);
+        std::shared_ptr<SvStream> const pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+        return pStream;
+    }
+
+    xmlDocPtr parseExport(utl::TempFile const & rTempFile, OUString const& rStreamName)
+    {
+        std::shared_ptr<SvStream> const pStream(parseExportStream(rTempFile, rStreamName));
         xmlDocPtr const pXmlDoc = parseXmlStream(pStream.get());
+        OUString const url(rTempFile.GetURL());
         pXmlDoc->name = reinterpret_cast<char *>(xmlStrdup(
             reinterpret_cast<xmlChar const *>(OUStringToOString(url, RTL_TEXTENCODING_UTF8).getStr())));
         return pXmlDoc;
diff --git a/sw/qa/extras/inc/swmodeltestbase.hxx b/sw/qa/extras/inc/swmodeltestbase.hxx
index 3b5427fd7089..1d6269f0b502 100644
--- a/sw/qa/extras/inc/swmodeltestbase.hxx
+++ b/sw/qa/extras/inc/swmodeltestbase.hxx
@@ -801,12 +801,19 @@ protected:
         return parseXmlStream(maTempFile.GetStream(StreamMode::READ));
     }
 
-    xmlDocPtr parseExportInternal( const OUString& url, const OUString& rStreamName )
+    std::shared_ptr<SvStream> parseExportStream(const OUString& url, const OUString& rStreamName)
     {
-        // Read the XML stream we're interested in.
+        // Read the stream we're interested in.
         uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), url);
         uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(rStreamName), uno::UNO_QUERY);
+        CPPUNIT_ASSERT(xInputStream.is());
         std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+        return pStream;
+    }
+
+    xmlDocPtr parseExportInternal(const OUString& url, const OUString& rStreamName)
+    {
+        std::shared_ptr<SvStream> pStream(parseExportStream(url, rStreamName));
 
         xmlDocPtr pXmlDoc = parseXmlStream(pStream.get());
         pXmlDoc->name = reinterpret_cast<char *>(xmlStrdup(reinterpret_cast<xmlChar const *>(OUStringToOString(url, RTL_TEXTENCODING_UTF8).getStr())));
commit 3558a6b712c5d53b3a51c8bb711e1d20be61d2a2
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Mar 7 18:30:22 2018 +0100

    sc lok: Let the tiled rendering draw the List Validation dropdowns.
    
    Change-Id: I84530cdda296dc51ceb0fd695af19211631508df
    Reviewed-on: https://gerrit.libreoffice.org/50909
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Marco Cecchetti <mrcekets at gmail.com>
    (cherry picked from commit d48840e5df14e8204668b06954f59d270c628684)

diff --git a/sc/source/ui/cctrl/cbuttonw.cxx b/sc/source/ui/cctrl/cbuttonw.cxx
index d354b933d9bc..7d8760661a66 100644
--- a/sc/source/ui/cctrl/cbuttonw.cxx
+++ b/sc/source/ui/cctrl/cbuttonw.cxx
@@ -17,6 +17,7 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <comphelper/lok.hxx>
 #include <vcl/outdev.hxx>
 #include <vcl/window.hxx>
 #include <vcl/decoview.hxx>
@@ -62,7 +63,8 @@ void ScDDComboBoxButton::Draw( const Point& rAt,
 
     tools::Rectangle   aBtnRect( rAt, rSize );
 
-    pOut->EnableMapMode( false );
+    if (!comphelper::LibreOfficeKit::isActive())
+        pOut->EnableMapMode(false);
 
     DecorationView aDecoView( pOut);
 
commit 756012d1b4f1d3420ffb0ca6b25621cdf73df69e
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Mar 6 19:21:48 2018 +0530

    lokdialog: Fix cursor invalidates for some vcl controls
    
    ... like TextEdit, etc. The problem was that the code assumed that there
    would be a mpData->mpWindow whenever a new cursor position is set. While
    that's the case with most views, some controls set the position when
    there's no window set.
    
    With this patch, we send the cursor_invalidate just before we make the
    cursor visible; by that time, we already have a valid mpWindow set in
    the ImplCursorData.
    
    Change-Id: I2cb40ae150e4d7555f17ebbb8e08c04fc05f447b
    Reviewed-on: https://gerrit.libreoffice.org/50834
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: pranavk <pranavk at collabora.co.uk>
    (cherry picked from commit 843f8e43e1e79bbd22cadabb54022c615c7a0d64)

diff --git a/vcl/source/window/cursor.cxx b/vcl/source/window/cursor.cxx
index 9f4d4a872d07..b9ae42d03585 100644
--- a/vcl/source/window/cursor.cxx
+++ b/vcl/source/window/cursor.cxx
@@ -189,9 +189,6 @@ void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool bRestore )
                 mpData->mbCurVisible = false;
                 mpData->maTimer.SetInvokeHandler( LINK( this, Cursor, ImplTimerHdl ) );
                 mpData->maTimer.SetDebugName( "vcl ImplCursorData maTimer" );
-
-                // tell about "initial" coordinates
-                LOKNotify( pWindow, "cursor_invalidate" );
             }
 
             mpData->mpWindow    = pWindow;
@@ -206,6 +203,7 @@ void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool bRestore )
                     mpData->maTimer.Start();
                 else if ( !mpData->mbCurVisible )
                     ImplDraw();
+                LOKNotify( pWindow, "cursor_invalidate" );
                 LOKNotify( pWindow, "cursor_visible" );
             }
         }
commit fd52f2ac76d357b7ab09e3d41269605b752cf560
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Wed Mar 7 06:45:51 2018 +0100

    Don't return local variable address
    
    Oversight in 2f061dad7f875f704e3744fc5780c1d145b22e9f
    
    Change-Id: I4cd4fcab7f5fa87f49ecc193a3f481fb9ac33932
    Reviewed-on: https://gerrit.libreoffice.org/50857
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    (cherry picked from commit 01c71fba5f525b035f8a723215669d499bc27a3f)

diff --git a/shell/source/win32/simplemail/smplmailclient.cxx b/shell/source/win32/simplemail/smplmailclient.cxx
index 8568ae87a908..c3e4a3ccd655 100644
--- a/shell/source/win32/simplemail/smplmailclient.cxx
+++ b/shell/source/win32/simplemail/smplmailclient.cxx
@@ -170,7 +170,7 @@ namespace {
 // senddoc process lifetime. So we use base temppath for the attachments,
 // and let the senddoc to do the cleanup if it was started successfully.
 // This function works like Desktop::CreateTemporaryDirectory()
-OUString&& InitBaseTempDirURL()
+OUString InitBaseTempDirURL()
 {
     // No need to intercept an exception here, since
     // Desktop::CreateTemporaryDirectory() has ensured that path manager is available
@@ -183,7 +183,7 @@ OUString&& InitBaseTempDirURL()
     if (aRetURL.endsWith("/"))
         aRetURL = aRetURL.copy(0, aRetURL.getLength() - 1);
 
-    return std::move(aRetURL);
+    return aRetURL;
 }
 
 const OUString& GetBaseTempDirURL()
commit f05e9233904b48cf71a95e01a6f0d0c862f5a532
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Tue Feb 27 23:40:15 2018 +0300

    tdf#116074: Don't block on sending email interactively
    
    When sending e-mail using a MAPI mail client that doesn't recognize
    MAPI_DIALOG_MODELESS flag, and doesn't return from MAPISendMail until
    message compose dialog is closed (like MS Outlook 2010 and older),
    waiting for the senddoc process blocks UI, which is unexpected and
    prevents users from copying stuff from documents to the mail body.
    
    Waiting for senddoc process completion is used for two things:
    1. To serialize sending multiple mails (e.g., using mailmerge);
    2. To show error in case when it failed.
    
    This patch allows to avoid blocking the UI in case when compose UI is
    requested - i.e., user interaction with the mail client is expected,
    and serialization is not required. In this case, the senddoc process
    will show the error message itself -> no need for main application to
    wait for its return. The error message now includes actual error code.
    
    To avoid cases when closing main program would remove temporary
    attachment files before they were used by mail client, they are
    copied to base temporary directory (instead of default session
    temporary directory that gets deleted upon program shutdown).
    senddoc cleans up its temporaries itself.
    
    The temporary attachment files are copied to files with ASCII-only
    filenames, and their original filenames are passed to mail clients
    using MAPI. This allows to avoid cases when the filenames contain
    characters outside of current Windows codepage, and the mail client
    does not support Unicode MAPI, thus receiving wrong filename and
    erroring out from the send.
    
    Reviewed-on: https://gerrit.libreoffice.org/50826
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    (cherry picked from commit 2f061dad7f875f704e3744fc5780c1d145b22e9f)
    
    Change-Id: I4a517bd7a797e76e4c0b7ea48bb1a7b652741a81

diff --git a/include/sfx2/strings.hrc b/include/sfx2/strings.hrc
index 17d3c2a1859d..597afd21836f 100644
--- a/include/sfx2/strings.hrc
+++ b/include/sfx2/strings.hrc
@@ -235,6 +235,9 @@
 #define STR_PRINT_NEWORISIZE                    NC_("STR_PRINT_NEWORISIZE", "The page size and orientation have been modified.\nWould you like to save the new settings in the\nactive document?")
 #define STR_CANT_CLOSE                          NC_("STR_CANT_CLOSE", "The document cannot be closed because a\n print job is being carried out.")
 #define STR_ERROR_SEND_MAIL                     NC_("STR_ERROR_SEND_MAIL", "An error occurred in sending the message. Possible errors could be a missing user account or a defective setup.\nPlease check the %PRODUCTNAME settings or your e-mail program settings.")
+// Error codes look like "MAPI_E_FAILURE" or "1234"
+#define STR_ERROR_SEND_MAIL_CODE                NC_("STR_ERROR_SEND_MAIL_CODE", "An error occurred in sending the message. Possible errors could be a missing user account or a defective setup.\n\nError code is $1")
+#define STR_ERROR_SEND_MAIL_HEADER              NC_("STR_ERROR_SEND_MAIL_HEADER", "Error sending mail")
 #define STR_QUERY_OPENASTEMPLATE                NC_("STR_QUERY_OPENASTEMPLATE", "This document cannot be edited, possibly due to missing access rights. Do you want to edit a copy of the document?")
 #define STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE   NC_("STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE", "This document cannot be edited, because it is locked in another session. Do you want to edit a copy of the document?\n\nYou can also try to ignore the lock and open the file for editing.")
 #define STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN   NC_("STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN", "Open ~Copy")
diff --git a/shell/Executable_senddoc.mk b/shell/Executable_senddoc.mk
index f51c8441aada..4b8c5e3d18bb 100644
--- a/shell/Executable_senddoc.mk
+++ b/shell/Executable_senddoc.mk
@@ -13,8 +13,12 @@ $(eval $(call gb_Executable_use_system_win32_libs,senddoc,\
 	kernel32 \
 ))
 
+$(eval $(call gb_Executable_use_sdk_api,senddoc))
+
 $(eval $(call gb_Executable_use_libraries,senddoc,\
+	i18nlangtag \
 	sal \
+	utl \
 ))
 
 $(eval $(call gb_Executable_add_exception_objects,senddoc,\
diff --git a/shell/Library_smplmail.mk b/shell/Library_smplmail.mk
index d68f0a65e8ec..fa8c222943ed 100644
--- a/shell/Library_smplmail.mk
+++ b/shell/Library_smplmail.mk
@@ -18,7 +18,10 @@ $(eval $(call gb_Library_use_system_win32_libs,smplmail,\
 $(eval $(call gb_Library_use_libraries,smplmail,\
 	cppu \
 	cppuhelper \
+	i18nlangtag \
 	sal \
+	tl \
+	utl \
 ))
 
 $(eval $(call gb_Library_set_componentfile,smplmail,shell/source/win32/simplemail/smplmail))
diff --git a/shell/source/win32/simplemail/senddoc.cxx b/shell/source/win32/simplemail/senddoc.cxx
index d972bad5f71e..636a0d862f4e 100644
--- a/shell/source/win32/simplemail/senddoc.cxx
+++ b/shell/source/win32/simplemail/senddoc.cxx
@@ -20,6 +20,11 @@
 #include <osl/diagnose.h>
 #include <sal/macros.h>
 
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sfx2/strings.hrc>
+#include <unotools/resmgr.hxx>
+
 #include <wchar.h>
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -38,7 +43,6 @@
 #endif
 
 typedef std::vector<std::wstring> StringList_t;
-typedef StringList_t::const_iterator StringListIterator_t;
 typedef std::vector<MapiRecipDescW> MapiRecipientList_t;
 typedef std::vector<MapiFileDescW> MapiAttachmentList_t;
 
@@ -46,13 +50,16 @@ const int LEN_SMTP_PREFIX = 5; // "SMTP:"
 
 namespace /* private */
 {
+    OUString gLangTag;
+    OUString gBootstrap;
     std::wstring gFrom;
     std::wstring gSubject;
     std::wstring gBody;
     StringList_t gTo;
     StringList_t gCc;
     StringList_t gBcc;
-    StringList_t gAttachments;
+    // Keep temp filepath and displayed name
+    std::vector<std::pair<std::wstring, std::wstring>> gAttachments;
     int gMapiFlags = 0;
 }
 
@@ -118,10 +125,16 @@ void initAttachmentList(MapiAttachmentList_t* pMapiAttachmentList)
     {
         MapiFileDescW mfd;
         ZeroMemory(&mfd, sizeof(mfd));
-        mfd.lpszPathName = const_cast<wchar_t*>(attachment.c_str());
-        // This is required for Outlook 2013 - otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE
+        mfd.lpszPathName = const_cast<wchar_t*>(attachment.first.c_str());
+        // MapiFileDesc documentation (https://msdn.microsoft.com/en-us/library/hh707272)
+        // allows using here either nullptr, or a pointer to empty string. However,
+        // for Outlook 2013, we cannot use nullptr here, and must point to a (possibly
+        // empty) string: otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE.
         // See http://peach.ease.lsoft.com/scripts/wa-PEACH.exe?A2=MAPI-L;d2bf3060.1604
-        mfd.lpszFileName = L"";
+        // Since C++11, c_str() must return a pointer to single null character when the
+        // string is empty, so we are OK here in case when there's no explicit file name
+        // passed
+        mfd.lpszFileName = const_cast<wchar_t*>(attachment.second.c_str());
         mfd.nPosition = sal::static_int_cast<ULONG>(-1);
         pMapiAttachmentList->push_back(mfd);
     }
@@ -151,11 +164,12 @@ void initMapiMessage(
     pMapiMessage->lpOriginator = aMapiOriginator;
     pMapiMessage->lpRecips = aMapiRecipientList.size() ? &aMapiRecipientList[0] : nullptr;
     pMapiMessage->nRecipCount = aMapiRecipientList.size();
-    pMapiMessage->lpFiles = &aMapiAttachmentList[0];
+    if (!aMapiAttachmentList.empty())
+        pMapiMessage->lpFiles = &aMapiAttachmentList[0];
     pMapiMessage->nFileCount = aMapiAttachmentList.size();
 }
 
-const wchar_t* const KnownParameter[] =
+const wchar_t* const KnownParameters[] =
 {
     L"--to",
     L"--cc",
@@ -165,16 +179,16 @@ const wchar_t* const KnownParameter[] =
     L"--body",
     L"--attach",
     L"--mapi-dialog",
-    L"--mapi-logon-ui"
+    L"--mapi-logon-ui",
+    L"--langtag",
+    L"--bootstrap",
 };
 
-const size_t nKnownParameter = SAL_N_ELEMENTS(KnownParameter);
-
 /** @internal */
 bool isKnownParameter(const wchar_t* aParameterName)
 {
-    for (size_t i = 0; i < nKnownParameter; i++)
-        if (_wcsicmp(aParameterName, KnownParameter[i]) == 0)
+    for (const wchar_t* KnownParameter : KnownParameters)
+        if (_wcsicmp(aParameterName, KnownParameter) == 0)
             return true;
 
     return false;
@@ -215,13 +229,122 @@ void initParameter(int argc, wchar_t* argv[])
             else if (_wcsicmp(argv[i], L"--body") == 0)
                 gBody = argv[i+1];
             else if (_wcsicmp(argv[i], L"--attach") == 0)
-                gAttachments.push_back(argv[i+1]);
+            {
+                std::wstring sPath(argv[i + 1]);
+                // An attachment may optionally be immediately followed by --attach-name and user-visible name
+                std::wstring sName;
+                if ((i + 3) < argc && _wcsicmp(argv[i+2], L"--attach-name") == 0)
+                {
+                    sName = argv[i+3];
+                    i += 2;
+                }
+                gAttachments.emplace_back(sPath, sName);
+            }
+            else if (_wcsicmp(argv[i], L"--langtag") == 0)
+                gLangTag = o3tl::toU(argv[i+1]);
+            else if (_wcsicmp(argv[i], L"--bootstrap") == 0)
+                gBootstrap = o3tl::toU(argv[i+1]);
 
             i++;
         }
     }
 }
 
+void ShowError(ULONG nMAPIResult)
+{
+    if (!gBootstrap.isEmpty())
+        rtl::Bootstrap::setIniFilename(gBootstrap);
+    LanguageTag aLangTag(gLangTag);
+    std::locale aLocale = Translate::Create("sfx", aLangTag);
+    OUString sMessage = Translate::get(STR_ERROR_SEND_MAIL_CODE, aLocale);
+    OUString sErrorId;
+    switch (nMAPIResult)
+    {
+    case MAPI_E_FAILURE:
+        sErrorId = "MAPI_E_FAILURE";
+        break;
+    case MAPI_E_LOGON_FAILURE:
+        sErrorId = "MAPI_E_LOGON_FAILURE";
+        break;
+    case MAPI_E_DISK_FULL:
+        sErrorId = "MAPI_E_DISK_FULL";
+        break;
+    case MAPI_E_INSUFFICIENT_MEMORY:
+        sErrorId = "MAPI_E_INSUFFICIENT_MEMORY";
+        break;
+    case MAPI_E_ACCESS_DENIED:
+        sErrorId = "MAPI_E_ACCESS_DENIED";
+        break;
+    case MAPI_E_TOO_MANY_SESSIONS:
+        sErrorId = "MAPI_E_ACCESS_DENIED";
+        break;
+    case MAPI_E_TOO_MANY_FILES:
+        sErrorId = "MAPI_E_TOO_MANY_FILES";
+        break;
+    case MAPI_E_TOO_MANY_RECIPIENTS:
+        sErrorId = "MAPI_E_TOO_MANY_RECIPIENTS";
+        break;
+    case MAPI_E_ATTACHMENT_NOT_FOUND:
+        sErrorId = "MAPI_E_ATTACHMENT_NOT_FOUND";
+        break;
+    case MAPI_E_ATTACHMENT_OPEN_FAILURE:
+        sErrorId = "MAPI_E_ATTACHMENT_OPEN_FAILURE";
+        break;
+    case MAPI_E_ATTACHMENT_WRITE_FAILURE:
+        sErrorId = "MAPI_E_ATTACHMENT_WRITE_FAILURE";
+        break;
+    case MAPI_E_UNKNOWN_RECIPIENT:
+        sErrorId = "MAPI_E_UNKNOWN_RECIPIENT";
+        break;
+    case MAPI_E_BAD_RECIPTYPE:
+        sErrorId = "MAPI_E_BAD_RECIPTYPE";
+        break;
+    case MAPI_E_NO_MESSAGES:
+        sErrorId = "MAPI_E_NO_MESSAGES";
+        break;
+    case MAPI_E_INVALID_MESSAGE:
+        sErrorId = "MAPI_E_INVALID_MESSAGE";
+        break;
+    case MAPI_E_TEXT_TOO_LARGE:
+        sErrorId = "MAPI_E_TEXT_TOO_LARGE";
+        break;
+    case MAPI_E_INVALID_SESSION:
+        sErrorId = "MAPI_E_INVALID_SESSION";
+        break;
+    case MAPI_E_TYPE_NOT_SUPPORTED:
+        sErrorId = "MAPI_E_TYPE_NOT_SUPPORTED";
+        break;
+    case MAPI_E_AMBIGUOUS_RECIPIENT:
+        sErrorId = "MAPI_E_AMBIGUOUS_RECIPIENT";
+        break;
+    case MAPI_E_MESSAGE_IN_USE:
+        sErrorId = "MAPI_E_MESSAGE_IN_USE";
+        break;
+    case MAPI_E_NETWORK_FAILURE:
+        sErrorId = "MAPI_E_NETWORK_FAILURE";
+        break;
+    case MAPI_E_INVALID_EDITFIELDS:
+        sErrorId = "MAPI_E_INVALID_EDITFIELDS";
+        break;
+    case MAPI_E_INVALID_RECIPS:
+        sErrorId = "MAPI_E_INVALID_RECIPS";
+        break;
+    case MAPI_E_NOT_SUPPORTED:
+        sErrorId = "MAPI_E_NOT_SUPPORTED";
+        break;
+    case MAPI_E_UNICODE_NOT_SUPPORTED:
+        sErrorId = "MAPI_E_UNICODE_NOT_SUPPORTED";
+        break;
+    default:
+        sErrorId = OUString::number(nMAPIResult);
+    }
+    sMessage = sMessage.replaceAll("$1", sErrorId);
+    OUString sTitle(Translate::get(STR_ERROR_SEND_MAIL_HEADER, aLocale));
+
+    MessageBoxW(nullptr, o3tl::toW(sMessage.getStr()), o3tl::toW(sTitle.getStr()),
+        MB_OK | MB_ICONINFORMATION);
+}
+
 /**
     Main.
     NOTE: Because this is program only serves implementation
@@ -275,6 +398,15 @@ int wmain(int argc, wchar_t* argv[])
     {
         OSL_FAIL(ex.what());
     }
+
+    // Now cleanup the temporary attachment files
+    for (const auto& rAttachment : gAttachments)
+        DeleteFileW(rAttachment.first.c_str());
+
+    // Only show the error message if UI was requested
+    if ((ulRet != SUCCESS_SUCCESS) && (gMapiFlags & (MAPI_DIALOG | MAPI_LOGON_UI)))
+        ShowError(ulRet);
+
     return ulRet;
 }
 
@@ -302,7 +434,11 @@ int wmain(int argc, wchar_t* argv[])
             oss << "--bcc " << address << std::endl;
 
         for (const auto& attachment : gAttachments)
-            oss << "--attach " << attachment << std::endl;
+        {
+            oss << "--attach " << attachment.first << std::endl;
+            if (!attachment.second.empty())
+                oss << "--attach-name " << attachment.second << std::endl;
+        }
 
         if (gMapiFlags & MAPI_DIALOG)
             oss << "--mapi-dialog" << std::endl;
@@ -310,6 +446,12 @@ int wmain(int argc, wchar_t* argv[])
         if (gMapiFlags & MAPI_LOGON_UI)
             oss << "--mapi-logon-ui" << std::endl;
 
+        if (!gLangTag.isEmpty())
+            oss << "--langtag " << gLangTag << std::endl;
+
+        if (!gBootstrap.isEmpty())
+            oss << "--bootstrap " << gBootstrap << std::endl;
+
         MessageBoxW(nullptr, oss.str().c_str(), L"Arguments", MB_OK | MB_ICONINFORMATION);
     }
 #endif
diff --git a/shell/source/win32/simplemail/smplmailclient.cxx b/shell/source/win32/simplemail/smplmailclient.cxx
index b38061c37037..8568ae87a908 100644
--- a/shell/source/win32/simplemail/smplmailclient.cxx
+++ b/shell/source/win32/simplemail/smplmailclient.cxx
@@ -28,15 +28,16 @@
 #include <com/sun/star/system/XSimpleMailMessage2.hpp>
 #include <osl/file.hxx>
 #include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/make_unique.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/syslocale.hxx>
 
 #define WIN32_LEAN_AND_MEAN
-#if defined _MSC_VER
-#pragma warning(push, 1)
-#endif
 #include <windows.h>
 #include <mapi.h>
-#if defined _MSC_VER
-#pragma warning(pop)
+#if defined GetTempPath
+#undef GetTempPath
 #endif
 
 #include <process.h>
@@ -62,8 +63,11 @@ const OUString FROM("--from");
 const OUString SUBJECT("--subject");
 const OUString BODY("--body");
 const OUString ATTACH("--attach");
+const OUString ATTACH_NAME("--attach-name");
 const OUString FLAG_MAPI_DIALOG("--mapi-dialog");
 const OUString FLAG_MAPI_LOGON_UI("--mapi-logon-ui");
+const OUString FLAG_LANGTAG("--langtag");
+const OUString FLAG_BOOTSTRAP("--bootstrap");
 
 namespace /* private */
 {
@@ -117,12 +121,14 @@ namespace /* private */
         @returns
         <TRUE/> on success.
     */
-    bool executeSenddoc(const std::vector<OUString>& rCommandArgs)
+    bool executeSenddoc(const std::vector<OUString>& rCommandArgs, bool bWait)
     {
         OUString senddocUrl = getSenddocUrl();
         if (senddocUrl.getLength() == 0)
             return false;
 
+        oslProcessOption nProcOption = osl_Process_DETACHED | (bWait ? osl_Process_WAIT : 0);
+
         oslProcess proc;
 
         /* for efficiency reasons we are using a 'bad' cast here
@@ -132,7 +138,7 @@ namespace /* private */
             senddocUrl.pData,
             const_cast<rtl_uString**>(reinterpret_cast<rtl_uString * const *>(&rCommandArgs[0])),
             rCommandArgs.size(),
-            osl_Process_WAIT | osl_Process_DETACHED,
+            nProcOption,
             nullptr,
             nullptr,
             nullptr,
@@ -142,6 +148,9 @@ namespace /* private */
         if (err != osl_Process_E_None)
             return false;
 
+        if (!bWait)
+            return true;
+
         oslProcessInfo procInfo;
         procInfo.Size = sizeof(oslProcessInfo);
         osl_getProcessInfo(proc, osl_Process_EXITCODE, &procInfo);
@@ -155,6 +164,76 @@ Reference<XSimpleMailMessage> SAL_CALL CSmplMailClient::createSimpleMailMessage(
     return Reference<XSimpleMailMessage>(new CSmplMailMsg());
 }
 
+namespace {
+// We cannot use the session-local temporary directory for the attachment,
+// because it will get removed upon program exit; and it must be alive for
+// senddoc process lifetime. So we use base temppath for the attachments,
+// and let the senddoc to do the cleanup if it was started successfully.
+// This function works like Desktop::CreateTemporaryDirectory()
+OUString&& InitBaseTempDirURL()
+{
+    // No need to intercept an exception here, since
+    // Desktop::CreateTemporaryDirectory() has ensured that path manager is available
+    SvtPathOptions aOpt;
+    OUString aRetURL = aOpt.GetTempPath();
+    if (aRetURL.isEmpty())
+    {
+        osl::File::getTempDirURL(aRetURL);
+    }
+    if (aRetURL.endsWith("/"))
+        aRetURL = aRetURL.copy(0, aRetURL.getLength() - 1);
+
+    return std::move(aRetURL);
+}
+
+const OUString& GetBaseTempDirURL()
+{
+    static const OUString aRetURL(InitBaseTempDirURL());
+    return aRetURL;
+}
+}
+
+OUString CSmplMailClient::CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName)
+{
+    // We do two things here:
+    // 1. Make the attachment temporary filename to not contain any fancy characters possible in
+    // original filename, that could confuse mailer, and extract the original filename to explicitly
+    // define it;
+    // 2. Allow the copied files be outside of the session's temporary directory, and thus not be
+    // removed in Desktop::RemoveTemporaryDirectory() if soffice process gets closed before the
+    // mailer finishes using them.
+
+    maAttachmentFiles.emplace_back(o3tl::make_unique<utl::TempFile>(&GetBaseTempDirURL()));
+    maAttachmentFiles.back()->EnableKillingFile();
+    INetURLObject aFilePathObj(maAttachmentFiles.back()->GetURL());
+    OUString sNewAttachmentURL = aFilePathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+    if (osl::File::copy(sOrigAttachURL, sNewAttachmentURL) == osl::FileBase::RC::E_None)
+    {
+        INetURLObject url(sOrigAttachURL, INetURLObject::EncodeMechanism::WasEncoded);
+        sUserVisibleName = url.getName(INetURLObject::LAST_SEGMENT, true,
+            INetURLObject::DecodeMechanism::WithCharset);
+    }
+    else
+    {
+        // Failed to copy original; the best effort is to use original file. It is possible that
+        // the file gets deleted before used in spawned process; but let's hope... the worst thing
+        // is the absent attachment file anyway.
+        sNewAttachmentURL = sOrigAttachURL;
+        maAttachmentFiles.pop_back();
+    }
+    return sNewAttachmentURL;
+}
+
+void CSmplMailClient::ReleaseAttachments()
+{
+    for (auto& pTempFile : maAttachmentFiles)
+    {
+        if (pTempFile)
+            pTempFile->EnableKillingFile(false);
+    }
+    maAttachmentFiles.clear();
+}
+
 /**
     Assemble a command line for SendDoc.exe out of the members
     of the supplied SimpleMailMessage.
@@ -224,11 +303,12 @@ void CSmplMailClient::assembleCommandLine(
         rCommandArgs.push_back(subject);
     }
 
-    Sequence<OUString> attachments = xSimpleMailMessage->getAttachement();
-    for (int i = 0; i < attachments.getLength(); i++)
+    for (const auto& attachment : xSimpleMailMessage->getAttachement())
     {
+        OUString sDisplayName;
+        OUString sTempFileURL(CopyAttachment(attachment, sDisplayName));
         OUString sysPath;
-        osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(attachments[i], sysPath);
+        osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(sTempFileURL, sysPath);
         if (err != osl::FileBase::E_None)
             throw IllegalArgumentException(
                 "Invalid attachment file URL",
@@ -237,6 +317,11 @@ void CSmplMailClient::assembleCommandLine(
 
         rCommandArgs.push_back(ATTACH);
         rCommandArgs.push_back(sysPath);
+        if (!sDisplayName.isEmpty())
+        {
+            rCommandArgs.push_back(ATTACH_NAME);
+            rCommandArgs.push_back(sDisplayName);
+        }
     }
 
     if (!(aFlag & NO_USER_INTERFACE))
@@ -244,6 +329,19 @@ void CSmplMailClient::assembleCommandLine(
 
     if (!(aFlag & NO_LOGON_DIALOG))
         rCommandArgs.push_back(FLAG_MAPI_LOGON_UI);
+
+    rCommandArgs.push_back(FLAG_LANGTAG);
+    rCommandArgs.push_back(SvtSysLocale().GetUILanguageTag().getBcp47());
+
+    rtl::Bootstrap aBootstrap;
+    OUString sBootstrapPath;
+    aBootstrap.getIniName(sBootstrapPath);
+    if (!sBootstrapPath.isEmpty())
+    {
+        rCommandArgs.push_back(FLAG_BOOTSTRAP);
+        rCommandArgs.push_back(sBootstrapPath);
+    }
+
 }
 
 void SAL_CALL CSmplMailClient::sendSimpleMailMessage(
@@ -254,10 +352,14 @@ void SAL_CALL CSmplMailClient::sendSimpleMailMessage(
     std::vector<OUString> senddocParams;
     assembleCommandLine(xSimpleMailMessage, aFlag, senddocParams);
 
-    if (!executeSenddoc(senddocParams))
+    const bool bWait = aFlag & NO_USER_INTERFACE;
+    if (!executeSenddoc(senddocParams, bWait))
         throw Exception(
             "Send email failed",
             static_cast<XSimpleMailClient*>(this));
+    // Let the launched senddoc to cleanup the attachments temporary files
+    if (!bWait)
+        ReleaseAttachments();
 }
 
 void CSmplMailClient::validateParameter(
diff --git a/shell/source/win32/simplemail/smplmailclient.hxx b/shell/source/win32/simplemail/smplmailclient.hxx
index 4a744c289eec..5844e99147e5 100644
--- a/shell/source/win32/simplemail/smplmailclient.hxx
+++ b/shell/source/win32/simplemail/smplmailclient.hxx
@@ -24,7 +24,9 @@
 #include <com/sun/star/lang/XServiceInfo.hpp>
 
 #include <com/sun/star/system/XSimpleMailClient.hpp>
+#include <unotools/tempfile.hxx>
 #include <vector>
+#include <memory>
 
 class CSmplMailClient : public cppu::WeakImplHelper<css::system::XSimpleMailClient>
 {
@@ -36,6 +38,11 @@ public:
 private:
     void validateParameter(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag);
     void assembleCommandLine(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag, std::vector<OUString>& rCommandArgs);
+    OUString CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName);
+    // Don't try to delete the copied attachment files; let the spawned process cleanup them
+    void ReleaseAttachments();
+
+    std::vector< std::unique_ptr<utl::TempFile> > maAttachmentFiles;
 };
 
 #endif
commit 79f1af12d7c8fe32e7ff08cce95fe04e2e0a48b9
Author: Henry Castro <hcastro at collabora.com>
Date:   Mon Mar 5 09:03:10 2018 -0400

    lokdialog: more, convert the dialog to async exec
    
    ClassificationDialog
    SwWatermarkDialog
    
    Change-Id: I835648df8df5ad3ee5a404a582c2179e5b3b276a
    Reviewed-on: https://gerrit.libreoffice.org/50771
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Henry Castro <hcastro at collabora.com>
    (cherry picked from commit 3e8d1e80d81999d2c5e160b756491a847226d423)

diff --git a/sw/source/uibase/app/docsh2.cxx b/sw/source/uibase/app/docsh2.cxx
index 83b1384aebd0..efad6a1cf4e8 100644
--- a/sw/source/uibase/app/docsh2.cxx
+++ b/sw/source/uibase/app/docsh2.cxx
@@ -1166,22 +1166,22 @@ void SwDocShell::Execute(SfxRequest& rReq)
         break;
         case SID_CLASSIFICATION_DIALOG:
         {
-            ScopedVclPtr<svx::ClassificationDialog> pDialog(VclPtr<svx::ClassificationDialog>::Create(nullptr, false));
+            VclPtr<svx::ClassificationDialog> pDialog(VclPtr<svx::ClassificationDialog>::Create(&GetView()->GetViewFrame()->GetWindow(), false));
 
             SwWrtShell* pShell = GetWrtShell();
             std::vector<svx::ClassificationResult> aInput = pShell->CollectAdvancedClassification();
             pDialog->setupValues(aInput);
 
-            if (RET_OK == pDialog->Execute())
-                pShell->ApplyAdvancedClassification(pDialog->getResult());
-
-            pDialog.disposeAndClear();
+            pDialog->StartExecuteAsync([pDialog, pShell](sal_Int32 nResult){
+                if (RET_OK == nResult)
+                    pShell->ApplyAdvancedClassification(pDialog->getResult());
+            });
         }
         break;
         case SID_PARAGRAPH_SIGN_CLASSIFY_DLG:
         {
             SwWrtShell* pShell = GetWrtShell();
-            ScopedVclPtr<svx::ClassificationDialog> pDialog(VclPtr<svx::ClassificationDialog>::Create(nullptr, true, [pShell]()
+            VclPtr<svx::ClassificationDialog> pDialog(VclPtr<svx::ClassificationDialog>::Create(&GetView()->GetViewFrame()->GetWindow(), true, [pShell]()
             {
                 pShell->SignParagraph();
             }));
@@ -1189,10 +1189,10 @@ void SwDocShell::Execute(SfxRequest& rReq)
             std::vector<svx::ClassificationResult> aInput = pShell->CollectParagraphClassification();
             pDialog->setupValues(aInput);
 
-            if (RET_OK == pDialog->Execute())
-                pShell->ApplyParagraphClassification(pDialog->getResult());
-
-            pDialog.disposeAndClear();
+            pDialog->StartExecuteAsync([pDialog, pShell](sal_Int32 nResult){
+                if (RET_OK == nResult)
+                    pShell->ApplyParagraphClassification(pDialog->getResult());
+            });
         }
         break;
         case SID_WATERMARK:
@@ -1220,9 +1220,8 @@ void SwDocShell::Execute(SfxRequest& rReq)
                 {
                     SfxViewShell* pViewShell = GetView()? GetView(): SfxViewShell::Current();
                     SfxBindings& rBindings( pViewShell->GetViewFrame()->GetBindings() );
-                    ScopedVclPtr<SwWatermarkDialog> pDlg( VclPtr<SwWatermarkDialog>::Create( nullptr, rBindings ) );
-                    pDlg->Execute();
-                    pDlg.disposeAndClear();
+                    VclPtr<SwWatermarkDialog> pDlg(VclPtr<SwWatermarkDialog>::Create(&GetView()->GetViewFrame()->GetWindow(), rBindings));
+                    pDlg->StartExecuteAsync([](sal_Int32 /*nResult*/){});
                 }
             }
         }
commit 4660d5a39a939d0b90a4588beb0072dbb8a14a99
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Mon Mar 5 07:22:14 2018 +0100

    We now use Unicode with MAPISendMailW, so no need for this
    
    Change-Id: I6f03b4212a3434cb5feae8da29107c5f411218e3
    Reviewed-on: https://gerrit.libreoffice.org/50759
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    (cherry picked from commit af269d967be348b813ea6d9e5e92a95886143622)

diff --git a/sfx2/source/dialog/mailmodel.cxx b/sfx2/source/dialog/mailmodel.cxx
index 00fdcb0a2264..42dd693d3c0c 100644
--- a/sfx2/source/dialog/mailmodel.cxx
+++ b/sfx2/source/dialog/mailmodel.cxx
@@ -750,11 +750,7 @@ SfxMailModel::SendMailResult SfxMailModel::Send( const css::uno::Reference< css:
                     OUString subject(
                         url.getBase(
                             INetURLObject::LAST_SEGMENT, false,
-#ifdef _WIN32
-                            INetURLObject::DecodeMechanism::NONE)); // MAPISendMail does not accept Unicode
-#else
                             INetURLObject::DecodeMechanism::WithCharset));
-#endif
                     if (subject.isEmpty()) {
                         subject = maAttachedDocuments[0];
                     }
commit 7a8d28d0858df133688a8a3b0496432f6cd916c9
Author: Tamas Bunth <tamas.bunth at collabora.co.uk>
Date:   Wed Feb 28 21:40:06 2018 +0100

    HSQLDB Binary import
    
    C++ implementation of reading HSQL's binary file format. This file
    contains the actual rows for the tables, represented in an AVL tree.
    
    Import starts from HsqlImporter, which calls to SchemaParser for some
    metadata (the positions of the trees in "data" file). After that it goes
    through the tree and read up the rows using HsqlRowInputStream.
    
    Finally, it uses sdbc's XPreparedStatement to insert the rows to the
    actual database.
    
    Change-Id: If4b17572e5989c218d45880bc3fd5a8820bb4101
    Reviewed-on: https://gerrit.libreoffice.org/50536
    Reviewed-by: Tamás Bunth <btomi96 at gmail.com>
    Tested-by: Tamás Bunth <btomi96 at gmail.com>
    (cherry picked from commit 60ac7418747530a006894a7941c67c5006d6158c)

diff --git a/dbaccess/Library_dbahsql.mk b/dbaccess/Library_dbahsql.mk
index f85660e6b3a8..bbcdea138a3e 100644
--- a/dbaccess/Library_dbahsql.mk
+++ b/dbaccess/Library_dbahsql.mk
@@ -27,6 +27,9 @@ $(eval $(call gb_Library_use_libraries,dbahsql,\
     sal \
     salhelper \
     dbtools \
+    ucbhelper \
+    utl \
+    tl \
 ))
 
 $(eval $(call gb_Library_add_exception_objects,dbahsql,\
@@ -35,6 +38,8 @@ $(eval $(call gb_Library_add_exception_objects,dbahsql,\
     dbaccess/source/filter/hsqldb/createparser \
     dbaccess/source/filter/hsqldb/columndef \
     dbaccess/source/filter/hsqldb/fbcreateparser \
+    dbaccess/source/filter/hsqldb/rowinputbinary \
+    dbaccess/source/filter/hsqldb/hsqlbinarynode \
 ))
 
 # vim: set noet sw=4 ts=4:
diff --git a/dbaccess/source/filter/hsqldb/columndef.hxx b/dbaccess/source/filter/hsqldb/columndef.hxx
index 4c46ac10a5ce..bded07bf5b4c 100644
--- a/dbaccess/source/filter/hsqldb/columndef.hxx
+++ b/dbaccess/source/filter/hsqldb/columndef.hxx
@@ -5,16 +5,6 @@
  * 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/.
- *
- * This file incorporates work covered by the following license notice:
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements. See the NOTICE file distributed
- *   with this work for additional information regarding copyright
- *   ownership. The ASF licenses this file to you under the Apache
- *   License, Version 2.0 (the "License"); you may not use this file
- *   except in compliance with the License. You may obtain a copy of
- *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
 #ifndef INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_COLUMNDEF_HXX
diff --git a/dbaccess/source/filter/hsqldb/createparser.hxx b/dbaccess/source/filter/hsqldb/createparser.hxx
index 4bc4bd2343b6..03532a1d197f 100644
--- a/dbaccess/source/filter/hsqldb/createparser.hxx
+++ b/dbaccess/source/filter/hsqldb/createparser.hxx
@@ -5,16 +5,6 @@
  * 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/.
- *
- * This file incorporates work covered by the following license notice:
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements. See the NOTICE file distributed
- *   with this work for additional information regarding copyright
- *   ownership. The ASF licenses this file to you under the Apache
- *   License, Version 2.0 (the "License"); you may not use this file
- *   except in compliance with the License. You may obtain a copy of
- *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
 #ifndef INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_CREATEPARSER_HXX
diff --git a/dbaccess/source/filter/hsqldb/fbcreateparser.hxx b/dbaccess/source/filter/hsqldb/fbcreateparser.hxx
index 02255089c1ca..fec8c6f9c34b 100644
--- a/dbaccess/source/filter/hsqldb/fbcreateparser.hxx
+++ b/dbaccess/source/filter/hsqldb/fbcreateparser.hxx
@@ -5,16 +5,6 @@
  * 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/.
- *
- * This file incorporates work covered by the following license notice:
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements. See the NOTICE file distributed
- *   with this work for additional information regarding copyright
- *   ownership. The ASF licenses this file to you under the Apache
- *   License, Version 2.0 (the "License"); you may not use this file
- *   except in compliance with the License. You may obtain a copy of
- *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
 #ifndef INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_FBCREATEPARSER_HXX
diff --git a/dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx b/dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx
new file mode 100644
index 000000000000..8a8cf1a9a20b
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/hsqlbinarynode.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "hsqlbinarynode.hxx"
+#include "rowinputbinary.hxx"
+
+#include <cppuhelper/implbase.hxx>
+#include <vector>
+
+namespace dbahsql
+{
+using ColumnTypeVector = std::vector<sal_Int32>;
+
+HsqlBinaryNode::HsqlBinaryNode(sal_Int32 nPos)
+    : m_nPos(nPos)
+{
+}
+
+void HsqlBinaryNode::readChildren(HsqlRowInputStream& input)
+{
+    SvStream* pStream = input.getInputStream();
+    if (!pStream)
+        return;
+
+    pStream->Seek(m_nPos + 8); // skip size and balance
+    pStream->ReadInt32(m_nLeft);
+    if (m_nLeft <= 0)
+        m_nLeft = -1;
+    pStream->ReadInt32(m_nRight);
+    if (m_nRight <= 0)
+        m_nRight = -1;
+}
+
+std::vector<css::uno::Any> HsqlBinaryNode::readRow(HsqlRowInputStream& input,
+                                                   const ColumnTypeVector& aColTypes)
+{
+    input.seek(m_nPos + 20); // go to data
+    return input.readOneRow(aColTypes);
+}
+
+sal_Int32 HsqlBinaryNode::getLeft() const { return m_nLeft; }
+sal_Int32 HsqlBinaryNode::getRight() const { return m_nRight; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx b/dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx
new file mode 100644
index 000000000000..7c3631a6c8b2
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/hsqlbinarynode.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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_DBACCESS_SOURCE_FILTER_HSQLDB_HSQLBINARYNODE_HXX
+#define INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_HSQLBINARYNODE_HXX
+
+#include <vector>
+#include <cppuhelper/implbase.hxx>
+
+#include "rowinputbinary.hxx"
+
+namespace dbahsql
+{
+class HsqlBinaryNode
+{
+private:
+    sal_Int32 m_nLeft = -1;
+    sal_Int32 m_nRight = -1;
+    sal_Int32 m_nPos = -1;
+
+public:
+    HsqlBinaryNode(sal_Int32 nPos);
+    void readChildren(HsqlRowInputStream& input);
+    sal_Int32 getLeft() const;
+    sal_Int32 getRight() const;
+    std::vector<css::uno::Any> readRow(HsqlRowInputStream& rInput,
+                                       const std::vector<sal_Int32>& aColTypes);
+};
+}
+
+#endif // INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_HSQLBINARYNODE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlimport.cxx b/dbaccess/source/filter/hsqldb/hsqlimport.cxx
index be0e1df4a538..c2483e694a8d 100644
--- a/dbaccess/source/filter/hsqldb/hsqlimport.cxx
+++ b/dbaccess/source/filter/hsqldb/hsqlimport.cxx
@@ -18,15 +18,156 @@
  */
 
 #include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+
 #include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XParameters.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+
+#include <comphelper/string.hxx>
 
 #include "hsqlimport.hxx"
 #include "parseschema.hxx"
+#include "rowinputbinary.hxx"
 
-namespace dbahsql
+namespace
 {
+using namespace ::comphelper;
+using namespace css::io;
 using namespace css::uno;
 using namespace css::sdbc;
+
+using ColumnTypeVector = std::vector<sal_Int32>;
+using RowVector = std::vector<Any>;
+using IndexVector = std::vector<sal_Int32>;
+
+class IndexStmtParser
+{
+private:
+    OUString m_sql;
+
+public:
+    IndexStmtParser(const OUString& sSql)
+        : m_sql(sSql)
+    {
+    }
+
+    bool isIndexStatement() const
+    {
+        return m_sql.startsWith("SET TABLE") && m_sql.indexOf("INDEX") >= 0;
+    }
+
+    IndexVector getIndexes() const
+    {
+        assert(isIndexStatement());
+
+        OUString sIndexPart = m_sql.copy(m_sql.indexOf("INDEX") + 5);
+        sal_Int32 nQuotePos = sIndexPart.indexOf("'") + 1;
+        OUString sIndexNums = sIndexPart.copy(nQuotePos, sIndexPart.lastIndexOf("'") - nQuotePos);
+
+        std::vector<OUString> sIndexes = string::split(sIndexNums, u' ');
+        IndexVector indexes;
+        for (const auto& sIndex : sIndexes)
+            indexes.push_back(sIndex.toInt32());
+
+        return indexes;
+    }
+
+    OUString getTableName() const
+    {
+        // SET TABLE <tableName>
+        return string::split(m_sql, u' ')[2];
+    }
+};
+
+void lcl_setParams(const RowVector& row, Reference<XParameters>& xParam,
+                   const ColumnTypeVector& rColTypes)
+{
+    assert(row.size() == rColTypes.size());
+    for (size_t i = 0; i < rColTypes.size(); ++i)
+    {
+        switch (rColTypes.at(i))
+        {
+            case DataType::CHAR:
+            case DataType::VARCHAR:
+            case DataType::LONGVARCHAR:
+            {
+                OUString sVal;
+                if (row.at(i) >>= sVal)
+                {
+                    xParam->setString(i + 1, sVal);
+                }
+            }
+            break;
+            case DataType::TINYINT:
+            case DataType::SMALLINT:
+            {
+                sal_Int16 nVal;
+                if (row.at(i) >>= nVal)
+                {
+                    xParam->setShort(i + 1, nVal);
+                }
+            }
+            break;
+            case DataType::INTEGER:
+            {
+                sal_Int32 nVal;
+                if (row.at(i) >>= nVal)
+                {
+                    xParam->setInt(i + 1, nVal);
+                }
+            }
+            break;
+            case DataType::BIGINT:
+                break;
+            case DataType::REAL:
+            case DataType::FLOAT:
+            case DataType::DOUBLE:
+                break;
+            case DataType::NUMERIC:
+            case DataType::DECIMAL:
+                break;
+            case DataType::DATE:
+                break;
+            case DataType::TIME:
+                break;
+            case DataType::TIMESTAMP:
+                break;
+            case DataType::BOOLEAN:
+                break;
+            case DataType::OTHER:
+                break;
+            case DataType::BINARY:
+            case DataType::VARBINARY:
+            case DataType::LONGVARBINARY:
+                break;
+            default:
+                throw WrongFormatException();
+        }
+    }
+}
+
+OUString lcl_createInsertStatement(const OUString& sTableName, sal_Int32 nColumnCount)
+{
+    assert(nColumnCount > 0);
+    OUStringBuffer sql("INSERT INTO ");
+    sql.append(sTableName);
+    sql.append(" VALUES (");
+    for (int i = 0; i < nColumnCount - 1; ++i)
+    {
+        sql.append("?,");
+    }
+    sql.append("?)");
+    return sql.makeStringAndClear();
+}
+
+} // unnamed namespace
+
+namespace dbahsql
+{
 using namespace css::embed;
 
 HsqlImporter::HsqlImporter(Reference<XConnection>& rConnection, const Reference<XStorage>& rStorage)
@@ -36,7 +177,81 @@ HsqlImporter::HsqlImporter(Reference<XConnection>& rConnection, const Reference<
     m_xStorage.set(rStorage);
 }
 
-void HsqlImporter::importSchema()
+void HsqlImporter::insertRow(const RowVector& xRows, const OUString& sTableName,
+                             const ColumnTypeVector& rColTypes)
+{
+    OUString sStatement = lcl_createInsertStatement(sTableName, xRows.size());
+    Reference<XPreparedStatement> xStatement = m_rConnection->prepareStatement(sStatement);
+
+    Reference<XParameters> xParameter(xStatement, UNO_QUERY);
+    assert(xParameter.is());
+    xParameter->clearParameters();
+
+    lcl_setParams(xRows, xParameter, rColTypes);
+    xStatement->executeQuery();
+}
+
+void HsqlImporter::processTree(HsqlBinaryNode& rNode, HsqlRowInputStream& rStream,
+                               const ColumnTypeVector& rColTypes, const OUString& sTableName)
+{
+    rNode.readChildren(rStream);
+    std::vector<Any> row = rNode.readRow(rStream, rColTypes);
+    insertRow(row, sTableName, rColTypes);
+
+    sal_Int32 nNext = rNode.getLeft();
+    if (nNext > 0)
+    {
+        HsqlBinaryNode aLeft{ nNext };
+        processTree(aLeft, rStream, rColTypes, sTableName);
+    }
+    nNext = rNode.getRight();
+    if (nNext > 0)
+    {
+        HsqlBinaryNode aRight{ nNext };
+        processTree(aRight, rStream, rColTypes, sTableName);
+    }
+}
+
+/**
+ * Format from the indexed file position is the following:
+ * <Node x20><Row>
+ * Where Node is a 20 byte data, representing the rows in a binary tree:
+ * <Size x4><Balance x4><Left x4> <Right x4><Parent x4>
+ *
+ * Size is the size of <Row>;
+ * Balance: ?
+ * Left/Right/Parent: File postition of the Left/Right/Parent child
+ */
+void HsqlImporter::parseTableRows(const IndexVector& rIndexes,
+                                  const std::vector<sal_Int32>& rColTypes,
+                                  const OUString& sTableName)
+{
+    constexpr char BINARY_FILENAME[] = "data";
+
+    if (!m_xStorage->hasByName(BINARY_FILENAME))
+    {
+        SAL_WARN("dbaccess", "data file does not exist in storage during hsqldb import");
+        assert(false); // TODO throw error
+    }
+
+    Reference<css::io::XStream> xStream(
+        m_xStorage->openStreamElement(BINARY_FILENAME, ElementModes::READ));
+
+    HsqlRowInputStream rowInput;
+    Reference<XInputStream> xInput = xStream->getInputStream();
+    rowInput.setInputStream(xInput);
+    for (const auto& index : rIndexes)
+    {
+        if (index <= 0)
+            break;
+
+        HsqlBinaryNode aNode{ index };
+        processTree(aNode, rowInput, rColTypes, sTableName);
+    }
+    xInput->closeInput();
+}
+
+void HsqlImporter::importHsqlDatabase()
 {
     assert(m_xStorage);
 
@@ -45,12 +260,26 @@ void HsqlImporter::importSchema()
 
     for (auto& sSql : statements)
     {
-        Reference<XStatement> statement = m_rConnection->createStatement();
-        statement->executeQuery(sSql);
+        // SET TABLE ... INDEX ...
+        // These statements tell us the position of the data in the binary data
+        // file
+        IndexStmtParser aIndexParser(sSql);
+        if (aIndexParser.isIndexStatement())
+        {
+            IndexVector aIndexes = aIndexParser.getIndexes();
+            OUString sTableName = aIndexParser.getTableName();
+            std::vector<sal_Int32> aColTypes = parser.getTableColumnTypes(sTableName);
+
+            parseTableRows(aIndexes, aColTypes, sTableName);
+        }
+        else
+        {
+            // other, "normal" statements
+            Reference<XStatement> statement = m_rConnection->createStatement();
+            statement->executeQuery(sSql);
+        }
     }
 }
-
-void HsqlImporter::importHsqlDatabase() { importSchema(); }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/hsqlimport.hxx b/dbaccess/source/filter/hsqldb/hsqlimport.hxx
index d4887633e184..b40f73079a7e 100644
--- a/dbaccess/source/filter/hsqldb/hsqlimport.hxx
+++ b/dbaccess/source/filter/hsqldb/hsqlimport.hxx
@@ -5,16 +5,6 @@
  * 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/.
- *
- * This file incorporates work covered by the following license notice:
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements. See the NOTICE file distributed
- *   with this work for additional information regarding copyright
- *   ownership. The ASF licenses this file to you under the Apache
- *   License, Version 2.0 (the "License"); you may not use this file
- *   except in compliance with the License. You may obtain a copy of
- *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
 #ifndef INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_HSQLIMPORT_HXX
@@ -23,6 +13,9 @@
 #include <com/sun/star/embed/XStorage.hpp>
 #include <com/sun/star/sdbc/XConnection.hpp>
 
+#include "rowinputbinary.hxx"
+#include "hsqlbinarynode.hxx"
+
 namespace dbahsql
 {
 class SAL_DLLPUBLIC_EXPORT HsqlImporter
@@ -32,7 +25,12 @@ private:
     css::uno::Reference<css::embed::XStorage> m_xStorage;
 
 protected:
-    void importSchema();
+    void insertRow(const std::vector<css::uno::Any>& xRows, const OUString& sTable,
+                   const std::vector<sal_Int32>& rColTypes);
+    void processTree(HsqlBinaryNode& rNode, HsqlRowInputStream& rStream,
+                     const std::vector<sal_Int32>& rColTypes, const OUString& sTableName);
+    void parseTableRows(const std::vector<sal_Int32>& rIndexes,
+                        const std::vector<sal_Int32>& rColTypes, const OUString& sTableName);
 
 public:
     HsqlImporter(css::uno::Reference<css::sdbc::XConnection>& rConnection,
diff --git a/dbaccess/source/filter/hsqldb/parseschema.cxx b/dbaccess/source/filter/hsqldb/parseschema.cxx
index a899fbba913e..5ee2b4d1e2c6 100644
--- a/dbaccess/source/filter/hsqldb/parseschema.cxx
+++ b/dbaccess/source/filter/hsqldb/parseschema.cxx
@@ -31,6 +31,8 @@ using namespace css::io;
 using namespace css::uno;
 using namespace css::embed;
 
+typedef std::vector<sal_Int32> ColumnTypeVector;
+
 SchemaParser::SchemaParser(Reference<XStorage>& rStorage)
     : m_rStorage(rStorage)
 {
@@ -60,16 +62,26 @@ SqlStatementVector SchemaParser::parseSchema()
         // every line contains exactly one DDL statement
         OUString sSql = xTextInput->readLine();
 
-        if (sSql.startsWith("SET") || sSql.startsWith("CREATE USER")
-            || sSql.startsWith("CREATE SCHEMA") || sSql.startsWith("GRANT"))
+        if (sSql.startsWith("SET TABLE") && sSql.indexOf("INDEX") > 0)
+        { // nothing
+        }
+        else if (sSql.startsWith("SET") || sSql.startsWith("CREATE USER")
+                 || sSql.startsWith("CREATE SCHEMA") || sSql.startsWith("GRANT"))
             continue;
-
-        if (sSql.startsWith("CREATE CACHED TABLE") || sSql.startsWith("CREATE TABLE"))
+        else if (sSql.startsWith("CREATE CACHED TABLE") || sSql.startsWith("CREATE TABLE"))
         {
             FbCreateStmtParser aCreateParser;
             aCreateParser.parse(sSql);
 
             sSql = aCreateParser.compose();
+
+            // Store columns for each table
+            ColumnTypeVector colTypes;
+            std::vector<ColumnDefinition> colDefs = aCreateParser.getColumnDef();
+            for (const auto& colDef : colDefs)
+                colTypes.push_back(colDef.getDataType());
+
+            m_ColumnTypes[aCreateParser.getTableName()] = colTypes;
         }
 
         parsedStatements.push_back(sSql);
@@ -77,6 +89,12 @@ SqlStatementVector SchemaParser::parseSchema()
 
     return parsedStatements;
 }
+
+ColumnTypeVector SchemaParser::getTableColumnTypes(const OUString& sTableName) const
+{
+    return m_ColumnTypes.at(sTableName);
 }
 
+} // namespace dbahsql
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/filter/hsqldb/parseschema.hxx b/dbaccess/source/filter/hsqldb/parseschema.hxx
index 6767ce08414a..af97ae578241 100644
--- a/dbaccess/source/filter/hsqldb/parseschema.hxx
+++ b/dbaccess/source/filter/hsqldb/parseschema.hxx
@@ -5,16 +5,6 @@
  * 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/.
- *
- * This file incorporates work covered by the following license notice:
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements. See the NOTICE file distributed
- *   with this work for additional information regarding copyright
- *   ownership. The ASF licenses this file to you under the Apache
- *   License, Version 2.0 (the "License"); you may not use this file
- *   except in compliance with the License. You may obtain a copy of
- *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
 #ifndef INCLUDED_DBACCESS_SOURCE_FILTER_HSQLDB_PARSECHEMA_HXX
@@ -23,6 +13,7 @@
 #include <com/sun/star/embed/XStorage.hpp>
 #include <com/sun/star/sdbc/XConnection.hpp>
 #include <vector>
+#include <map>
 
 namespace dbahsql
 {
@@ -33,9 +24,15 @@ class SchemaParser
 private:
     css::uno::Reference<css::embed::XStorage>& m_rStorage;
 
+    // column type for each table. It is filled after parsing schema.
+    std::map<OUString, std::vector<sal_Int32>> m_ColumnTypes;
+
 public:
     explicit SchemaParser(css::uno::Reference<css::embed::XStorage>& rStorage);
+
     SqlStatementVector parseSchema();
+
+    std::vector<sal_Int32> getTableColumnTypes(const OUString& sTableName) const;
 };
 }
 
diff --git a/dbaccess/source/filter/hsqldb/rowinputbinary.cxx b/dbaccess/source/filter/hsqldb/rowinputbinary.cxx
new file mode 100644
index 000000000000..37cfa918fef5
--- /dev/null
+++ b/dbaccess/source/filter/hsqldb/rowinputbinary.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "rowinputbinary.hxx"
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/io/XConnectable.hpp>
+
+#include <unotools/ucbstreamhelper.hxx>
+#include <tools/stream.hxx>
+
+namespace dbahsql
+{
+using namespace css::uno;
+using namespace css::sdbc;
+using namespace css::io;
+
+typedef std::vector<sal_Int32> ColumnTypeVector;
+
+HsqlRowInputStream::HsqlRowInputStream() {}
+
+void HsqlRowInputStream::setInputStream(Reference<XInputStream>& rStream)
+{
+    m_pStream.reset(utl::UcbStreamHelper::CreateStream(rStream, true));
+    m_pStream->SetEndian(SvStreamEndian::BIG);
+}
+
+SvStream* HsqlRowInputStream::getInputStream() const { return m_pStream.get(); }
+
+void HsqlRowInputStream::seek(sal_Int32 nPos) { m_pStream->Seek(nPos); }
+
+OUString HsqlRowInputStream::readString()
+{
+    sal_Int32 nLen = 0;
+    m_pStream->ReadInt32(nLen);
+    return readUTF(nLen);
+}
+
+OUString HsqlRowInputStream::readUTF(sal_Int32 nUTFLen)
+{
+    Sequence<sal_Unicode> aBuffer(nUTFLen);
+    sal_Unicode* pStr = aBuffer.getArray();
+
+    sal_Int32 nCount = 0;
+    sal_Int32 nStrLen = 0;
+    while (nCount < nUTFLen)
+    {
+        unsigned char cIn = 0;
+        m_pStream->ReadUChar(cIn);
+        sal_uInt8 c = reinterpret_cast<sal_uInt8&>(cIn);
+        sal_uInt8 char2, char3;
+        switch (c >> 4)
+        {
+            case 0:
+            case 1:
+            case 2:
+            case 3:
+            case 4:
+            case 5:
+            case 6:
+            case 7:
+                // 0xxxxxxx
+                nCount++;
+                pStr[nStrLen++] = c;
+                break;
+
+            case 12:
+            case 13:
+                // 110x xxxx   10xx xxxx
+                nCount += 2;
+                if (nCount > nUTFLen)
+                {
+                    throw WrongFormatException();
+                }
+
+                m_pStream->ReadUChar(cIn);
+                char2 = reinterpret_cast<sal_uInt8&>(cIn);
+                if ((char2 & 0xC0) != 0x80)
+                {
+                    throw WrongFormatException();
+                }
+
+                pStr[nStrLen++] = (sal_Unicode(c & 0x1F) << 6) | (char2 & 0x3F);
+                break;
+
+            case 14:
+                // 1110 xxxx  10xx xxxx  10xx xxxx
+                nCount += 3;
+                if (nCount > nUTFLen)
+                {
+                    throw WrongFormatException();
+                }
+
+                m_pStream->ReadUChar(cIn);
+                char2 = reinterpret_cast<sal_uInt8&>(cIn);
+                m_pStream->ReadUChar(cIn);
+                char3 = reinterpret_cast<sal_uInt8&>(cIn);
+
+                if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
+                {
+                    throw WrongFormatException();
+                }
+                pStr[nStrLen++] = (sal_Unicode(c & 0x0F) << 12) | (sal_Unicode(char2 & 0x3F) << 6)
+                                  | (char3 & 0x3F);
+                break;
+
+            default:
+                // 10xx xxxx,  1111 xxxx
+                throw WrongFormatException();
+        }
+    }
+    return OUString(pStr, nStrLen);
+}
+
+bool HsqlRowInputStream::checkNull()
+{
+    unsigned char cIn = 0;
+    m_pStream->ReadUChar(cIn);
+    sal_uInt8 nNull = reinterpret_cast<sal_uInt8&>(cIn);
+    return nNull == 0;
+}
+
+std::vector<Any> HsqlRowInputStream::readOneRow(const ColumnTypeVector& nColTypes)
+{
+    auto nLen = nColTypes.size();
+    std::vector<Any> aData;
+
+    for (size_t i = 0; i < nLen; ++i)
+    {
+        if (checkNull())
+        {
+            aData.push_back(Any());
+            continue;
+        }
+
+        sal_Int32 nType = nColTypes[i];
+
+        // TODO throw error on EoF
+
+        switch (nType)
+        {
+            case DataType::CHAR:
+            case DataType::VARCHAR:
+            case DataType::LONGVARCHAR:
+                aData.push_back(makeAny(readString()));
+                break;
+            case DataType::TINYINT:
+            case DataType::SMALLINT:
+            {
+                sal_Int16 value = 0;
+                m_pStream->ReadInt16(value);
+                aData.push_back(makeAny(value));
+            }
+            break;
+            case DataType::INTEGER:
+            {
+                sal_Int32 value = 0;
+                m_pStream->ReadInt32(value);
+                aData.push_back(makeAny(value));
+            }
+            break;
+            case DataType::BIGINT:
+            {
+                sal_Int64 value = 0;
+                m_pStream->ReadInt64(value);
+                aData.push_back(makeAny(value));
+            }
+            break;
+            case DataType::REAL:
+            case DataType::FLOAT:
+            case DataType::DOUBLE:
+            {
+                double value = 0;
+                m_pStream->ReadDouble(value);
+                // FIXME double is not necessarily 4 bytes
+                aData.push_back(makeAny(value));
+            }
+            break;
+            case DataType::NUMERIC:
+            case DataType::DECIMAL:
+            {
+                sal_Int32 nSize = 0;
+                m_pStream->ReadInt32(nSize);
+
+                std::vector<sal_uInt8> aBytes(nSize);
+                m_pStream->ReadBytes(aBytes.data(), nSize);
+
+                // TODO make a numeric out of this.
+            }
+            break;
+            case DataType::DATE:
+                break;
+            case DataType::TIME:
+                break;
+            case DataType::TIMESTAMP:
+                break;
+            case DataType::BOOLEAN:
+            {
+                sal_uInt8 nBool = 0;
+                m_pStream->ReadUChar(nBool);
+                aData.push_back(makeAny(static_cast<bool>(nBool)));
+            }
+            break;
+            case DataType::OTHER:
+                // TODO
+                break;
+            case DataType::BINARY:
+            case DataType::VARBINARY:
+            case DataType::LONGVARBINARY:
+            {
+                sal_Int32 nSize = 0;
+                m_pStream->ReadInt32(nSize);
+
+                Sequence<sal_uInt8> aBytes(nSize);
+                m_pStream->ReadBytes(aBytes.getArray(), nSize);
+                aData.push_back(makeAny(aBytes));
+            }
+            break;
+
+            default:
+                // TODO other exception

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list