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 <hcas...@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 <c...@libreoffice.org> Reviewed-by: Henry Castro <hcas...@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.kagan...@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 <c...@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kagan...@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 <t...@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 <c...@libreoffice.org> Reviewed-by: Tor Lillqvist <t...@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 <t...@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 <c...@libreoffice.org> Reviewed-by: Tor Lillqvist <t...@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 <ke...@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 <ke...@collabora.com> Tested-by: Jan Holesovsky <ke...@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.nakash...@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 <c...@libreoffice.org> Reviewed-by: Jan Holesovsky <ke...@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.nakash...@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 <c...@libreoffice.org> Reviewed-by: Jan Holesovsky <ke...@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 <ke...@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 <c...@libreoffice.org> Reviewed-by: Marco Cecchetti <mrcek...@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 <pran...@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 <c...@libreoffice.org> Reviewed-by: pranavk <pran...@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.kagan...@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 <c...@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kagan...@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.kagan...@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 <c...@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kagan...@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 <hcas...@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 <c...@libreoffice.org> Reviewed-by: Henry Castro <hcas...@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.kagan...@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 <c...@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kagan...@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.bu...@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 <btom...@gmail.com> Tested-by: Tamás Bunth <btom...@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 _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits