Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/ucb/source/ucp/tdoc/tdoc_provider.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
/**************************************************************************
22
                                TODO
23
 **************************************************************************
24
25
 *************************************************************************/
26
27
#include <comphelper/diagnose_ex.hxx>
28
29
#include <com/sun/star/embed/InvalidStorageException.hpp>
30
#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
31
#include <com/sun/star/io/IOException.hpp>
32
#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
33
#include <cppuhelper/queryinterface.hxx>
34
#include <ucbhelper/contentidentifier.hxx>
35
#include <ucbhelper/macros.hxx>
36
37
#include "tdoc_provider.hxx"
38
#include "tdoc_content.hxx"
39
#include "tdoc_uri.hxx"
40
#include "tdoc_docmgr.hxx"
41
#include "tdoc_storage.hxx"
42
43
using namespace com::sun::star;
44
using namespace tdoc_ucp;
45
46
47
// ContentProvider Implementation.
48
49
50
ContentProvider::ContentProvider(
51
            const uno::Reference< uno::XComponentContext >& rxContext )
52
10.4k
: ContentProvider_Base( rxContext ),
53
10.4k
  m_xDocsMgr( new OfficeDocumentsManager( rxContext, this ) ),
54
10.4k
  m_xStgElemFac( new StorageElementFactory( rxContext, m_xDocsMgr ) )
55
10.4k
{
56
10.4k
}
57
58
59
// virtual
60
ContentProvider::~ContentProvider()
61
10.4k
{
62
10.4k
    if ( m_xDocsMgr.is() )
63
10.4k
        m_xDocsMgr->destroy();
64
10.4k
}
65
66
67
// XServiceInfo methods.
68
OUString SAL_CALL ContentProvider::getImplementationName()
69
0
{
70
0
    return u"com.sun.star.comp.ucb.TransientDocumentsContentProvider"_ustr;
71
0
}
72
73
sal_Bool SAL_CALL ContentProvider::supportsService( const OUString& ServiceName )
74
0
{
75
0
    return cppu::supportsService( this, ServiceName );
76
0
}
77
78
css::uno::Sequence< OUString > SAL_CALL ContentProvider::getSupportedServiceNames()
79
0
{
80
0
    return { u"com.sun.star.ucb.TransientDocumentsContentProvider"_ustr };
81
0
}
82
83
84
// Service factory implementation.
85
86
87
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
88
ucb_tdoc_ContentProvider_get_implementation(
89
    css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
90
10.4k
{
91
10.4k
    return cppu::acquire(new ContentProvider(context));
92
10.4k
}
93
94
// XContentProvider methods.
95
96
97
// virtual
98
uno::Reference< ucb::XContent > SAL_CALL
99
ContentProvider::queryContent(
100
        const uno::Reference< ucb::XContentIdentifier >& Identifier )
101
0
{
102
0
    Uri aUri( Identifier->getContentIdentifier() );
103
0
    if ( !aUri.isValid() )
104
0
        throw ucb::IllegalIdentifierException(
105
0
            u"Invalid URL!"_ustr,
106
0
            Identifier );
107
108
    // Normalize URI.
109
0
    uno::Reference< ucb::XContentIdentifier > xCanonicId
110
0
        = new ::ucbhelper::ContentIdentifier( aUri.getUri() );
111
112
0
    osl::MutexGuard aGuard( m_aMutex );
113
114
    // Check, if a content with given id already exists...
115
0
    rtl::Reference< ucbhelper::ContentImplHelper > xContent
116
0
        = queryExistingContent( xCanonicId );
117
118
0
    if ( !xContent.is() )
119
0
    {
120
        // Create a new content.
121
0
        xContent = Content::create( m_xContext, this, xCanonicId );
122
0
        registerNewContent( xContent );
123
0
    }
124
125
0
    return xContent;
126
0
}
127
128
129
// XTransientDocumentsDocumentContentIdentifierFactory methods.
130
131
uno::Reference<ucb::XContentIdentifier> SAL_CALL
132
ContentProvider::createDocumentContentIdentifier(
133
        uno::Reference<frame::XModel> const& xModel)
134
10.4k
{
135
    // model -> id -> content identifier -> queryContent
136
10.4k
    if ( !m_xDocsMgr.is() )
137
0
    {
138
0
        throw lang::IllegalArgumentException(
139
0
            u"No Document Manager!"_ustr,
140
0
            getXWeak(),
141
0
            1 );
142
0
    }
143
144
10.4k
    OUString aDocId = tdoc_ucp::OfficeDocumentsManager::queryDocumentId(xModel);
145
10.4k
    if ( aDocId.isEmpty() )
146
0
    {
147
0
        throw lang::IllegalArgumentException(
148
0
            u"Unable to obtain document id from model!"_ustr,
149
0
            getXWeak(),
150
0
            1 );
151
0
    }
152
153
10.4k
    OUString aBuffer = TDOC_URL_SCHEME ":/" + aDocId;
154
155
10.4k
    uno::Reference< ucb::XContentIdentifier > xId
156
10.4k
        = new ::ucbhelper::ContentIdentifier( aBuffer );
157
10.4k
    return xId;
158
10.4k
}
159
160
// XTransientDocumentsDocumentContentFactory methods.
161
162
uno::Reference< ucb::XContent > SAL_CALL
163
ContentProvider::createDocumentContent(
164
        uno::Reference<frame::XModel> const& xModel)
165
4.09k
{
166
4.09k
    uno::Reference<ucb::XContentIdentifier> const xId(
167
4.09k
            createDocumentContentIdentifier(xModel));
168
169
4.09k
    osl::MutexGuard aGuard( m_aMutex );
170
171
    // Check, if a content with given id already exists...
172
4.09k
    rtl::Reference< ucbhelper::ContentImplHelper > xContent
173
4.09k
        = queryExistingContent( xId );
174
175
4.09k
    if ( !xContent.is() )
176
4.09k
    {
177
        // Create a new content.
178
4.09k
        xContent = Content::create( m_xContext, this, xId );
179
4.09k
    }
180
181
4.09k
    if ( xContent.is() )
182
0
        return xContent;
183
184
    // no content.
185
4.09k
    throw lang::IllegalArgumentException(
186
4.09k
        u"Illegal Content Identifier!"_ustr,
187
4.09k
        getXWeak(),
188
4.09k
        1 );
189
4.09k
}
190
191
192
// interface OfficeDocumentsEventListener
193
194
195
// virtual
196
void ContentProvider::notifyDocumentClosed( std::u16string_view rDocId )
197
0
{
198
0
    osl::MutexGuard aGuard( getContentListMutex() );
199
200
0
    ::ucbhelper::ContentRefList aAllContents;
201
0
    queryExistingContents( aAllContents );
202
203
    // Notify all content objects related to the closed doc.
204
205
0
    bool bFoundDocumentContent = false;
206
0
    rtl::Reference< Content > xRoot;
207
208
0
    for ( const auto& rContent : aAllContents )
209
0
    {
210
0
        Uri aUri( rContent->getIdentifier()->getContentIdentifier() );
211
0
        OSL_ENSURE( aUri.isValid(),
212
0
                    "ContentProvider::notifyDocumentClosed - Invalid URI!" );
213
214
0
        if ( !bFoundDocumentContent )
215
0
        {
216
0
            if ( aUri.isRoot() )
217
0
            {
218
0
                xRoot = static_cast< Content * >( rContent.get() );
219
0
            }
220
0
            else if ( aUri.isDocument() )
221
0
            {
222
0
                if ( aUri.getDocumentId() == rDocId )
223
0
                {
224
0
                    bFoundDocumentContent = true;
225
226
                    // document content will notify removal of child itself;
227
                    // no need for the root to propagate this.
228
0
                    xRoot.clear();
229
0
                }
230
0
            }
231
0
        }
232
233
0
        if ( aUri.getDocumentId() == rDocId )
234
0
        {
235
            // Inform content.
236
0
            rtl::Reference< Content > xContent
237
0
                = static_cast< Content * >( rContent.get() );
238
239
0
            xContent->notifyDocumentClosed();
240
0
        }
241
0
    }
242
243
0
    if ( xRoot.is() )
244
0
    {
245
        // No document content found for rDocId but root content
246
        // instantiated. Root content must announce document removal
247
        // to content event listeners.
248
0
        xRoot->notifyChildRemoved( rDocId );
249
0
    }
250
0
}
251
252
253
// virtual
254
void ContentProvider::notifyDocumentOpened( std::u16string_view rDocId )
255
0
{
256
0
    osl::MutexGuard aGuard( getContentListMutex() );
257
258
0
    ::ucbhelper::ContentRefList aAllContents;
259
0
    queryExistingContents( aAllContents );
260
261
    // Find root content. If instantiated let it propagate document insertion.
262
263
0
    for ( const auto& rContent : aAllContents )
264
0
    {
265
0
        Uri aUri( rContent->getIdentifier()->getContentIdentifier() );
266
0
        OSL_ENSURE( aUri.isValid(),
267
0
                    "ContentProvider::notifyDocumentOpened - Invalid URI!" );
268
269
0
        if ( aUri.isRoot() )
270
0
        {
271
0
            rtl::Reference< Content > xRoot
272
0
                = static_cast< Content * >( rContent.get() );
273
0
            xRoot->notifyChildInserted( rDocId );
274
275
            // Done.
276
0
            break;
277
0
        }
278
0
    }
279
0
}
280
281
282
// Non-UNO
283
284
285
uno::Reference< embed::XStorage >
286
ContentProvider::queryStorage( const OUString & rUri,
287
                               StorageAccessMode eMode ) const
288
4.09k
{
289
4.09k
    if ( m_xStgElemFac.is() )
290
4.09k
    {
291
4.09k
        try
292
4.09k
        {
293
4.09k
            return m_xStgElemFac->createStorage( rUri, eMode );
294
4.09k
        }
295
4.09k
        catch ( embed::InvalidStorageException const & )
296
4.09k
        {
297
4.09k
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
298
4.09k
        }
299
4.09k
        catch ( lang::IllegalArgumentException const & )
300
4.09k
        {
301
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
302
0
        }
303
4.09k
        catch ( io::IOException const & )
304
4.09k
        {
305
            // Okay to happen, for instance when the storage does not exist.
306
            //OSL_ENSURE( false, "Caught IOException!" );
307
0
        }
308
4.09k
        catch ( embed::StorageWrappedTargetException const & )
309
4.09k
        {
310
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
311
0
        }
312
4.09k
    }
313
4.09k
    return uno::Reference< embed::XStorage >();
314
4.09k
}
315
316
317
uno::Reference< embed::XStorage >
318
ContentProvider::queryStorageClone( const OUString & rUri ) const
319
0
{
320
0
    if ( m_xStgElemFac.is() )
321
0
    {
322
0
        try
323
0
        {
324
0
            Uri aUri( rUri );
325
0
            uno::Reference< embed::XStorage > xParentStorage
326
0
                = m_xStgElemFac->createStorage( aUri.getParentUri(), READ );
327
0
            uno::Reference< embed::XStorage > xStorage
328
0
                = m_xStgElemFac->createTemporaryStorage();
329
330
0
            xParentStorage->copyStorageElementLastCommitTo(
331
0
                                aUri.getDecodedName(), xStorage );
332
0
            return xStorage;
333
0
        }
334
0
        catch ( embed::InvalidStorageException const & )
335
0
        {
336
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
337
0
        }
338
0
        catch ( lang::IllegalArgumentException const & )
339
0
        {
340
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
341
0
        }
342
0
        catch ( io::IOException const & )
343
0
        {
344
            // Okay to happen, for instance when the storage does not exist.
345
            //OSL_ENSURE( false, "Caught IOException!" );
346
0
        }
347
0
        catch ( embed::StorageWrappedTargetException const & )
348
0
        {
349
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
350
0
        }
351
0
    }
352
353
0
    return uno::Reference< embed::XStorage >();
354
0
}
355
356
357
uno::Reference< io::XInputStream >
358
ContentProvider::queryInputStream( const OUString & rUri,
359
                                   const OUString & rPassword ) const
360
0
{
361
0
    if ( m_xStgElemFac.is() )
362
0
    {
363
0
        try
364
0
        {
365
0
            return m_xStgElemFac->createInputStream( rUri, rPassword );
366
0
        }
367
0
        catch ( embed::InvalidStorageException const & )
368
0
        {
369
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
370
0
        }
371
0
        catch ( lang::IllegalArgumentException const & )
372
0
        {
373
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
374
0
        }
375
0
        catch ( io::IOException const & )
376
0
        {
377
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
378
0
        }
379
0
        catch ( embed::StorageWrappedTargetException const & )
380
0
        {
381
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
382
0
        }
383
//        catch ( packages::WrongPasswordException const & )
384
//        {
385
//            // the key provided is wrong; rethrow; to be handled by caller.
386
//            throw;
387
//        }
388
0
    }
389
0
    return uno::Reference< io::XInputStream >();
390
0
}
391
392
393
uno::Reference< io::XOutputStream >
394
ContentProvider::queryOutputStream( const OUString & rUri,
395
                                    const OUString & rPassword,
396
                                    bool bTruncate ) const
397
0
{
398
0
    if ( m_xStgElemFac.is() )
399
0
    {
400
0
        try
401
0
        {
402
0
            return
403
0
                m_xStgElemFac->createOutputStream( rUri, rPassword, bTruncate );
404
0
        }
405
0
        catch ( embed::InvalidStorageException const & )
406
0
        {
407
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
408
0
        }
409
0
        catch ( lang::IllegalArgumentException const & )
410
0
        {
411
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
412
0
        }
413
0
        catch ( io::IOException const & )
414
0
        {
415
            // Okay to happen, for instance when the storage does not exist.
416
            //OSL_ENSURE( false, "Caught IOException!" );
417
0
        }
418
0
        catch ( embed::StorageWrappedTargetException const & )
419
0
        {
420
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
421
0
        }
422
//        catch ( packages::WrongPasswordException const & )
423
//        {
424
//            // the key provided is wrong; rethrow; to be handled by caller.
425
//            throw;
426
//        }
427
0
    }
428
0
    return uno::Reference< io::XOutputStream >();
429
0
}
430
431
432
uno::Reference< io::XStream >
433
ContentProvider::queryStream( const OUString & rUri,
434
                              const OUString & rPassword,
435
                              bool bTruncate ) const
436
0
{
437
0
    if ( m_xStgElemFac.is() )
438
0
    {
439
0
        try
440
0
        {
441
0
            return m_xStgElemFac->createStream( rUri, rPassword, bTruncate );
442
0
        }
443
0
        catch ( embed::InvalidStorageException const & )
444
0
        {
445
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
446
0
        }
447
0
        catch ( lang::IllegalArgumentException const & )
448
0
        {
449
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
450
0
        }
451
0
        catch ( io::IOException const & )
452
0
        {
453
            // Okay to happen, for instance when the storage does not exist.
454
            //OSL_ENSURE( false, "Caught IOException!" );
455
0
        }
456
0
        catch ( embed::StorageWrappedTargetException const & )
457
0
        {
458
0
            TOOLS_WARN_EXCEPTION("ucb.ucp", "");
459
0
        }
460
//        catch ( packages::WrongPasswordException const & )
461
//        {
462
//            // the key provided is wrong; rethrow; to be handled by caller.
463
//            throw;
464
//        }
465
0
    }
466
0
    return uno::Reference< io::XStream >();
467
0
}
468
469
470
bool ContentProvider::queryNamesOfChildren(
471
    const OUString & rUri, uno::Sequence< OUString > & rNames ) const
472
0
{
473
0
    Uri aUri( rUri );
474
0
    if ( aUri.isRoot() )
475
0
    {
476
        // special handling for root, which has no storage, but children.
477
0
        if ( m_xDocsMgr.is() )
478
0
        {
479
0
            rNames = m_xDocsMgr->queryDocuments();
480
0
            return true;
481
0
        }
482
0
    }
483
0
    else
484
0
    {
485
0
        if ( m_xStgElemFac.is() )
486
0
        {
487
0
            try
488
0
            {
489
0
                uno::Reference< embed::XStorage > xStorage
490
0
                    = m_xStgElemFac->createStorage( rUri, READ );
491
492
0
                OSL_ENSURE( xStorage.is(), "Got no Storage!" );
493
494
0
                if ( xStorage.is() )
495
0
                {
496
0
                    rNames = xStorage->getElementNames();
497
0
                    return true;
498
0
                }
499
0
            }
500
0
            catch ( embed::InvalidStorageException const & )
501
0
            {
502
0
                TOOLS_WARN_EXCEPTION("ucb.ucp", "");
503
0
            }
504
0
            catch ( lang::IllegalArgumentException const & )
505
0
            {
506
0
                TOOLS_WARN_EXCEPTION("ucb.ucp", "");
507
0
            }
508
0
            catch ( io::IOException const & )
509
0
            {
510
                // Okay to happen, for instance if the storage does not exist.
511
                //OSL_ENSURE( false, "Caught IOException!" );
512
0
            }
513
0
            catch ( embed::StorageWrappedTargetException const & )
514
0
            {
515
0
                TOOLS_WARN_EXCEPTION("ucb.ucp", "");
516
0
            }
517
0
        }
518
0
    }
519
0
    return false;
520
0
}
521
522
523
OUString
524
ContentProvider::queryStorageTitle( const OUString & rUri ) const
525
0
{
526
0
    OUString aTitle;
527
528
0
    Uri aUri( rUri );
529
0
    if ( aUri.isRoot() )
530
0
    {
531
        // always empty.
532
0
        aTitle.clear();
533
0
    }
534
0
    else if ( aUri.isDocument() )
535
0
    {
536
        // for documents, title shall not be derived from URL. It shall
537
        // be something more 'speaking' than just the document UID.
538
0
        if ( m_xDocsMgr.is() )
539
0
            aTitle = m_xDocsMgr->queryStorageTitle( aUri.getDocumentId() );
540
0
    }
541
0
    else
542
0
    {
543
        // derive title from URL
544
0
        aTitle = aUri.getDecodedName();
545
0
    }
546
547
0
    OSL_ENSURE( !aTitle.isEmpty() || aUri.isRoot(),
548
0
                "ContentProvider::queryStorageTitle - empty title!" );
549
0
    return aTitle;
550
0
}
551
552
553
uno::Reference< frame::XModel >
554
ContentProvider::queryDocumentModel( const OUString & rUri ) const
555
0
{
556
0
    uno::Reference< frame::XModel > xModel;
557
558
0
    if ( m_xDocsMgr.is() )
559
0
    {
560
0
        Uri aUri( rUri );
561
0
        xModel = m_xDocsMgr->queryDocumentModel( aUri.getDocumentId() );
562
0
    }
563
564
0
    OSL_ENSURE( xModel.is(),
565
0
                "ContentProvider::queryDocumentModel - no model!" );
566
0
    return xModel;
567
0
}
568
569
570
0
css::util::DateTime ContentProvider::queryStreamDateModified(OUString const & uri) const {
571
0
    return m_xDocsMgr->queryStreamDateModified(uri);
572
0
}
573
574
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */