Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/doc/DocumentMetadataAccess.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
21
#include <sfx2/DocumentMetadataAccess.hxx>
22
23
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
24
#include <com/sun/star/beans/XPropertySet.hpp>
25
#include <com/sun/star/embed/ElementModes.hpp>
26
#include <com/sun/star/embed/XStorage.hpp>
27
#include <com/sun/star/embed/XTransactedObject.hpp>
28
#include <com/sun/star/frame/XTransientDocumentsDocumentContentIdentifierFactory.hpp>
29
#include <com/sun/star/task/ErrorCodeIOException.hpp>
30
#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
31
#include <com/sun/star/rdf/FileFormat.hpp>
32
#include <com/sun/star/rdf/ParseException.hpp>
33
#include <com/sun/star/rdf/RepositoryException.hpp>
34
#include <com/sun/star/rdf/URIs.hpp>
35
#include <com/sun/star/rdf/Statement.hpp>
36
#include <com/sun/star/rdf/URI.hpp>
37
#include <com/sun/star/rdf/Repository.hpp>
38
39
#include <rtl/ref.hxx>
40
#include <rtl/ustrbuf.hxx>
41
#include <rtl/uri.hxx>
42
#include <rtl/bootstrap.hxx>
43
#include <sal/log.hxx>
44
45
#include <comphelper/interaction.hxx>
46
#include <unotools/mediadescriptor.hxx>
47
#include <comphelper/sequence.hxx>
48
#include <comphelper/sequenceashashmap.hxx>
49
#include <comphelper/storagehelper.hxx>
50
#include <cppuhelper/exc_hlp.hxx>
51
#include <o3tl/string_view.hxx>
52
53
#include <sfx2/docfile.hxx>
54
#include <sfx2/XmlIdRegistry.hxx>
55
#include <sfx2/objsh.hxx>
56
#include <comphelper/diagnose_ex.hxx>
57
58
#include <libxml/tree.h>
59
60
#include <utility>
61
#include <vector>
62
#include <set>
63
#include <string_view>
64
65
#include <com/sun/star/uri/XUriReference.hpp>
66
#include <com/sun/star/uri/UriReferenceFactory.hpp>
67
68
69
/*
70
 Note: in the context of this implementation, all rdf.QueryExceptions and
71
 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
72
73
 This implementation assumes that it is only used with ODF documents, not mere
74
 ODF packages. In other words, we enforce that metadata files must not be
75
 called reserved names.
76
 */
77
78
using namespace ::com::sun::star;
79
80
namespace sfx2 {
81
82
83
bool isValidNCName(std::u16string_view i_rIdref)
84
6.14k
{
85
6.14k
    const OString id(
86
6.14k
        OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
87
6.14k
    return !(xmlValidateNCName(
88
6.14k
        reinterpret_cast<const unsigned char*>(id.getStr()), 0));
89
6.14k
}
90
91
92
constexpr OUString s_content = u"content.xml"_ustr;
93
constexpr OUString s_styles = u"styles.xml"_ustr;
94
constexpr OUString s_manifest = u"manifest.rdf"_ustr;
95
const char s_odfmime [] = "application/vnd.oasis.opendocument.";
96
97
98
static bool isContentFile(std::u16string_view i_rPath)
99
5.76k
{
100
5.76k
    return i_rPath == s_content;
101
5.76k
}
102
103
static bool isStylesFile (std::u16string_view i_rPath)
104
14
{
105
14
    return i_rPath == s_styles;
106
14
}
107
108
bool isValidXmlId(std::u16string_view i_rStreamName,
109
    std::u16string_view i_rIdref)
110
6.14k
{
111
6.14k
    return isValidNCName(i_rIdref)
112
5.76k
        && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
113
6.14k
}
114
115
static bool isReservedFile(std::u16string_view i_rPath)
116
0
{
117
0
    return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == u"meta.xml" || i_rPath == u"settings.xml";
118
0
}
119
120
121
uno::Reference<rdf::XURI> createBaseURI(
122
    uno::Reference<uno::XComponentContext> const & i_xContext,
123
    uno::Reference<frame::XModel> const & i_xModel,
124
    OUString const & i_rPkgURI, std::u16string_view i_rSubDocument)
125
6.33k
{
126
6.33k
    if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
127
0
        throw uno::RuntimeException();
128
0
    }
129
130
6.33k
    OUString pkgURI(i_rPkgURI);
131
132
    // tdf#123293 chicken/egg problem when loading from stream: there is no URI,
133
    // and also the model doesn't have a storage yet, so we need to get the
134
    // tdoc URI without a storage...
135
6.33k
    if (pkgURI.isEmpty())
136
6.33k
    {
137
6.33k
        assert(i_xModel.is());
138
6.33k
        uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
139
6.33k
            const xTDDCIF(
140
6.33k
                    i_xContext->getServiceManager()->createInstanceWithContext(
141
6.33k
                        u"com.sun.star.ucb.TransientDocumentsContentProvider"_ustr,
142
6.33k
                        i_xContext),
143
6.33k
                uno::UNO_QUERY_THROW);
144
6.33k
        uno::Reference<ucb::XContentIdentifier> const xContentId(
145
6.33k
            xTDDCIF->createDocumentContentIdentifier(i_xModel));
146
6.33k
        SAL_WARN_IF(!xContentId.is(), "sfx", "createBaseURI: cannot create ContentIdentifier");
147
6.33k
        if (!xContentId.is())
148
0
        {
149
0
            throw uno::RuntimeException(u"createBaseURI: cannot create ContentIdentifier"_ustr);
150
0
        }
151
6.33k
        pkgURI = xContentId->getContentIdentifier();
152
6.33k
        assert(!pkgURI.isEmpty());
153
6.33k
        if (!pkgURI.isEmpty() && !pkgURI.endsWith("/"))
154
6.33k
        {
155
6.33k
            pkgURI += "/";
156
6.33k
        }
157
6.33k
    }
158
159
    // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
160
    // this really should be done somewhere else, not here.
161
6.33k
    if (pkgURI.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &pkgURI))
162
0
    {
163
        // expand it here (makeAbsolute requires hierarchical URI)
164
0
        if (!pkgURI.isEmpty()) {
165
0
            pkgURI = ::rtl::Uri::decode(
166
0
                    pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
167
0
            if (pkgURI.isEmpty()) {
168
0
                throw uno::RuntimeException();
169
0
            }
170
0
            ::rtl::Bootstrap::expandMacros(pkgURI);
171
0
        }
172
0
    }
173
174
6.33k
    const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
175
6.33k
        uri::UriReferenceFactory::create( i_xContext);
176
6.33k
    uno::Reference< uri::XUriReference > xBaseURI;
177
178
6.33k
    const uno::Reference< uri::XUriReference > xPkgURI(
179
6.33k
        xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
180
6.33k
    xPkgURI->clearFragment();
181
182
    // need to know whether the storage is a FileSystemStorage
183
    // XServiceInfo would be better, but it is not implemented
184
//    if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
185
6.33k
    if (true) {
186
6.33k
        xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
187
6.33k
    }
188
6.33k
    OUStringBuffer buf(64);
189
6.33k
    if (!xBaseURI->getUriReference().endsWith("/"))
190
0
    {
191
0
        const sal_Int32 count( xBaseURI->getPathSegmentCount() );
192
0
        if (count > 0)
193
0
        {
194
0
            buf.append(xBaseURI->getPathSegment(count - 1));
195
0
        }
196
0
        buf.append('/');
197
0
    }
198
6.33k
    if (!i_rSubDocument.empty())
199
0
    {
200
0
        buf.append(OUString::Concat(i_rSubDocument) + "/");
201
0
    }
202
6.33k
    if (!buf.isEmpty())
203
0
    {
204
0
        const uno::Reference< uri::XUriReference > xPathURI(
205
0
            xUriFactory->parse(buf.makeStringAndClear()), uno::UNO_SET_THROW );
206
0
        xBaseURI.set(
207
0
            xUriFactory->makeAbsolute(xBaseURI, xPathURI,
208
0
                true, uri::RelativeUriExcessParentSegments_ERROR),
209
0
            uno::UNO_SET_THROW);
210
0
    }
211
212
6.33k
    return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
213
6.33k
}
214
215
216
struct DocumentMetadataAccess_Impl
217
{
218
    // note: these are all initialized in constructor, and loadFromStorage
219
    const uno::Reference<uno::XComponentContext> m_xContext;
220
    const SfxObjectShell & m_rXmlIdRegistrySupplier;
221
    uno::Reference<rdf::XURI> m_xBaseURI;
222
    uno::Reference<rdf::XRepository> m_xRepository;
223
    uno::Reference<rdf::XNamedGraph> m_xManifest;
224
    DocumentMetadataAccess_Impl(
225
            uno::Reference<uno::XComponentContext> i_xContext,
226
            SfxObjectShell const & i_rRegistrySupplier)
227
6.33k
      : m_xContext(std::move(i_xContext))
228
6.33k
      , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
229
6.33k
    {
230
6.33k
        OSL_ENSURE(m_xContext.is(), "context null");
231
6.33k
    }
232
};
233
234
// this is... a hack.
235
template<sal_Int16 Constant>
236
static uno::Reference<rdf::XURI> const &
237
getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
238
39.1k
{
239
39.1k
    static uno::Reference< rdf::XURI > xURI(
240
39.1k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
39.1k
    return xURI;
242
39.1k
}
DocumentMetadataAccess.cxx:com::sun::star::uno::Reference<com::sun::star::rdf::XURI> const& sfx2::getURI<(short)2008>(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&)
Line
Count
Source
238
6.33k
{
239
6.33k
    static uno::Reference< rdf::XURI > xURI(
240
6.33k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
6.33k
    return xURI;
242
6.33k
}
DocumentMetadataAccess.cxx:com::sun::star::uno::Reference<com::sun::star::rdf::XURI> const& sfx2::getURI<(short)2000>(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&)
Line
Count
Source
238
7.44k
{
239
7.44k
    static uno::Reference< rdf::XURI > xURI(
240
7.44k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
7.44k
    return xURI;
242
7.44k
}
DocumentMetadataAccess.cxx:com::sun::star::uno::Reference<com::sun::star::rdf::XURI> const& sfx2::getURI<(short)1000>(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&)
Line
Count
Source
238
6.33k
{
239
6.33k
    static uno::Reference< rdf::XURI > xURI(
240
6.33k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
6.33k
    return xURI;
242
6.33k
}
DocumentMetadataAccess.cxx:com::sun::star::uno::Reference<com::sun::star::rdf::XURI> const& sfx2::getURI<(short)2103>(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&)
Line
Count
Source
238
6.33k
{
239
6.33k
    static uno::Reference< rdf::XURI > xURI(
240
6.33k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
6.33k
    return xURI;
242
6.33k
}
DocumentMetadataAccess.cxx:com::sun::star::uno::Reference<com::sun::star::rdf::XURI> const& sfx2::getURI<(short)2104>(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&)
Line
Count
Source
238
6.33k
{
239
6.33k
    static uno::Reference< rdf::XURI > xURI(
240
6.33k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
6.33k
    return xURI;
242
6.33k
}
DocumentMetadataAccess.cxx:com::sun::star::uno::Reference<com::sun::star::rdf::XURI> const& sfx2::getURI<(short)2007>(com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> const&)
Line
Count
Source
238
6.33k
{
239
6.33k
    static uno::Reference< rdf::XURI > xURI(
240
6.33k
        rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241
6.33k
    return xURI;
242
6.33k
}
243
244
245
/** would storing the file to a XStorage succeed? */
246
static bool isFileNameValid(std::u16string_view i_rFileName)
247
0
{
248
0
    if (i_rFileName.empty()) return false;
249
0
    if (i_rFileName[0] == '/')        return false; // no absolute paths!
250
0
    sal_Int32 idx(0);
251
0
    do {
252
0
      const OUString segment(
253
0
        o3tl::getToken(i_rFileName, 0, u'/', idx) );
254
0
      if (segment.isEmpty()      ||  // no empty segments
255
0
          segment == "."         ||  // no . segments
256
0
          segment == ".."        ||  // no .. segments
257
0
          !::comphelper::OStorageHelper::IsValidZipEntryFileName(
258
0
              segment, false))      // no invalid characters
259
0
                                      return false;
260
0
    } while (idx >= 0);
261
0
    return true;
262
0
}
263
264
/** split a uri hierarchy into first segment and rest */
265
static bool
266
splitPath(OUString const & i_rPath,
267
    OUString & o_rDir, OUString& o_rRest)
268
6.33k
{
269
6.33k
    const sal_Int32 idx(i_rPath.indexOf(u'/'));
270
6.33k
    if (idx < 0 || idx >= i_rPath.getLength()) {
271
6.33k
        o_rDir.clear();
272
6.33k
        o_rRest = i_rPath;
273
6.33k
        return true;
274
6.33k
    } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
275
        // input must not start or end with '/'
276
0
        return false;
277
0
    } else {
278
0
        o_rDir  = i_rPath.copy(0, idx);
279
0
        o_rRest = i_rPath.copy(idx+1);
280
0
        return true;
281
0
    }
282
6.33k
}
283
284
static bool
285
splitXmlId(std::u16string_view i_XmlId,
286
    OUString & o_StreamName, OUString& o_Idref )
287
0
{
288
0
    const size_t idx(i_XmlId.find(u'#'));
289
0
    if (idx == std::u16string_view::npos)
290
0
        return false;
291
0
    o_StreamName = i_XmlId.substr(0, idx);
292
0
    o_Idref      = i_XmlId.substr(idx+1);
293
0
    return isValidXmlId(o_StreamName, o_Idref);
294
0
}
295
296
297
static uno::Reference<rdf::XURI>
298
getURIForStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
299
    OUString const& i_rPath)
300
6.33k
{
301
6.33k
    const uno::Reference<rdf::XURI> xURI(
302
6.33k
        rdf::URI::createNS( i_rImpl.m_xContext,
303
6.33k
            i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
304
6.33k
        uno::UNO_SET_THROW);
305
6.33k
    return xURI;
306
6.33k
}
307
308
/** add statements declaring i_xResource to be a file of type i_xType with
309
    path i_rPath to manifest, with optional additional types i_pTypes */
310
static void
311
addFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
312
    uno::Reference<rdf::XURI> const& i_xType,
313
    OUString const & i_rPath,
314
    const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
315
0
{
316
0
    try {
317
0
        const uno::Reference<rdf::XURI> xURI( getURIForStream(
318
0
            i_rImpl, i_rPath) );
319
320
0
        i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
321
0
            getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
322
0
            xURI);
323
0
        i_rImpl.m_xManifest->addStatement(xURI,
324
0
            getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
325
0
            i_xType);
326
0
        if (i_pTypes) {
327
0
            for (const auto& rType : *i_pTypes) {
328
0
                i_rImpl.m_xManifest->addStatement(xURI,
329
0
                    getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
330
0
                    rType);
331
0
            }
332
0
        }
333
0
    } catch (const uno::RuntimeException &) {
334
0
        throw;
335
0
    } catch (const uno::Exception &) {
336
0
        css::uno::Any anyEx = cppu::getCaughtException();
337
0
        throw lang::WrappedTargetRuntimeException(
338
0
            u"addFile: exception"_ustr, /*this*/nullptr, anyEx);
339
0
    }
340
0
}
341
342
/** add content.xml or styles.xml to manifest */
343
static bool
344
addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
345
    const OUString & i_rPath)
346
0
{
347
0
    uno::Reference<rdf::XURI> xType;
348
0
    if (isContentFile(i_rPath)) {
349
0
        xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
350
0
    } else if (isStylesFile(i_rPath)) {
351
0
        xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
352
0
    } else {
353
0
        return false;
354
0
    }
355
0
    addFile(i_rImpl, xType, i_rPath, nullptr);
356
0
    return true;
357
0
}
358
359
/** add metadata file to manifest */
360
static void
361
addMetadataFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
362
    const OUString & i_rPath,
363
    const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
364
0
{
365
0
    addFile(i_rImpl,
366
0
            getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
367
0
            i_rPath, &i_rTypes);
368
0
}
369
370
/** remove a file from the manifest */
371
static void
372
removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
373
    uno::Reference<rdf::XURI> const& i_xPart)
374
0
{
375
0
    if (!i_xPart.is()) throw uno::RuntimeException();
376
0
    try {
377
0
        i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI,
378
0
            getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
379
0
            i_xPart);
380
0
        i_rImpl.m_xManifest->removeStatements(i_xPart,
381
0
            getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), nullptr);
382
0
    } catch (const uno::RuntimeException &) {
383
0
        throw;
384
0
    } catch (const uno::Exception &) {
385
0
        css::uno::Any anyEx = cppu::getCaughtException();
386
0
        throw lang::WrappedTargetRuntimeException(
387
0
            u"removeFile: exception"_ustr,
388
0
            nullptr, anyEx);
389
0
    }
390
0
}
391
392
static ::std::vector< uno::Reference< rdf::XURI > >
393
getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
394
6.33k
{
395
6.33k
    ::std::vector< uno::Reference< rdf::XURI > > ret;
396
6.33k
    try {
397
6.33k
        const uno::Reference<container::XEnumeration> xEnum(
398
6.33k
            i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI,
399
6.33k
                getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
400
6.33k
            uno::UNO_SET_THROW);
401
6.33k
        while (xEnum->hasMoreElements()) {
402
0
            rdf::Statement stmt;
403
0
            if (!(xEnum->nextElement() >>= stmt)) {
404
0
                throw uno::RuntimeException();
405
0
            }
406
0
            const uno::Reference<rdf::XURI> xPart(stmt.Object,
407
0
                uno::UNO_QUERY);
408
0
            if (!xPart.is()) continue;
409
0
            ret.push_back(xPart);
410
0
        }
411
6.33k
        return ret;
412
6.33k
    } catch (const uno::RuntimeException &) {
413
0
        throw;
414
0
    } catch (const uno::Exception &) {
415
0
        css::uno::Any anyEx = cppu::getCaughtException();
416
0
        throw lang::WrappedTargetRuntimeException(
417
0
            u"getAllParts: exception"_ustr,
418
0
            nullptr, anyEx);
419
0
    }
420
6.33k
}
421
422
static bool
423
isPartOfType(struct DocumentMetadataAccess_Impl const & i_rImpl,
424
    uno::Reference<rdf::XURI> const & i_xPart,
425
    uno::Reference<rdf::XURI> const & i_xType)
426
0
{
427
0
    if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
428
0
    try {
429
0
        const uno::Reference<container::XEnumeration> xEnum(
430
0
            i_rImpl.m_xManifest->getStatements(i_xPart,
431
0
                getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
432
0
                i_xType),
433
0
            uno::UNO_SET_THROW);
434
0
        return xEnum->hasMoreElements();
435
0
    } catch (const uno::RuntimeException &) {
436
0
        throw;
437
0
    } catch (const uno::Exception &) {
438
0
        css::uno::Any anyEx = cppu::getCaughtException();
439
0
        throw lang::WrappedTargetRuntimeException(
440
0
            u"isPartOfType: exception"_ustr,
441
0
            nullptr, anyEx);
442
0
    }
443
0
}
444
445
static ::std::vector<uno::Reference<rdf::XURI>>
446
getAllParts(struct DocumentMetadataAccess_Impl const& i_rImpl,
447
            const uno::Reference<rdf::XURI>& i_xType)
448
1.11k
{
449
1.11k
    ::std::vector<uno::Reference<rdf::XURI>> ret;
450
1.11k
    try
451
1.11k
    {
452
1.11k
        const uno::Reference<container::XEnumeration> xEnum(
453
1.11k
            i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI,
454
1.11k
                                               getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
455
1.11k
                                               nullptr),
456
1.11k
            uno::UNO_SET_THROW);
457
1.11k
        while (xEnum->hasMoreElements())
458
0
        {
459
0
            rdf::Statement stmt;
460
0
            if (!(xEnum->nextElement() >>= stmt))
461
0
            {
462
0
                throw uno::RuntimeException();
463
0
            }
464
0
            const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
465
0
            if (!xPart.is())
466
0
                continue;
467
468
0
            const uno::Reference<container::XEnumeration> xEnum2(
469
0
                i_rImpl.m_xManifest->getStatements(
470
0
                    xPart, getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType),
471
0
                uno::UNO_SET_THROW);
472
0
            if (xEnum2->hasMoreElements())
473
0
                ret.emplace_back(xPart);
474
0
        }
475
1.11k
        return ret;
476
1.11k
    }
477
1.11k
    catch (const uno::RuntimeException&)
478
1.11k
    {
479
0
        throw;
480
0
    }
481
1.11k
    catch (const uno::Exception& e)
482
1.11k
    {
483
0
        throw lang::WrappedTargetRuntimeException(u"getAllParts: exception"_ustr, nullptr,
484
0
                                                  uno::Any(e));
485
0
    }
486
1.11k
}
487
488
static ucb::InteractiveAugmentedIOException
489
mkException( OUString const & i_rMessage,
490
    ucb::IOErrorCode const i_ErrorCode,
491
    OUString const & i_rUri, OUString const & i_rResource)
492
6.33k
{
493
6.33k
    const beans::PropertyValue uriProp(u"Uri"_ustr,
494
6.33k
        -1, uno::Any(i_rUri), static_cast<beans::PropertyState>(0));
495
6.33k
    const beans::PropertyValue rnProp(
496
6.33k
        u"ResourceName"_ustr,
497
6.33k
        -1, uno::Any(i_rResource), static_cast<beans::PropertyState>(0));
498
6.33k
    return ucb::InteractiveAugmentedIOException(i_rMessage, {},
499
6.33k
                                                task::InteractionClassification_ERROR, i_ErrorCode,
500
6.33k
                                                { uno::Any(uriProp), uno::Any(rnProp) });
501
6.33k
}
502
503
/** error handling policy.
504
    <p>If a handler is given, ask it how to proceed:
505
    <ul><li>(default:) cancel import, raise exception</li>
506
        <li>ignore the error and continue</li>
507
        <li>retry the action that led to the error</li></ul></p>
508
    N.B.: must not be called before DMA is fully initialized!
509
    @returns true iff caller should retry
510
 */
511
static bool
512
handleError( ucb::InteractiveAugmentedIOException const & i_rException,
513
    const uno::Reference<task::XInteractionHandler> & i_xHandler)
514
0
{
515
0
    if (!i_xHandler.is()) {
516
0
        throw lang::WrappedTargetException(
517
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: exception"_ustr,
518
0
            /* *this*/ nullptr, uno::Any(i_rException));
519
0
    }
520
521
0
    ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
522
0
        new ::comphelper::OInteractionRequest(uno::Any(i_rException)) );
523
0
    ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
524
0
        new ::comphelper::OInteractionRetry );
525
0
    ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
526
0
        new ::comphelper::OInteractionApprove );
527
0
    ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
528
0
        new ::comphelper::OInteractionAbort );
529
530
0
    pRequest->addContinuation( pApprove );
531
0
    pRequest->addContinuation( pAbort );
532
    // actually call the handler
533
0
    i_xHandler->handle( pRequest );
534
0
    if (pRetry->wasSelected()) {
535
0
        return true;
536
0
    } else if (pApprove->wasSelected()) {
537
0
        return false;
538
0
    } else {
539
0
        OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
540
0
        throw lang::WrappedTargetException(
541
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: exception"_ustr,
542
0
            /* *this*/ nullptr, uno::Any(i_rException));
543
0
    }
544
0
}
545
546
/** check if storage has content.xml/styles.xml;
547
    e.g. ODB files seem to only have content.xml */
548
static void
549
collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
550
    std::set< OUString > & o_rFiles)
551
6.33k
{
552
6.33k
    try {
553
6.33k
        if (i_xStorage->hasByName(s_content) &&
554
0
            i_xStorage->isStreamElement(s_content))
555
0
        {
556
0
            o_rFiles.insert(s_content);
557
0
        }
558
6.33k
        if (i_xStorage->hasByName(s_styles) &&
559
0
            i_xStorage->isStreamElement(s_styles))
560
0
        {
561
0
            o_rFiles.insert(s_styles);
562
0
        }
563
6.33k
    } catch (const uno::Exception &) {
564
0
        TOOLS_WARN_EXCEPTION("sfx", "collectFilesFromStorage");
565
0
    }
566
6.33k
}
567
568
/** import a metadata file into repository */
569
static void
570
readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
571
    uno::Reference< embed::XStorage > const & i_xStorage,
572
    OUString const & i_rPath,
573
    OUString const & i_rBaseURI)
574
6.33k
{
575
6.33k
    try {
576
6.33k
        OUString dir;
577
6.33k
        OUString rest;
578
6.33k
        if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
579
6.33k
        if (dir.isEmpty()) {
580
6.33k
            if (!i_xStorage->isStreamElement(i_rPath)) {
581
0
                throw mkException(
582
0
                    u"readStream: is not a stream"_ustr,
583
0
                    ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
584
0
            }
585
6.33k
            const uno::Reference<io::XStream> xStream(
586
6.33k
                i_xStorage->openStreamElement(i_rPath,
587
6.33k
                    embed::ElementModes::READ), uno::UNO_SET_THROW);
588
6.33k
            const uno::Reference<io::XInputStream> xInStream(
589
6.33k
                xStream->getInputStream(), uno::UNO_SET_THROW );
590
6.33k
            const uno::Reference<rdf::XURI> xBaseURI(
591
6.33k
                rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
592
6.33k
            const uno::Reference<rdf::XURI> xURI(
593
6.33k
                rdf::URI::createNS(i_rImpl.m_xContext,
594
6.33k
                    i_rBaseURI, i_rPath));
595
6.33k
            i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
596
6.33k
                xInStream, xURI, xBaseURI);
597
6.33k
        } else {
598
0
            if (!i_xStorage->isStorageElement(dir)) {
599
0
                throw mkException(
600
0
                    u"readStream: is not a directory"_ustr,
601
0
                    ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
602
0
            }
603
0
            const uno::Reference<embed::XStorage> xDir(
604
0
                i_xStorage->openStorageElement(dir,
605
0
                    embed::ElementModes::READ));
606
0
            const uno::Reference< beans::XPropertySet > xDirProps(xDir,
607
0
                uno::UNO_QUERY_THROW);
608
0
            try {
609
0
                OUString mimeType;
610
0
                xDirProps->getPropertyValue(
611
0
                        utl::MediaDescriptor::PROP_MEDIATYPE )
612
0
                    >>= mimeType;
613
0
                if (mimeType.startsWith(s_odfmime)) {
614
0
                    SAL_WARN("sfx", "readStream: refusing to recurse into embedded document");
615
0
                    return;
616
0
                }
617
0
            } catch (const uno::Exception &) { }
618
0
            readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
619
0
        }
620
6.33k
    } catch (const container::NoSuchElementException & e) {
621
6.33k
        throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
622
6.33k
            i_rBaseURI + i_rPath, i_rPath);
623
6.33k
    } catch (const io::IOException & e) {
624
0
        throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
625
0
            i_rBaseURI + i_rPath, i_rPath);
626
0
    } catch (const rdf::ParseException & e) {
627
0
        throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
628
0
            i_rBaseURI + i_rPath, i_rPath);
629
0
    }
630
6.33k
}
631
632
/** import a metadata file into repository */
633
static void
634
importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
635
    uno::Reference<embed::XStorage> const & i_xStorage,
636
    OUString const & i_rBaseURI,
637
    uno::Reference<task::XInteractionHandler> const & i_xHandler,
638
    const OUString& i_rPath)
639
0
{
640
0
retry:
641
0
    try {
642
0
        readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
643
0
    } catch (const ucb::InteractiveAugmentedIOException & e) {
644
0
        if (handleError(e, i_xHandler)) goto retry;
645
0
    } catch (const uno::RuntimeException &) {
646
0
        throw;
647
0
    } catch (const uno::Exception &) {
648
0
        css::uno::Any anyEx = cppu::getCaughtException();
649
0
        throw lang::WrappedTargetRuntimeException(
650
0
            u"importFile: exception"_ustr,
651
0
            nullptr, anyEx);
652
0
    }
653
0
}
654
655
/** actually write a metadata file to the storage */
656
static void
657
exportStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
658
    uno::Reference< embed::XStorage > const & i_xStorage,
659
    uno::Reference<rdf::XURI> const & i_xGraphName,
660
    OUString const & i_rFileName,
661
    OUString const & i_rBaseURI)
662
0
{
663
0
    const uno::Reference<io::XStream> xStream(
664
0
        i_xStorage->openStreamElement(i_rFileName,
665
0
            embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
666
0
        uno::UNO_SET_THROW);
667
0
    const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
668
0
        uno::UNO_QUERY);
669
0
    if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
670
0
        xStreamProps->setPropertyValue(
671
0
            u"MediaType"_ustr,
672
0
            uno::Any(u"application/rdf+xml"_ustr));
673
0
    }
674
0
    const uno::Reference<io::XOutputStream> xOutStream(
675
0
        xStream->getOutputStream(), uno::UNO_SET_THROW );
676
0
    const uno::Reference<rdf::XURI> xBaseURI(
677
0
        rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
678
0
    i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
679
0
        xOutStream, i_xGraphName, xBaseURI);
680
0
}
681
682
/** write a metadata file to the storage */
683
static void
684
writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
685
    uno::Reference< embed::XStorage > const & i_xStorage,
686
    uno::Reference<rdf::XURI> const & i_xGraphName,
687
    OUString const & i_rPath,
688
    OUString const & i_rBaseURI)
689
0
{
690
0
    OUString dir;
691
0
    OUString rest;
692
0
    if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
693
0
    try {
694
0
        if (dir.isEmpty()) {
695
0
            exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
696
0
                i_rBaseURI);
697
0
        } else {
698
0
            const uno::Reference<embed::XStorage> xDir(
699
0
                i_xStorage->openStorageElement(dir,
700
0
                    embed::ElementModes::WRITE));
701
0
            const uno::Reference< beans::XPropertySet > xDirProps(xDir,
702
0
                uno::UNO_QUERY_THROW);
703
0
            try {
704
0
                OUString mimeType;
705
0
                xDirProps->getPropertyValue(
706
0
                        utl::MediaDescriptor::PROP_MEDIATYPE )
707
0
                    >>= mimeType;
708
0
                if (mimeType.startsWith(s_odfmime)) {
709
0
                    SAL_WARN("sfx", "writeStream: refusing to recurse into embedded document");
710
0
                    return;
711
0
                }
712
0
            } catch (const uno::Exception &) { }
713
0
            writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/");
714
0
            uno::Reference<embed::XTransactedObject> const xTransaction(
715
0
                xDir, uno::UNO_QUERY);
716
0
            if (xTransaction.is()) {
717
0
                xTransaction->commit();
718
0
            }
719
0
        }
720
0
    } catch (const uno::RuntimeException &) {
721
0
        throw;
722
0
    } catch (const io::IOException &) {
723
0
        throw;
724
0
    }
725
0
}
726
727
static void
728
initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
729
    const uno::Reference< embed::XStorage > & i_xStorage,
730
    const uno::Reference<rdf::XURI> & i_xBaseURI,
731
    const uno::Reference<task::XInteractionHandler> & i_xHandler)
732
6.33k
{
733
6.33k
retry:
734
    // clear old data
735
6.33k
    i_rImpl.m_xManifest.clear();
736
    // init BaseURI
737
6.33k
    i_rImpl.m_xBaseURI = i_xBaseURI;
738
739
    // create repository
740
6.33k
    i_rImpl.m_xRepository.clear();
741
6.33k
    i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
742
6.33k
            uno::UNO_SET_THROW);
743
744
    // try to delay raising errors until after initialization is done
745
6.33k
    uno::Any rterr;
746
6.33k
    ucb::InteractiveAugmentedIOException iaioe;
747
6.33k
    bool err(false);
748
749
6.33k
    const uno::Reference <rdf::XURI> xManifest(
750
6.33k
        getURIForStream(i_rImpl, s_manifest));
751
6.33k
    try {
752
6.33k
        readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
753
6.33k
    } catch (const ucb::InteractiveAugmentedIOException & e) {
754
        // no manifest.rdf: this is not an error in ODF < 1.2
755
6.33k
        if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
756
0
            iaioe = e;
757
0
            err = true;
758
0
        }
759
6.33k
    } catch (const uno::Exception & e) {
760
0
        rterr <<= e;
761
0
    }
762
763
    // init manifest graph
764
6.33k
    const uno::Reference<rdf::XNamedGraph> xManifestGraph(
765
6.33k
        i_rImpl.m_xRepository->getGraph(xManifest));
766
6.33k
    i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
767
6.33k
        i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
768
769
    // document statement
770
6.33k
    i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
771
6.33k
        getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
772
6.33k
        getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
773
774
6.33k
    OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
775
6.33k
    OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
776
6.33k
    OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
777
778
6.33k
    if (rterr.hasValue()) {
779
0
        throw lang::WrappedTargetRuntimeException(
780
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: "
781
0
            "exception"_ustr, nullptr, rterr);
782
0
    }
783
784
6.33k
    if (err && handleError(iaioe, i_xHandler))
785
0
        goto retry;
786
6.33k
}
787
788
/** init Impl struct */
789
static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
790
0
{
791
0
    try {
792
793
0
        i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
794
0
            getURIForStream(i_rImpl, s_manifest)),
795
0
            uno::UNO_SET_THROW);
796
797
        // insert the document statement
798
0
        i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
799
0
            getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
800
0
            getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
801
0
    } catch (const uno::Exception &) {
802
0
        css::uno::Any anyEx = cppu::getCaughtException();
803
0
        throw lang::WrappedTargetRuntimeException(
804
0
            u"init: unexpected exception"_ustr, nullptr,
805
0
            anyEx);
806
0
    }
807
808
    // add top-level content files
809
0
    if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
810
0
        throw uno::RuntimeException();
811
0
    }
812
0
    if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
813
0
        throw uno::RuntimeException();
814
0
    }
815
0
}
816
817
818
DocumentMetadataAccess::DocumentMetadataAccess(
819
        uno::Reference< uno::XComponentContext > const & i_xContext,
820
        const SfxObjectShell & i_rRegistrySupplier)
821
6.33k
    : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
822
6.33k
{
823
    // no initialization: must call loadFrom...
824
6.33k
}
825
826
DocumentMetadataAccess::DocumentMetadataAccess(
827
        uno::Reference< uno::XComponentContext > const & i_xContext,
828
        const SfxObjectShell & i_rRegistrySupplier,
829
        OUString const & i_rURI)
830
0
    : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
831
0
{
832
0
    OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
833
0
    OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
834
0
    if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
835
0
    m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
836
0
    m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
837
0
            uno::UNO_SET_THROW);
838
839
    // init repository
840
0
    init(*m_pImpl);
841
842
0
    OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
843
0
    OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
844
0
    OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
845
0
}
846
847
DocumentMetadataAccess::~DocumentMetadataAccess()
848
6.33k
{
849
6.33k
}
850
851
// css::rdf::XRepositorySupplier:
852
uno::Reference< rdf::XRepository > SAL_CALL
853
DocumentMetadataAccess::getRDFRepository()
854
24
{
855
24
    OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
856
24
    return m_pImpl->m_xRepository;
857
24
}
858
859
// css::rdf::XNode:
860
OUString SAL_CALL
861
DocumentMetadataAccess::getStringValue()
862
0
{
863
0
    return m_pImpl->m_xBaseURI->getStringValue();
864
0
}
865
866
// css::rdf::XURI:
867
OUString SAL_CALL
868
DocumentMetadataAccess::getNamespace()
869
0
{
870
0
    return m_pImpl->m_xBaseURI->getNamespace();
871
0
}
872
873
OUString SAL_CALL
874
DocumentMetadataAccess::getLocalName()
875
0
{
876
0
    return m_pImpl->m_xBaseURI->getLocalName();
877
0
}
878
879
// css::rdf::XDocumentMetadataAccess:
880
uno::Reference< rdf::XMetadatable > SAL_CALL
881
DocumentMetadataAccess::getElementByMetadataReference(
882
    const css::beans::StringPair & i_rReference)
883
0
{
884
0
    const IXmlIdRegistry * pReg(
885
0
        m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
886
0
    if (!pReg) {
887
0
        throw uno::RuntimeException(
888
0
            u"DocumentMetadataAccess::getElementByXmlId: no registry"_ustr, *this);
889
0
    }
890
0
    return pReg->GetElementByMetadataReference(i_rReference);
891
0
}
892
893
uno::Reference< rdf::XMetadatable > SAL_CALL
894
DocumentMetadataAccess::getElementByURI(
895
    const uno::Reference< rdf::XURI > & i_xURI )
896
0
{
897
0
    if (!i_xURI.is()) {
898
0
        throw lang::IllegalArgumentException(
899
0
            u"DocumentMetadataAccess::getElementByURI: URI is null"_ustr, *this, 0);
900
0
    }
901
902
0
    const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
903
0
    const OUString name( i_xURI->getStringValue() );
904
0
    if (!name.match(baseURI)) {
905
0
        return nullptr;
906
0
    }
907
0
    OUString path;
908
0
    OUString idref;
909
0
    if (!splitXmlId(name.subView(baseURI.getLength()), path, idref)) {
910
0
        return nullptr;
911
0
    }
912
913
0
    return getElementByMetadataReference( beans::StringPair(path, idref) );
914
0
}
915
916
uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
917
DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
918
1.11k
{
919
1.11k
    if (!i_xType.is())
920
0
    {
921
0
        throw lang::IllegalArgumentException(u"DocumentMetadataAccess::getMetadataGraphsWithType: "
922
0
                                             "type is null"_ustr,
923
0
                                             *this, 0);
924
0
    }
925
926
1.11k
    return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
927
1.11k
}
928
929
uno::Reference<rdf::XURI> SAL_CALL
930
DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
931
    const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
932
0
{
933
0
    if (!isFileNameValid(i_rFileName)) {
934
0
        throw lang::IllegalArgumentException(
935
0
            u"DocumentMetadataAccess::addMetadataFile: invalid FileName"_ustr,
936
0
            *this, 0);
937
0
    }
938
0
    if (isReservedFile(i_rFileName)) {
939
0
        throw lang::IllegalArgumentException(
940
0
            u"DocumentMetadataAccess::addMetadataFile:"
941
0
            "invalid FileName: reserved"_ustr, *this, 0);
942
0
    }
943
0
    if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
944
0
            [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
945
0
        throw lang::IllegalArgumentException(
946
0
                u"DocumentMetadataAccess::addMetadataFile: "
947
0
                "null type"_ustr, *this, 2);
948
0
    }
949
950
0
    const uno::Reference<rdf::XURI> xGraphName(
951
0
        getURIForStream(*m_pImpl, i_rFileName) );
952
953
0
    try {
954
0
        m_pImpl->m_xRepository->createGraph(xGraphName);
955
0
    } catch (const rdf::RepositoryException &) {
956
0
        css::uno::Any anyEx = cppu::getCaughtException();
957
0
        throw lang::WrappedTargetRuntimeException(
958
0
            u"DocumentMetadataAccess::addMetadataFile: exception"_ustr,
959
0
            *this, anyEx);
960
        // note: all other exceptions are propagated
961
0
    }
962
963
0
    addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
964
0
    return xGraphName;
965
0
}
966
967
uno::Reference<rdf::XURI> SAL_CALL
968
DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
969
    const uno::Reference< io::XInputStream > & i_xInStream,
970
    const OUString & i_rFileName,
971
    const uno::Reference< rdf::XURI > & i_xBaseURI,
972
    const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
973
0
{
974
0
    if (!isFileNameValid(i_rFileName)) {
975
0
        throw lang::IllegalArgumentException(
976
0
            u"DocumentMetadataAccess::importMetadataFile: invalid FileName"_ustr,
977
0
            *this, 0);
978
0
    }
979
0
    if (isReservedFile(i_rFileName)) {
980
0
        throw lang::IllegalArgumentException(
981
0
            u"DocumentMetadataAccess::importMetadataFile:"
982
0
            "invalid FileName: reserved"_ustr, *this, 0);
983
0
    }
984
0
    if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
985
0
            [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
986
0
        throw lang::IllegalArgumentException(
987
0
            u"DocumentMetadataAccess::importMetadataFile: null type"_ustr,
988
0
            *this, 5);
989
0
    }
990
991
0
    const uno::Reference<rdf::XURI> xGraphName(
992
0
        getURIForStream(*m_pImpl, i_rFileName) );
993
994
0
    try {
995
0
        m_pImpl->m_xRepository->importGraph(
996
0
            i_Format, i_xInStream, xGraphName, i_xBaseURI);
997
0
    } catch (const rdf::RepositoryException &) {
998
0
        css::uno::Any anyEx = cppu::getCaughtException();
999
0
        throw lang::WrappedTargetRuntimeException(
1000
0
                u"DocumentMetadataAccess::importMetadataFile: "
1001
0
                "RepositoryException"_ustr, *this, anyEx);
1002
        // note: all other exceptions are propagated
1003
0
    }
1004
1005
    // add to manifest
1006
0
    addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
1007
0
    return xGraphName;
1008
0
}
1009
1010
void SAL_CALL
1011
DocumentMetadataAccess::removeMetadataFile(
1012
    const uno::Reference< rdf::XURI > & i_xGraphName)
1013
0
{
1014
0
    try {
1015
0
        m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1016
0
    } catch (const rdf::RepositoryException &) {
1017
0
        css::uno::Any anyEx = cppu::getCaughtException();
1018
0
        throw lang::WrappedTargetRuntimeException(
1019
0
                u"DocumentMetadataAccess::removeMetadataFile: "
1020
0
                "RepositoryException"_ustr, *this, anyEx);
1021
        // note: all other exceptions are propagated
1022
0
    }
1023
1024
    // remove file from manifest
1025
0
    removeFile(*m_pImpl, i_xGraphName);
1026
0
}
1027
1028
void SAL_CALL
1029
DocumentMetadataAccess::addContentOrStylesFile(
1030
    const OUString & i_rFileName)
1031
0
{
1032
0
    if (!isFileNameValid(i_rFileName)) {
1033
0
        throw lang::IllegalArgumentException(
1034
0
            u"DocumentMetadataAccess::addContentOrStylesFile: "
1035
0
            "invalid FileName"_ustr, *this, 0);
1036
0
    }
1037
1038
0
    if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1039
0
        throw lang::IllegalArgumentException(
1040
0
            u"DocumentMetadataAccess::addContentOrStylesFile: "
1041
0
            "invalid FileName: must end with content.xml or styles.xml"_ustr,
1042
0
            *this, 0);
1043
0
    }
1044
0
}
1045
1046
void SAL_CALL
1047
DocumentMetadataAccess::removeContentOrStylesFile(
1048
    const OUString & i_rFileName)
1049
0
{
1050
0
    if (!isFileNameValid(i_rFileName)) {
1051
0
        throw lang::IllegalArgumentException(
1052
0
            u"DocumentMetadataAccess::removeContentOrStylesFile: "
1053
0
            "invalid FileName"_ustr, *this, 0);
1054
0
    }
1055
1056
0
    try {
1057
0
        const uno::Reference<rdf::XURI> xPart(
1058
0
            getURIForStream(*m_pImpl, i_rFileName) );
1059
0
        const uno::Reference<container::XEnumeration> xEnum(
1060
0
            m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI,
1061
0
                getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1062
0
                xPart),
1063
0
            uno::UNO_SET_THROW);
1064
0
        if (!xEnum->hasMoreElements()) {
1065
0
            throw container::NoSuchElementException(
1066
0
                "DocumentMetadataAccess::removeContentOrStylesFile: "
1067
0
                "cannot find stream in manifest graph: " + i_rFileName,
1068
0
                *this);
1069
0
        }
1070
1071
        // remove file from manifest
1072
0
        removeFile(*m_pImpl, xPart);
1073
1074
0
    } catch (const uno::RuntimeException &) {
1075
0
        throw;
1076
0
    } catch (const uno::Exception &) {
1077
0
        css::uno::Any anyEx = cppu::getCaughtException();
1078
0
        throw lang::WrappedTargetRuntimeException(
1079
0
            u"DocumentMetadataAccess::removeContentOrStylesFile: exception"_ustr,
1080
0
            *this, anyEx);
1081
0
    }
1082
0
}
1083
1084
void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1085
    const uno::Reference< embed::XStorage > & i_xStorage,
1086
    const uno::Reference<rdf::XURI> & i_xBaseURI,
1087
    const uno::Reference<task::XInteractionHandler> & i_xHandler)
1088
6.33k
{
1089
6.33k
    if (!i_xStorage.is()) {
1090
0
        throw lang::IllegalArgumentException(
1091
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: "
1092
0
            "storage is null"_ustr, *this, 0);
1093
0
    }
1094
6.33k
    if (!i_xBaseURI.is()) {
1095
0
        throw lang::IllegalArgumentException(
1096
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: "
1097
0
            "base URI is null"_ustr, *this, 1);
1098
0
    }
1099
6.33k
    const OUString baseURI( i_xBaseURI->getStringValue());
1100
6.33k
    if (baseURI.indexOf('#') >= 0) {
1101
0
        throw lang::IllegalArgumentException(
1102
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: "
1103
0
            "base URI not absolute"_ustr, *this, 1);
1104
0
    }
1105
6.33k
    if (!baseURI.endsWith("/")) {
1106
0
        throw lang::IllegalArgumentException(
1107
0
            u"DocumentMetadataAccess::loadMetadataFromStorage: "
1108
0
            "base URI does not end with slash"_ustr, *this, 1);
1109
0
    }
1110
1111
6.33k
    initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1112
1113
6.33k
    std::set< OUString > StgFiles;
1114
6.33k
    collectFilesFromStorage(i_xStorage, StgFiles);
1115
1116
6.33k
    std::vector< OUString > MfstMetadataFiles;
1117
1118
6.33k
    try {
1119
6.33k
        const ::std::vector< uno::Reference< rdf::XURI > > parts(
1120
6.33k
            getAllParts(*m_pImpl) );
1121
6.33k
        const uno::Reference<rdf::XURI>& xContentFile(
1122
6.33k
            getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1123
6.33k
        const uno::Reference<rdf::XURI>& xStylesFile(
1124
6.33k
            getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1125
6.33k
        const uno::Reference<rdf::XURI>& xMetadataFile(
1126
6.33k
            getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1127
6.33k
        const sal_Int32 len( baseURI.getLength() );
1128
6.33k
        for (const auto& rxPart : parts) {
1129
0
            const OUString name(rxPart->getStringValue());
1130
0
            if (!name.match(baseURI)) {
1131
0
                SAL_WARN("sfx", "loadMetadataFromStorage: graph not in document: " << name);
1132
0
                continue;
1133
0
            }
1134
0
            const OUString relName( name.copy(len) );
1135
0
            if (relName == s_manifest) {
1136
0
                SAL_WARN("sfx", "loadMetadataFromStorage: found ourselves a recursive manifest!");
1137
0
                continue;
1138
0
            }
1139
            // remove found items from StgFiles
1140
0
            StgFiles.erase(relName);
1141
0
            if (isContentFile(relName)) {
1142
0
                if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
1143
0
                    const uno::Reference <rdf::XURI> xName(
1144
0
                        getURIForStream(*m_pImpl, relName) );
1145
                    // add missing type statement
1146
0
                    m_pImpl->m_xManifest->addStatement(xName,
1147
0
                        getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1148
0
                        xContentFile);
1149
0
                }
1150
0
            } else if (isStylesFile(relName)) {
1151
0
                if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
1152
0
                    const uno::Reference <rdf::XURI> xName(
1153
0
                        getURIForStream(*m_pImpl, relName) );
1154
                    // add missing type statement
1155
0
                    m_pImpl->m_xManifest->addStatement(xName,
1156
0
                        getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1157
0
                        xStylesFile);
1158
0
                }
1159
0
            } else if (isReservedFile(relName)) {
1160
0
                SAL_WARN("sfx", "loadMetadataFromStorage: reserved file name in manifest");
1161
0
            } else {
1162
0
                if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
1163
0
                    MfstMetadataFiles.push_back(relName);
1164
0
                }
1165
                // do not add statement for MetadataFile; it could be
1166
                // something else! just ignore it...
1167
0
            }
1168
0
        }
1169
6.33k
    } catch (const uno::RuntimeException &) {
1170
0
        throw;
1171
0
    } catch (const uno::Exception &) {
1172
0
        css::uno::Any anyEx = cppu::getCaughtException();
1173
0
        throw lang::WrappedTargetRuntimeException(
1174
0
                u"DocumentMetadataAccess::loadMetadataFromStorage: "
1175
0
                "exception"_ustr, *this, anyEx);
1176
0
    }
1177
1178
6.33k
    for (const auto& aStgFile : StgFiles)
1179
0
        addContentOrStylesFileImpl(*m_pImpl, aStgFile);
1180
1181
6.33k
    for (const auto& aMfstMetadataFile : MfstMetadataFiles)
1182
0
        importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
1183
6.33k
}
1184
1185
void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1186
    const uno::Reference< embed::XStorage > & i_xStorage)
1187
0
{
1188
0
    if (!i_xStorage.is()) {
1189
0
        throw lang::IllegalArgumentException(
1190
0
            u"DocumentMetadataAccess::storeMetadataToStorage: "
1191
0
            "storage is null"_ustr, *this, 0);
1192
0
    }
1193
1194
    // export manifest
1195
0
    const uno::Reference <rdf::XURI> xManifest(
1196
0
        getURIForStream(*m_pImpl, s_manifest) );
1197
0
    const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1198
0
    try {
1199
0
        writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
1200
0
    } catch (const uno::RuntimeException &) {
1201
0
        throw;
1202
0
    } catch (const io::IOException &) {
1203
0
        css::uno::Any anyEx = cppu::getCaughtException();
1204
0
        throw lang::WrappedTargetException(
1205
0
            u"storeMetadataToStorage: IO exception"_ustr, *this, anyEx);
1206
0
    } catch (const uno::Exception &) {
1207
0
        css::uno::Any anyEx = cppu::getCaughtException();
1208
0
        throw lang::WrappedTargetRuntimeException(
1209
0
                u"storeMetadataToStorage: exception"_ustr, *this, anyEx);
1210
0
    }
1211
1212
    // export metadata streams
1213
0
    try {
1214
0
        const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1215
0
            m_pImpl->m_xRepository->getGraphNames());
1216
0
        const sal_Int32 len( baseURI.getLength() );
1217
0
        for (const uno::Reference<rdf::XURI>& xName : graphs) {
1218
0
            const OUString name(xName->getStringValue());
1219
0
            if (!name.match(baseURI)) {
1220
0
                SAL_WARN("sfx", "storeMetadataToStorage: graph not in document: " << name);
1221
0
                continue;
1222
0
            }
1223
0
            const OUString relName( name.copy(len) );
1224
0
            if (relName == s_manifest) {
1225
0
                continue;
1226
0
            }
1227
0
            if (!isFileNameValid(relName) || isReservedFile(relName)) {
1228
0
                SAL_WARN("sfx", "storeMetadataToStorage: invalid file name: " << relName);
1229
0
                continue;
1230
0
            }
1231
0
            try {
1232
0
                writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1233
0
            } catch (const uno::RuntimeException &) {
1234
0
                throw;
1235
0
            } catch (const io::IOException &) {
1236
0
                css::uno::Any anyEx = cppu::getCaughtException();
1237
0
                throw lang::WrappedTargetException(
1238
0
                    u"storeMetadataToStorage: IO exception"_ustr,
1239
0
                    *this, anyEx);
1240
0
            } catch (const uno::Exception &) {
1241
0
                css::uno::Any anyEx = cppu::getCaughtException();
1242
0
                throw lang::WrappedTargetRuntimeException(
1243
0
                    u"storeMetadataToStorage: exception"_ustr,
1244
0
                    *this, anyEx);
1245
0
            }
1246
0
        }
1247
0
    } catch (const rdf::RepositoryException &) {
1248
0
        css::uno::Any anyEx = cppu::getCaughtException();
1249
0
        throw lang::WrappedTargetRuntimeException(
1250
0
                u"storeMetadataToStorage: exception"_ustr, *this, anyEx);
1251
0
    }
1252
0
}
1253
1254
void SAL_CALL
1255
DocumentMetadataAccess::loadMetadataFromMedium(
1256
    const uno::Sequence< beans::PropertyValue > & i_rMedium)
1257
0
{
1258
0
    uno::Reference<io::XInputStream> xIn;
1259
0
    comphelper::SequenceAsHashMap md(i_rMedium);
1260
0
    OUString URL;
1261
0
    md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
1262
0
    OUString BaseURL;
1263
0
    md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= BaseURL;
1264
0
    if (utl::MediaDescriptor::addInputStream(md)) {
1265
0
        md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
1266
0
    }
1267
0
    if (!xIn.is() && URL.isEmpty()) {
1268
0
        throw lang::IllegalArgumentException(
1269
0
            u"DocumentMetadataAccess::loadMetadataFromMedium: "
1270
0
            "invalid medium: no URL, no input stream"_ustr, *this, 0);
1271
0
    }
1272
0
    uno::Reference<embed::XStorage> xStorage;
1273
0
    try {
1274
0
        if (xIn.is()) {
1275
0
            xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1276
0
                            xIn, m_pImpl->m_xContext);
1277
0
        } else { // fallback to url
1278
0
            xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1279
0
                            URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1280
0
        }
1281
0
    } catch (const uno::RuntimeException &) {
1282
0
        throw;
1283
0
    } catch (const io::IOException &) {
1284
0
        throw;
1285
0
    } catch (const uno::Exception &) {
1286
0
        css::uno::Any anyEx = cppu::getCaughtException();
1287
0
        throw lang::WrappedTargetException(
1288
0
                    u"DocumentMetadataAccess::loadMetadataFromMedium: "
1289
0
                    "exception"_ustr, *this, anyEx);
1290
0
    }
1291
0
    if (!xStorage.is()) {
1292
0
        throw uno::RuntimeException(
1293
0
            u"DocumentMetadataAccess::loadMetadataFromMedium: "
1294
0
            "cannot get Storage"_ustr, *this);
1295
0
    }
1296
0
    uno::Reference<rdf::XURI> xBaseURI;
1297
0
    try {
1298
0
        xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
1299
0
    } catch (const uno::Exception &) {
1300
        // fall back to URL
1301
0
        try {
1302
0
            xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
1303
0
        } catch (const uno::Exception &) {
1304
0
            OSL_FAIL("cannot create base URI");
1305
0
        }
1306
0
    }
1307
0
    uno::Reference<task::XInteractionHandler> xIH;
1308
0
    md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER ] >>= xIH;
1309
0
    loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1310
0
}
1311
1312
void SAL_CALL
1313
DocumentMetadataAccess::storeMetadataToMedium(
1314
    const uno::Sequence< beans::PropertyValue > & i_rMedium)
1315
0
{
1316
0
    comphelper::SequenceAsHashMap md(i_rMedium);
1317
0
    OUString URL;
1318
0
    md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
1319
0
    if (URL.isEmpty()) {
1320
0
        throw lang::IllegalArgumentException(
1321
0
            u"DocumentMetadataAccess::storeMetadataToMedium: "
1322
0
            "invalid medium: no URL"_ustr, *this, 0);
1323
0
    }
1324
1325
0
    SfxMedium aMedium(i_rMedium);
1326
0
    uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1327
1328
0
    bool sfx(false);
1329
0
    if (xStorage.is()) {
1330
0
        sfx = true;
1331
0
    } else {
1332
0
        xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1333
0
                        URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1334
0
    }
1335
1336
0
    if (!xStorage.is()) {
1337
0
        throw uno::RuntimeException(
1338
0
            u"DocumentMetadataAccess::storeMetadataToMedium: "
1339
0
            "cannot get Storage"_ustr, *this);
1340
0
    }
1341
    // set MIME type of the storage
1342
0
    auto iter = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
1343
0
    if (iter != md.end()) {
1344
0
        uno::Reference< beans::XPropertySet > xProps(xStorage,
1345
0
            uno::UNO_QUERY_THROW);
1346
0
        try {
1347
            // this is NOT supported in FileSystemStorage
1348
0
            xProps->setPropertyValue(
1349
0
                utl::MediaDescriptor::PROP_MEDIATYPE,
1350
0
                iter->second);
1351
0
        } catch (const uno::Exception &) { }
1352
0
    }
1353
0
    storeMetadataToStorage(xStorage);
1354
1355
0
    if (!sfx)
1356
0
        return;
1357
1358
0
    const bool bOk = aMedium.Commit();
1359
0
    aMedium.Close();
1360
0
    if ( !bOk ) {
1361
0
        ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning();
1362
0
        if ( nError == ERRCODE_NONE ) {
1363
0
            nError = ERRCODE_IO_GENERAL;
1364
0
        }
1365
0
        task::ErrorCodeIOException ex(
1366
0
            "DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toString(),
1367
0
            uno::Reference< uno::XInterface >(), sal_uInt32(nError.GetCode()));
1368
0
        throw lang::WrappedTargetException(OUString(), *this,
1369
0
                uno::Any(ex));
1370
0
    }
1371
0
}
1372
1373
} // namespace sfx2
1374
1375
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */