Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svtools/source/misc/templatefoldercache.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
#include <sal/config.h>
21
22
#include <osl/file.hxx>
23
#include <svtools/templatefoldercache.hxx>
24
#include <unotools/ucbstreamhelper.hxx>
25
#include <com/sun/star/sdbc/XResultSet.hpp>
26
#include <com/sun/star/ucb/XDynamicResultSet.hpp>
27
#include <com/sun/star/sdbc/XRow.hpp>
28
#include <com/sun/star/ucb/CommandAbortedException.hpp>
29
#include <com/sun/star/ucb/XContentAccess.hpp>
30
#include <com/sun/star/uno/XComponentContext.hpp>
31
#include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
32
#include <ucbhelper/content.hxx>
33
#include <osl/diagnose.h>
34
#include <rtl/ref.hxx>
35
#include <salhelper/simplereferenceobject.hxx>
36
#include <tools/stream.hxx>
37
#include <tools/time.hxx>
38
#include <tools/urlobj.hxx>
39
#include <tools/debug.hxx>
40
#include <comphelper/diagnose_ex.hxx>
41
#include <unotools/pathoptions.hxx>
42
43
#include <comphelper/processfactory.hxx>
44
45
#include <mutex>
46
#include <utility>
47
#include <vector>
48
#include <algorithm>
49
50
51
namespace svt
52
{
53
54
55
    using namespace ::utl;
56
    using namespace ::com::sun::star;
57
    using namespace ::com::sun::star::sdbc;
58
    using namespace ::com::sun::star::ucb;
59
    using namespace ::com::sun::star::uno;
60
61
62
    //= helpers
63
64
65
    static SvStream& WriteDateTime( SvStream& _rStorage, const util::DateTime& _rDate )
66
0
    {
67
0
        sal_uInt16 hundredthSeconds = static_cast< sal_uInt16 >( _rDate.NanoSeconds / tools::Time::nanoPerCenti );
68
0
        _rStorage.WriteUInt16( hundredthSeconds );
69
70
0
        _rStorage.WriteUInt16( _rDate.Seconds );
71
0
        _rStorage.WriteUInt16( _rDate.Minutes );
72
0
        _rStorage.WriteUInt16( _rDate.Hours );
73
0
        _rStorage.WriteUInt16( _rDate.Day );
74
0
        _rStorage.WriteUInt16( _rDate.Month );
75
0
        _rStorage.WriteInt16( _rDate.Year );
76
77
0
        return _rStorage;
78
0
    }
79
80
81
    static SvStream& operator >> ( SvStream& _rStorage, util::DateTime& _rDate )
82
0
    {
83
0
        sal_uInt16 hundredthSeconds;
84
0
        _rStorage.ReadUInt16( hundredthSeconds );
85
0
        _rDate.NanoSeconds = static_cast< sal_uInt32 >( hundredthSeconds ) * tools::Time::nanoPerCenti;
86
87
0
        _rStorage.ReadUInt16( _rDate.Seconds );
88
0
        _rStorage.ReadUInt16( _rDate.Minutes );
89
0
        _rStorage.ReadUInt16( _rDate.Hours );
90
0
        _rStorage.ReadUInt16( _rDate.Day );
91
0
        _rStorage.ReadUInt16( _rDate.Month );
92
0
        _rStorage.ReadInt16( _rDate.Year );
93
94
0
        return _rStorage;
95
0
    }
96
97
    //= TemplateContent
98
99
    namespace {
100
101
    struct TemplateContent;
102
103
    }
104
105
    typedef ::std::vector< ::rtl::Reference< TemplateContent > >    TemplateFolderContent;
106
    typedef TemplateFolderContent::const_iterator           ConstFolderIterator;
107
    typedef TemplateFolderContent::iterator                 FolderIterator;
108
109
    namespace {
110
111
    /** a struct describing one content in one of the template dirs (or at least it's relevant aspects)
112
    */
113
    struct TemplateContent : public ::salhelper::SimpleReferenceObject
114
    {
115
    public:
116
117
    private:
118
        INetURLObject           m_aURL;
119
        util::DateTime          m_aLastModified;    // date of last modification as reported by UCP
120
        TemplateFolderContent   m_aSubContents;     // sorted (by name) list of the children
121
122
    private:
123
        void    implResetDate( )
124
0
        {
125
0
            m_aLastModified.NanoSeconds = m_aLastModified.Seconds = m_aLastModified.Minutes = m_aLastModified.Hours = 0;
126
0
            m_aLastModified.Day = m_aLastModified.Month = m_aLastModified.Year = 0;
127
0
        }
128
129
    private:
130
        virtual ~TemplateContent() override;
131
132
    public:
133
        explicit TemplateContent( INetURLObject _aURL );
134
135
        // attribute access
136
0
        OUString                 getURL( ) const                             { return m_aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); }
137
0
        void                     setModDate( const util::DateTime& _rDate )  { m_aLastModified = _rDate; }
138
0
        void                     setModDateNormalized( const util::DateTime& _rDate ) {
139
0
            auto norm = _rDate;
140
0
            norm.NanoSeconds
141
0
                = (norm.NanoSeconds / tools::Time::nanoPerCenti) * tools::Time::nanoPerCenti;
142
0
            setModDate(norm);
143
0
        }
144
0
        const util::DateTime&    getModDate( ) const                         { return m_aLastModified; }
145
146
0
        TemplateFolderContent&   getSubContents()            { return m_aSubContents; }
147
0
        const TemplateFolderContent& getSubContents() const  { return m_aSubContents; }
148
149
0
                ConstFolderIterator              end() const             { return m_aSubContents.end(); }
150
        TemplateFolderContent::size_type
151
0
                                        size() const    { return m_aSubContents.size(); }
152
153
        void                     push_back( const ::rtl::Reference< TemplateContent >& _rxNewElement )
154
0
                                                        { m_aSubContents.push_back( _rxNewElement ); }
155
    };
156
157
    }
158
159
    TemplateContent::TemplateContent( INetURLObject _aURL )
160
0
        :m_aURL(std::move( _aURL ))
161
0
    {
162
0
        DBG_ASSERT( INetProtocol::NotValid != m_aURL.GetProtocol(), "TemplateContent::TemplateContent: invalid URL!" );
163
0
        implResetDate();
164
0
    }
165
166
167
    TemplateContent::~TemplateContent()
168
0
    {
169
0
    }
170
171
172
    //= stl helpers
173
174
    namespace {
175
176
    /// compares two TemplateContent by URL
177
    struct TemplateContentURLLess
178
    {
179
        bool operator() ( const ::rtl::Reference< TemplateContent >& _rxLHS, const ::rtl::Reference< TemplateContent >& _rxRHS ) const
180
0
        {
181
0
            return _rxLHS->getURL() < _rxRHS->getURL();
182
0
        }
183
    };
184
185
186
    /// sorts the sib contents of a TemplateFolderContent
187
    struct SubContentSort
188
    {
189
        void operator() ( TemplateFolderContent& _rFolder ) const
190
0
        {
191
            // sort the directory by name
192
0
            ::std::sort(
193
0
                _rFolder.begin(),
194
0
                _rFolder.end(),
195
0
                TemplateContentURLLess()
196
0
            );
197
198
            // sort the sub directories by name
199
0
            ::std::for_each(
200
0
                _rFolder.begin(),
201
0
                _rFolder.end(),
202
0
                *this
203
0
            );
204
0
        }
205
206
        void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const
207
0
        {
208
0
            if ( _rxContent.is() && _rxContent->size() )
209
0
            {
210
0
                operator()( _rxContent->getSubContents() );
211
0
            }
212
0
        }
213
    };
214
215
    /** does a deep compare of two template contents
216
    */
217
    struct TemplateContentEqual
218
    {
219
220
        bool operator() (const ::rtl::Reference< TemplateContent >& _rLHS, const ::rtl::Reference< TemplateContent >& _rRHS )
221
0
        {
222
0
            if ( !_rLHS.is() || !_rRHS.is() )
223
0
            {
224
0
                OSL_FAIL( "TemplateContentEqual::operator(): invalid contents!" );
225
0
                return true;
226
                    // this is not strictly true, in case only one is invalid - but this is a heavy error anyway
227
0
            }
228
229
0
            if ( _rLHS->getURL() != _rRHS->getURL() )
230
0
                return false;
231
232
0
            if ( _rLHS->getModDate() != _rRHS->getModDate() )
233
0
                return false;
234
235
0
            if ( _rLHS->getSubContents().size() != _rRHS->getSubContents().size() )
236
0
                return false;
237
238
0
            if ( !_rLHS->getSubContents().empty() )
239
0
            {   // there are children
240
                // -> compare them
241
0
                ::std::pair< FolderIterator, FolderIterator > aFirstDifferent = ::std::mismatch(
242
0
                    _rLHS->getSubContents().begin(),
243
0
                    _rLHS->getSubContents().end(),
244
0
                    _rRHS->getSubContents().begin(),
245
0
                    *this
246
0
                );
247
0
                if ( aFirstDifferent.first != _rLHS->getSubContents().end() )
248
0
                    return false;// the sub contents differ
249
0
            }
250
251
0
            return true;
252
0
        }
253
    };
254
255
256
    /// base class for functors which act on a SvStream
257
    struct StorageHelper
258
    {
259
    protected:
260
        SvStream&   m_rStorage;
261
0
        explicit StorageHelper( SvStream& _rStorage ) : m_rStorage( _rStorage ) { }
262
    };
263
264
265
    struct StoreContentURL : public StorageHelper
266
    {
267
        uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs;
268
269
        StoreContentURL( SvStream& _rStorage,
270
                         uno::Reference<
271
                            util::XOfficeInstallationDirectories > xOfficeInstDirs )
272
0
        : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { }
273
274
        void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const
275
0
        {
276
            // use the base class operator with the local name of the content
277
0
            OUString sURL = _rxContent->getURL();
278
            // #116281# Keep office installation relocatable. Never store
279
            // any direct references to office installation directory.
280
0
            sURL = m_xOfficeInstDirs->makeRelocatableURL( sURL );
281
0
            m_rStorage.WriteUniOrByteString( sURL, m_rStorage.GetStreamCharSet() );
282
0
        }
283
    };
284
285
286
    /// functor which stores the complete content of a TemplateContent
287
    struct StoreFolderContent : public StorageHelper
288
    {
289
        uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs;
290
291
    public:
292
        StoreFolderContent( SvStream& _rStorage,
293
                         uno::Reference<
294
                            util::XOfficeInstallationDirectories > xOfficeInstDirs )
295
0
        : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { }
296
297
298
        void operator() ( const TemplateContent& _rContent ) const
299
0
        {
300
            // store the info about this content
301
0
            WriteDateTime( m_rStorage, _rContent.getModDate() );
302
303
            // store the info about the children
304
            // the number
305
0
            m_rStorage.WriteInt32( _rContent.size() );
306
            // their URLs ( the local name is not enough, since URL might be not a hierarchical one, "expand:" for example )
307
0
            ::std::for_each(
308
0
                _rContent.getSubContents().begin(),
309
0
                _rContent.getSubContents().end(),
310
0
                StoreContentURL( m_rStorage, m_xOfficeInstDirs )
311
0
            );
312
            // their content
313
0
            ::std::for_each(
314
0
                _rContent.getSubContents().begin(),
315
0
                _rContent.getSubContents().end(),
316
0
                *this
317
0
            );
318
0
        }
319
320
321
        void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const
322
0
        {
323
0
            if ( _rxContent.is() )
324
0
            {
325
0
                operator()( *_rxContent );
326
0
            }
327
0
        }
328
    };
329
330
331
    /// functor which reads a complete TemplateContent instance
332
    struct ReadFolderContent : public StorageHelper
333
    {
334
        uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs;
335
336
        ReadFolderContent( SvStream& _rStorage,
337
                         uno::Reference<
338
                            util::XOfficeInstallationDirectories > xOfficeInstDirs )
339
0
        : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { }
340
341
342
        void operator() ( TemplateContent& _rContent ) const
343
0
        {
344
            // store the info about this content
345
0
            util::DateTime aModDate;
346
0
            m_rStorage >> aModDate;
347
0
            _rContent.setModDate( aModDate );
348
349
            // store the info about the children
350
            // the number
351
0
            sal_Int32 nChildren = 0;
352
0
            m_rStorage.ReadInt32( nChildren );
353
0
            TemplateFolderContent& rChildren = _rContent.getSubContents();
354
0
            rChildren.resize( 0 );
355
0
            rChildren.reserve( nChildren );
356
            // initialize them with their (local) names
357
0
            while ( nChildren-- )
358
0
            {
359
0
                OUString sURL = m_rStorage.ReadUniOrByteString(m_rStorage.GetStreamCharSet());
360
0
                sURL = m_xOfficeInstDirs->makeAbsoluteURL( sURL );
361
0
                rChildren.push_back( new TemplateContent( INetURLObject( sURL ) ) );
362
0
            }
363
364
            // their content
365
0
            ::std::for_each(
366
0
                _rContent.getSubContents().begin(),
367
0
                _rContent.getSubContents().end(),
368
0
                *this
369
0
            );
370
0
        }
371
372
373
        void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const
374
0
        {
375
0
            if ( _rxContent.is() )
376
0
            {
377
0
                operator()( *_rxContent );
378
0
            }
379
0
        }
380
    };
381
382
    }
383
384
    //= TemplateFolderCacheImpl
385
386
    class TemplateFolderCacheImpl
387
    {
388
    private:
389
        TemplateFolderContent           m_aPreviousState;   // the current state of the template dirs (as found on the HD)
390
        TemplateFolderContent           m_aCurrentState;    // the previous state of the template dirs (as found in the cache file)
391
392
        std::mutex                      m_aMutex;
393
        // will be lazy inited; never access directly; use getOfficeInstDirs().
394
        uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs;
395
396
        std::unique_ptr<SvStream>       m_pCacheStream;
397
        bool                            m_bNeedsUpdate : 1;
398
        bool                            m_bKnowState : 1;
399
        bool                            m_bValidCurrentState : 1;
400
        bool                            m_bAutoStoreState : 1;
401
402
    public:
403
        explicit TemplateFolderCacheImpl( bool _bAutoStoreState );
404
        ~TemplateFolderCacheImpl( );
405
406
        bool        needsUpdate();
407
        void        storeState();
408
409
    private:
410
        bool        openCacheStream( bool _bForRead );
411
        void        closeCacheStream( );
412
413
        /// read the state of the dirs from the cache file
414
        bool        readPreviousState();
415
        /// read the current state of the dirs
416
        bool        readCurrentState();
417
418
        static OUString    implParseSmart( const OUString& _rPath );
419
420
        bool        implReadFolder( const ::rtl::Reference< TemplateContent >& _rxRoot );
421
422
        static  sal_Int32   getMagicNumber();
423
        static  void        normalize( TemplateFolderContent& _rState );
424
425
        // @return <TRUE/> if the states equal
426
        static  bool        equalStates( const TemplateFolderContent& _rLHS, const TemplateFolderContent& _rRHS );
427
428
        // late initialize m_xOfficeInstDirs
429
        const uno::Reference< util::XOfficeInstallationDirectories >& getOfficeInstDirs();
430
    };
431
432
433
    TemplateFolderCacheImpl::TemplateFolderCacheImpl( bool _bAutoStoreState )
434
0
        :m_bNeedsUpdate         ( true )
435
0
        ,m_bKnowState           ( false )
436
0
        ,m_bValidCurrentState   ( false )
437
0
        ,m_bAutoStoreState      ( _bAutoStoreState )
438
0
    {
439
0
    }
440
441
442
    TemplateFolderCacheImpl::~TemplateFolderCacheImpl( )
443
0
    {
444
        // store the current state if possible and required
445
0
        if ( m_bValidCurrentState && m_bAutoStoreState )
446
0
            storeState();
447
448
0
        closeCacheStream( );
449
0
    }
450
451
452
    sal_Int32 TemplateFolderCacheImpl::getMagicNumber()
453
0
    {
454
0
        return (sal_Int8('T') << 12)
455
0
                | (sal_Int8('D') << 8)
456
0
                | (sal_Int8('S') << 4)
457
0
                | (sal_Int8('C'));
458
0
    }
459
460
461
    void TemplateFolderCacheImpl::normalize( TemplateFolderContent& _rState )
462
0
    {
463
0
        SubContentSort()( _rState );
464
0
    }
465
466
467
    bool TemplateFolderCacheImpl::equalStates( const TemplateFolderContent& _rLHS, const TemplateFolderContent& _rRHS )
468
0
    {
469
0
        if ( _rLHS.size() != _rRHS.size() )
470
0
            return false;
471
472
        // as both arrays are sorted (by definition - this is a precondition of this method)
473
        // we can simply go from the front to the back and compare the single elements
474
475
0
        ::std::pair< ConstFolderIterator, ConstFolderIterator > aFirstDifferent = ::std::mismatch(
476
0
            _rLHS.begin(),
477
0
            _rLHS.end(),
478
0
            _rRHS.begin(),
479
0
            TemplateContentEqual()
480
0
        );
481
482
0
        return aFirstDifferent.first == _rLHS.end();
483
0
    }
484
485
486
    void TemplateFolderCacheImpl::storeState()
487
0
    {
488
0
        if ( !m_bValidCurrentState )
489
0
            readCurrentState( );
490
491
0
        if ( !(m_bValidCurrentState && openCacheStream( false )) )
492
0
            return;
493
494
0
        m_pCacheStream->WriteInt32( getMagicNumber() );
495
496
        // store the template root folders
497
        // the size
498
0
        m_pCacheStream->WriteInt32( m_aCurrentState.size() );
499
        // the complete URLs
500
0
        ::std::for_each(
501
0
            m_aCurrentState.begin(),
502
0
            m_aCurrentState.end(),
503
0
            StoreContentURL( *m_pCacheStream, getOfficeInstDirs() )
504
0
        );
505
506
        // the contents
507
0
        ::std::for_each(
508
0
            m_aCurrentState.begin(),
509
0
            m_aCurrentState.end(),
510
0
            StoreFolderContent( *m_pCacheStream, getOfficeInstDirs() )
511
0
        );
512
0
    }
513
514
515
    OUString TemplateFolderCacheImpl::implParseSmart( const OUString& _rPath )
516
0
    {
517
0
        INetURLObject aParser;
518
0
        aParser.SetSmartProtocol( INetProtocol::File );
519
0
        aParser.SetURL( _rPath );
520
0
        if ( INetProtocol::NotValid == aParser.GetProtocol() )
521
0
        {
522
0
            OUString sURL;
523
0
            osl::FileBase::getFileURLFromSystemPath( _rPath, sURL );
524
0
            aParser.SetURL( sURL );
525
0
        }
526
0
        return aParser.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
527
0
    }
528
529
530
    void TemplateFolderCacheImpl::closeCacheStream( )
531
0
    {
532
0
        m_pCacheStream.reset();
533
0
    }
534
535
536
    bool TemplateFolderCacheImpl::implReadFolder( const ::rtl::Reference< TemplateContent >& _rxRoot )
537
0
    {
538
0
        try
539
0
        {
540
            // create a content for the current folder root
541
0
            Reference< XResultSet > xResultSet;
542
0
            Sequence< OUString > aContentProperties{ u"Title"_ustr, u"DateModified"_ustr, u"DateCreated"_ustr,
543
0
                                                     u"IsFolder"_ustr };
544
545
            // get the set of sub contents in the folder
546
0
            try
547
0
            {
548
0
                Reference< XDynamicResultSet > xDynResultSet;
549
550
0
                ::ucbhelper::Content aTemplateRoot( _rxRoot->getURL(), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
551
0
                xDynResultSet = aTemplateRoot.createDynamicCursor( aContentProperties );
552
0
                if ( xDynResultSet.is() )
553
0
                    xResultSet = xDynResultSet->getStaticResultSet();
554
0
            }
555
0
            catch( CommandAbortedException& )
556
0
            {
557
0
                TOOLS_WARN_EXCEPTION( "svtools.misc", "" );
558
0
                return false;
559
0
            }
560
0
            catch( css::uno::Exception& )
561
0
            {
562
0
            }
563
564
            // collect the infos about the sub contents
565
0
            if ( xResultSet.is() )
566
0
            {
567
0
                Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );
568
0
                Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
569
570
0
                while ( xResultSet->next() )
571
0
                {
572
0
                    INetURLObject aSubContentURL( xContentAccess->queryContentIdentifierString() );
573
574
                    // a new content instance
575
0
                    ::rtl::Reference< TemplateContent > xChild = new TemplateContent( std::move(aSubContentURL) );
576
577
                    // the modified date
578
0
                    xChild->setModDateNormalized( xRow->getTimestamp( 2 ) );  // date modified
579
0
                    if ( xRow->wasNull() )
580
0
                        xChild->setModDateNormalized( xRow->getTimestamp( 3 ) );
581
                            // fallback: date created
582
583
                    // push back this content
584
0
                    _rxRoot->push_back( xChild );
585
586
                    // is it a folder?
587
0
                    if ( xRow->getBoolean( 4 ) && !xRow->wasNull() )
588
0
                    {   // yes -> step down
589
0
                        ConstFolderIterator aNextLevelRoot = _rxRoot->end();
590
0
                        --aNextLevelRoot;
591
0
                        implReadFolder( *aNextLevelRoot );
592
0
                    }
593
0
                }
594
0
            }
595
0
        }
596
0
        catch( const Exception& )
597
0
        {
598
0
            TOOLS_WARN_EXCEPTION( "svtools", "TemplateFolderCacheImpl::implReadFolder" );
599
0
            return false;
600
0
        }
601
0
        return true;
602
0
    }
603
604
605
    bool TemplateFolderCacheImpl::readCurrentState()
606
0
    {
607
        // reset
608
0
        m_bValidCurrentState = false;
609
0
        TemplateFolderContent aTemplateFolderContent;
610
0
        m_aCurrentState.swap( aTemplateFolderContent );
611
612
        // the template directories from the config
613
0
        const SvtPathOptions aPathOptions;
614
0
        const OUString& aDirs = aPathOptions.GetTemplatePath();
615
616
        // loop through all the root-level template folders
617
0
        sal_Int32 nIndex = 0;
618
0
        do
619
0
        {
620
0
            OUString sTemplatePath( aDirs.getToken(0, ';', nIndex) );
621
0
            sTemplatePath = aPathOptions.ExpandMacros( sTemplatePath );
622
623
            // Make sure excess ".." path segments (from expanding bootstrap
624
            // variables in paths) are normalized in the same way they are
625
            // normalized for paths read from the .templdir.cache file (where
626
            // paths have gone through makeRelocatable URL on writing out and
627
            // then through makeAbsoluteURL when reading back in), as otherwise
628
            // equalStates() in needsUpdate() could erroneously consider
629
            // m_aCurrentState and m_aPreviousState as different:
630
0
            sTemplatePath = getOfficeInstDirs()->makeAbsoluteURL(
631
0
                getOfficeInstDirs()->makeRelocatableURL(sTemplatePath));
632
633
            // create a new entry
634
0
            m_aCurrentState.push_back( new TemplateContent( INetURLObject( sTemplatePath ) ) );
635
0
            TemplateFolderContent::iterator aCurrentRoot = m_aCurrentState.end();
636
0
            --aCurrentRoot;
637
638
0
            if ( !implReadFolder( *aCurrentRoot ) )
639
0
                return false;
640
0
        }
641
0
        while ( nIndex >= 0 );
642
643
        // normalize the array (which basically means "sort it")
644
0
        normalize( m_aCurrentState );
645
646
0
        m_bValidCurrentState = true;
647
0
        return m_bValidCurrentState;
648
0
    }
649
650
651
    bool TemplateFolderCacheImpl::readPreviousState()
652
0
    {
653
0
        DBG_ASSERT( m_pCacheStream, "TemplateFolderCacheImpl::readPreviousState: not to be called without stream!" );
654
655
        // reset
656
0
        TemplateFolderContent aTemplateFolderContent;
657
0
        m_aPreviousState.swap( aTemplateFolderContent );
658
659
        // check the magic number
660
0
        sal_Int32 nMagic = 0;
661
0
        m_pCacheStream->ReadInt32( nMagic );
662
0
        DBG_ASSERT( getMagicNumber() == nMagic, "TemplateFolderCacheImpl::readPreviousState: invalid cache file!" );
663
0
        if ( getMagicNumber() != nMagic )
664
0
            return false;
665
666
        // the root directories
667
        // their number
668
0
        sal_Int32 nRootDirectories = 0;
669
0
        m_pCacheStream->ReadInt32( nRootDirectories );
670
        // init empty TemplateContents with the URLs
671
0
        m_aPreviousState.reserve( nRootDirectories );
672
0
        while ( nRootDirectories-- )
673
0
        {
674
0
            OUString sURL = m_pCacheStream->ReadUniOrByteString(m_pCacheStream->GetStreamCharSet());
675
            // #116281# Keep office installation relocatable. Never store
676
            // any direct references to office installation directory.
677
0
            sURL = getOfficeInstDirs()->makeAbsoluteURL( sURL );
678
0
            m_aPreviousState.push_back(
679
0
                new TemplateContent( INetURLObject(sURL) ) );
680
0
        }
681
682
        // read the contents of the root folders
683
0
        ::std::for_each(
684
0
            m_aPreviousState.begin(),
685
0
            m_aPreviousState.end(),
686
0
            ReadFolderContent( *m_pCacheStream, getOfficeInstDirs() )
687
0
        );
688
689
0
        DBG_ASSERT( !m_pCacheStream->GetErrorCode(), "TemplateFolderCacheImpl::readPreviousState: unknown error during reading the state cache!" );
690
691
        // normalize the array (which basically means "sort it")
692
0
        normalize( m_aPreviousState );
693
694
0
        return true;
695
0
    }
696
697
698
    bool TemplateFolderCacheImpl::openCacheStream( bool _bForRead )
699
0
    {
700
        // close any old stream instance
701
0
        closeCacheStream( );
702
703
        // get the storage directory
704
0
        OUString sStorageURL = implParseSmart( SvtPathOptions().GetStoragePath() );
705
0
        INetURLObject aStorageURL( sStorageURL );
706
0
        if ( INetProtocol::NotValid == aStorageURL.GetProtocol() )
707
0
        {
708
0
            OSL_FAIL( "TemplateFolderCacheImpl::openCacheStream: invalid storage path!" );
709
0
            return false;
710
0
        }
711
712
        // append our name
713
0
        aStorageURL.Append( u".templdir.cache" );
714
715
        // open the stream
716
0
        m_pCacheStream = UcbStreamHelper::CreateStream( aStorageURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),
717
0
            _bForRead ? StreamMode::READ | StreamMode::NOCREATE : StreamMode::WRITE | StreamMode::TRUNC );
718
0
        DBG_ASSERT( m_pCacheStream, "TemplateFolderCacheImpl::openCacheStream: could not open/create the cache stream!" );
719
0
        if ( m_pCacheStream && m_pCacheStream->GetErrorCode() )
720
0
        {
721
0
            m_pCacheStream.reset();
722
0
        }
723
724
0
        if ( m_pCacheStream )
725
0
            m_pCacheStream->SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
726
727
0
        return nullptr != m_pCacheStream;
728
0
    }
729
730
731
    bool TemplateFolderCacheImpl::needsUpdate()
732
0
    {
733
0
        if ( m_bKnowState )
734
0
            return m_bNeedsUpdate;
735
736
0
        m_bNeedsUpdate = true;
737
0
        m_bKnowState = true;
738
739
0
        if ( readCurrentState() )
740
0
        {
741
            // open the stream which contains the cached state of the directories
742
0
            if ( openCacheStream( true ) )
743
0
            {   // opening the stream succeeded
744
0
                if ( readPreviousState() )
745
0
                {
746
0
                    m_bNeedsUpdate = !equalStates( m_aPreviousState, m_aCurrentState );
747
0
                }
748
0
                else
749
0
                {
750
0
                    closeCacheStream();
751
0
                }
752
0
            }
753
0
        }
754
0
        return m_bNeedsUpdate;
755
0
    }
756
757
758
    const uno::Reference< util::XOfficeInstallationDirectories >&
759
    TemplateFolderCacheImpl::getOfficeInstDirs()
760
0
    {
761
0
        if ( !m_xOfficeInstDirs.is() )
762
0
        {
763
0
            std::lock_guard aGuard( m_aMutex );
764
0
            if ( !m_xOfficeInstDirs.is() )
765
0
            {
766
0
                const uno::Reference< uno::XComponentContext >& xCtx(
767
0
                    comphelper::getProcessComponentContext() );
768
0
                m_xOfficeInstDirs = util::theOfficeInstallationDirectories::get(xCtx);
769
0
            }
770
0
        }
771
0
        return m_xOfficeInstDirs;
772
0
    }
773
774
775
    //= TemplateFolderCache
776
777
778
    TemplateFolderCache::TemplateFolderCache( bool _bAutoStoreState )
779
0
        :m_pImpl( new TemplateFolderCacheImpl( _bAutoStoreState ) )
780
0
    {
781
0
    }
782
783
784
    TemplateFolderCache::~TemplateFolderCache( )
785
0
    {
786
0
    }
787
788
789
    bool TemplateFolderCache::needsUpdate()
790
0
    {
791
0
        return m_pImpl->needsUpdate();
792
0
    }
793
794
795
    void TemplateFolderCache::storeState()
796
0
    {
797
0
        m_pImpl->storeState();
798
0
    }
799
800
801
}   // namespace sfx2
802
803
804
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */