tdf#138824 tdf#137937 XLSX export: fix parent directory path

Parent directory paths (../) removed by
XclExpHyperlink::BuildFileName() resulted broken external
reference.

Note: on Linux, now this fix creates a working, but still
fragile path relative to the root directory.

Co-authored-by: Tibor Nagy (NISZ)

Change-Id: I9401d75d1fba0194d4ff509d9b7305969b8804e2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107603
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
(cherry picked from commit 107a20ee079ae852b3b33412f234aab2dc35168f)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/108006
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
diff --git a/sc/qa/unit/data/ods/childDir/tdf138824_linkToParentDirectory.ods b/sc/qa/unit/data/ods/childDir/tdf138824_linkToParentDirectory.ods
new file mode 100644
index 0000000..2f0c6d2
--- /dev/null
+++ b/sc/qa/unit/data/ods/childDir/tdf138824_linkToParentDirectory.ods
Binary files differ
diff --git a/sc/qa/unit/data/ods/tdf138824_externalSource.ods b/sc/qa/unit/data/ods/tdf138824_externalSource.ods
new file mode 100644
index 0000000..59228e3
--- /dev/null
+++ b/sc/qa/unit/data/ods/tdf138824_externalSource.ods
Binary files differ
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 35d349f..e173f85 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -274,6 +274,7 @@ public:
    void testTdf76047_externalLink();
    void testTdf87973_externalLinkSkipUnuseds();
    void testTdf138741_externalLinkSkipUnusedsCrash();
    void testTdf138824_linkToParentDirectory();
    void testTdf129969();
    void testTdf84874();
    void testTdf136721_paper_size();
@@ -449,6 +450,7 @@ public:
    CPPUNIT_TEST(testTdf76047_externalLink);
    CPPUNIT_TEST(testTdf87973_externalLinkSkipUnuseds);
    CPPUNIT_TEST(testTdf138741_externalLinkSkipUnusedsCrash);
    CPPUNIT_TEST(testTdf138824_linkToParentDirectory);
    CPPUNIT_TEST(testTdf129969);
    CPPUNIT_TEST(testTdf84874);
    CPPUNIT_TEST(testTdf136721_paper_size);
@@ -5624,7 +5626,7 @@ void ScExportTest::testTdf87973_externalLinkSkipUnuseds()
    ScDocument& rDoc = pShell->GetDocument();

    // change external link to: 87973_externalSource.ods
    OUString aFormula, bFormula;
    OUString aFormula, aFormula2;
    rDoc.GetFormula(3, 1, 0, aFormula);
    auto nIdxOfFilename = aFormula.indexOf("tdf132105_external.ods");
    aFormula = aFormula.replaceAt(nIdxOfFilename, 22, "87973_externalSource.ods");
@@ -5646,9 +5648,9 @@ void ScExportTest::testTdf87973_externalLinkSkipUnuseds()

    // check if the the new filename is present in the link (and not replaced by '[2]')
    ScDocument& rDoc2 = pDocSh->GetDocument();
    rDoc2.GetFormula(3, 1, 0, bFormula);
    CPPUNIT_ASSERT(bFormula.indexOf("tdf132105_external.ods") < 0);
    CPPUNIT_ASSERT(bFormula.indexOf("87973_externalSource.ods") > 0);
    rDoc2.GetFormula(3, 1, 0, aFormula2);
    CPPUNIT_ASSERT(aFormula2.indexOf("tdf132105_external.ods") < 0);
    CPPUNIT_ASSERT(aFormula2.indexOf("87973_externalSource.ods") >= 0);

    pDocSh->DoClose();
}
@@ -5664,6 +5666,48 @@ void ScExportTest::testTdf138741_externalLinkSkipUnusedsCrash()
    xShell->DoClose();
}

void ScExportTest::testTdf138824_linkToParentDirectory()
{
    ScDocShellRef xShell = loadDoc("childDir/tdf138824_linkToParentDirectory.", FORMAT_ODS);
    CPPUNIT_ASSERT(xShell.is());

    ScDocument& rDoc = xShell->GetDocument();

    // saveAndReload save the file to a temporary directory
    // the link must be changed to point to that parent directory
    utl::TempFile aTempFile;
    auto aTempFilename = aTempFile.GetURL();
    auto nIdxOfTmpFile = aTempFilename.lastIndexOf('/');
    nIdxOfTmpFile = aTempFilename.lastIndexOf('/', nIdxOfTmpFile);
    aTempFilename = aTempFilename.copy(0, nIdxOfTmpFile + 1);

    // change external link to tmp directory
    OUString aFormula;
    rDoc.GetFormula(3, 1, 0, aFormula);
    auto nIdxOfFilename = aFormula.indexOf("tdf138824_externalSource.ods");
    auto nIdxOfFile = aFormula.indexOf("file");

    aFormula = aFormula.replaceAt(nIdxOfFile, nIdxOfFilename - nIdxOfFile, aTempFilename);
    rDoc.SetFormula(ScAddress(3, 1, 0), aFormula, formula::FormulaGrammar::GRAM_NATIVE_UI);

    ScDocShellRef xDocSh = saveAndReload(&(*xShell), FORMAT_XLSX);
    CPPUNIT_ASSERT(xDocSh.is());

    std::shared_ptr<utl::TempFile> pXPathFile
        = ScBootstrapFixture::exportTo(&(*xDocSh), FORMAT_XLSX);
    xmlDocUniquePtr pDoc = XPathHelper::parseExport(
        pXPathFile, m_xSFactory, "xl/externalLinks/_rels/externalLink1.xml.rels");
    CPPUNIT_ASSERT(pDoc);

    // it should be "../tdf138824_externalSource.ods" but because of an other bug,
    // on linux some other directory names may added into the middle
    OUString aValue = getXPath(pDoc, "/r:Relationships/r:Relationship", "Target");
    CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(aValue.indexOf("../")));
    CPPUNIT_ASSERT(aValue.indexOf("/tdf138824_externalSource.ods") > 0);

    xDocSh->DoClose();
}

void ScExportTest::testTdf129969()
{
    ScDocShellRef xShell = loadDoc("external_hyperlink.", FORMAT_ODS);
diff --git a/sc/source/filter/excel/xelink.cxx b/sc/source/filter/excel/xelink.cxx
index affc984..cd5d570 100644
--- a/sc/source/filter/excel/xelink.cxx
+++ b/sc/source/filter/excel/xelink.cxx
@@ -1680,10 +1680,15 @@ void XclExpSupbook::SaveXml( XclExpXmlStream& rStrm )
    // Add relation for this stream, e.g. xl/externalLinks/_rels/externalLink1.xml.rels
    sal_uInt16 nLevel = 0;
    bool bRel = true;

    // BuildFileName delete ../ and convert them to nLevel
    // but addrelation needs ../ instead of nLevel, so we have to convert it back
    OUString sFile = XclExpHyperlink::BuildFileName(nLevel, bRel, maUrl, GetRoot(), true);
    while (nLevel-- > 0)
        sFile = "../" + sFile;

    OUString sId = rStrm.addRelation( pExternalLink->getOutputStream(),
            oox::getRelationship(Relationship::EXTERNALLINKPATH),
            XclExpHyperlink::BuildFileName( nLevel, bRel, maUrl, GetRoot(), true),
            true );
            oox::getRelationship(Relationship::EXTERNALLINKPATH), sFile, true );

    pExternalLink->startElement( XML_externalLink,
            XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8());