Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/jobs/jobdata.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 <string_view>
23
24
#include <jobs/configaccess.hxx>
25
#include <jobs/jobdata.hxx>
26
#include <classes/converter.hxx>
27
28
#include <com/sun/star/beans/XPropertySet.hpp>
29
#include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp>
30
#include <com/sun/star/container/XNameAccess.hpp>
31
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
32
33
#include <tools/wldcrd.hxx>
34
#include <unotools/configpaths.hxx>
35
#include <utility>
36
#include <vcl/svapp.hxx>
37
38
namespace framework{
39
40
/**
41
    @short      standard ctor
42
    @descr      It initialize this new instance.
43
                But for real working it's necessary to call setAlias() or setService() later.
44
                Because we need the job data ...
45
46
    @param      rxContext
47
                    reference to the uno service manager
48
*/
49
JobData::JobData( css::uno::Reference< css::uno::XComponentContext > xContext )
50
0
    : m_xContext    (std::move(xContext                    ))
51
0
{
52
    // share code for member initialization with defaults!
53
0
    impl_reset();
54
0
}
55
56
/**
57
    @short  copy ctor
58
    @descr  Sometimes such job data container must be moved from one using place
59
            to another one. Then a copy ctor and copy operator must be available.
60
61
    @param  rCopy
62
                the original instance, from which we must copy all data
63
*/
64
JobData::JobData( const JobData& rCopy )
65
0
{
66
    // use the copy operator to share the same code
67
0
    *this = rCopy;
68
0
}
69
70
/**
71
    @short  operator for copying JobData instances
72
    @descr  Sometimes such job data container must be moved from one using place
73
            to another one. Then a copy ctor and copy operator must be available.
74
75
    @param  rCopy
76
                the original instance, from which we must copy all data
77
*/
78
JobData& JobData::operator=( const JobData& rCopy )
79
0
{
80
    // Please don't copy the uno service manager reference.
81
    // That can change the uno context, which isn't a good idea!
82
0
    m_eMode                = rCopy.m_eMode;
83
0
    m_eEnvironment         = rCopy.m_eEnvironment;
84
0
    m_sAlias               = rCopy.m_sAlias;
85
0
    m_sService             = rCopy.m_sService;
86
0
    m_sContext             = rCopy.m_sContext;
87
0
    m_sEvent               = rCopy.m_sEvent;
88
0
    m_lArguments           = rCopy.m_lArguments;
89
0
    return *this;
90
0
}
91
92
/**
93
    @short  let this instance die
94
    @descr  There is no chance any longer to work. We have to
95
            release all used resources and free used memory.
96
*/
97
JobData::~JobData()
98
0
{
99
0
    impl_reset();
100
0
}
101
102
/**
103
    @short      initialize this instance as a job with configuration
104
    @descr      They given alias can be used to address some configuration data.
105
                We read it and fill our internal structures. Of course old information
106
                will be lost doing so.
107
108
    @param      sAlias
109
                    the alias name of this job, used to locate job properties inside cfg
110
*/
111
void JobData::setAlias( const OUString& sAlias )
112
0
{
113
    // delete all old information! Otherwise we mix it with the new one ...
114
0
    impl_reset();
115
116
    // take over the new information
117
0
    m_sAlias   = sAlias;
118
0
    m_eMode    = E_ALIAS;
119
120
    // try to open the configuration set of this job directly and get a property access to it
121
    // We open it readonly here
122
0
    ConfigAccess aConfig(
123
0
        m_xContext,
124
0
        ("/org.openoffice.Office.Jobs/Jobs/"
125
0
         + utl::wrapConfigurationElementName(m_sAlias)));
126
0
    aConfig.open(ConfigAccess::E_READONLY);
127
0
    if (aConfig.getMode()==ConfigAccess::E_CLOSED)
128
0
    {
129
0
        impl_reset();
130
0
        return;
131
0
    }
132
133
0
    css::uno::Reference< css::beans::XPropertySet > xJobProperties(aConfig.cfg(), css::uno::UNO_QUERY);
134
0
    if (xJobProperties.is())
135
0
    {
136
0
        css::uno::Any aValue;
137
138
        // read uno implementation name
139
0
        aValue   = xJobProperties->getPropertyValue(u"Service"_ustr);
140
0
        aValue >>= m_sService;
141
142
        // read module context list
143
0
        aValue   = xJobProperties->getPropertyValue(u"Context"_ustr);
144
0
        aValue >>= m_sContext;
145
146
        // read whole argument list
147
0
        aValue = xJobProperties->getPropertyValue(u"Arguments"_ustr);
148
0
        css::uno::Reference< css::container::XNameAccess > xArgumentList;
149
0
        if (
150
0
            (aValue >>= xArgumentList)  &&
151
0
            (xArgumentList.is()      )
152
0
           )
153
0
        {
154
0
            css::uno::Sequence< OUString > lArgumentNames = xArgumentList->getElementNames();
155
0
            sal_Int32                             nCount         = lArgumentNames.getLength();
156
0
            m_lArguments.resize(nCount);
157
0
            for (sal_Int32 i=0; i<nCount; ++i)
158
0
            {
159
0
                m_lArguments[i].Name  = lArgumentNames[i];
160
0
                m_lArguments[i].Value = xArgumentList->getByName(m_lArguments[i].Name);
161
0
            }
162
0
        }
163
0
    }
164
165
0
    aConfig.close();
166
0
}
167
168
/**
169
    @short      initialize this instance as a job without configuration
170
    @descr      This job has no configuration data. We have to forget all old information
171
                and set only some of them new, so this instance can work.
172
173
    @param      sService
174
                    the uno service name of this "non configured" job
175
*/
176
void JobData::setService( const OUString& sService )
177
0
{
178
    // delete all old information! Otherwise we mix it with the new one ...
179
0
    impl_reset();
180
    // take over the new information
181
0
    m_sService = sService;
182
0
    m_eMode    = E_SERVICE;
183
0
}
184
185
/**
186
    @short      initialize this instance with new job values.
187
    @descr      It reads automatically all properties of the specified
188
                job (using it's alias name) and "register it" for the
189
                given event. This registration will not be validated against
190
                the underlying configuration! (That must be done from outside.
191
                Because the caller must have the configuration already open to
192
                get the values for sEvent and sAlias! And doing so it can perform
193
                only, if the time stamp values are read outside too.
194
                Further it makes no sense to initialize and start a disabled job.
195
                So this initialization method will be called for enabled jobs only.)
196
197
    @param      sEvent
198
                    the triggered event, for which this job should be started
199
200
    @param      sAlias
201
                    mark the required job inside event registration list
202
*/
203
void JobData::setEvent( const OUString& sEvent ,
204
                        const OUString& sAlias )
205
0
{
206
    // share code to read all job properties!
207
0
    setAlias(sAlias);
208
209
    // take over the new information - which differ against set one of method setAlias()!
210
0
    m_sEvent = sEvent;
211
0
    m_eMode  = E_EVENT;
212
0
}
213
214
/**
215
    @short      set the new job specific arguments
216
    @descr      If a job finish his work, it can give us a new list of arguments (which
217
                will not interpreted by us). We write it back to the configuration only
218
                (if this job has its own configuration!).
219
                So a job can have persistent data without implementing anything
220
                or define own config areas for that.
221
222
    @param      lArguments
223
                    list of arguments, which should be set for this job
224
 */
225
void JobData::setJobConfig( std::vector< css::beans::NamedValue >&& lArguments )
226
0
{
227
    // update member
228
0
    m_lArguments = std::move(lArguments);
229
230
    // update the configuration ... if possible!
231
0
    if (m_eMode!=E_ALIAS)
232
0
        return;
233
234
    // It doesn't matter if this config object was already opened before.
235
    // It doesn nothing here then ... or it change the mode automatically, if
236
    // it was opened using another one before.
237
0
    ConfigAccess aConfig(
238
0
        m_xContext,
239
0
        ("/org.openoffice.Office.Jobs/Jobs/"
240
0
         + utl::wrapConfigurationElementName(m_sAlias)));
241
0
    aConfig.open(ConfigAccess::E_READWRITE);
242
0
    if (aConfig.getMode()==ConfigAccess::E_CLOSED)
243
0
        return;
244
245
0
    css::uno::Reference< css::beans::XMultiHierarchicalPropertySet > xArgumentList(aConfig.cfg(), css::uno::UNO_QUERY);
246
0
    if (xArgumentList.is())
247
0
    {
248
0
        sal_Int32                             nCount = m_lArguments.size();
249
0
        css::uno::Sequence< OUString > lNames (nCount);
250
0
        auto lNamesRange = asNonConstRange(lNames);
251
0
        css::uno::Sequence< css::uno::Any >   lValues(nCount);
252
0
        auto lValuesRange = asNonConstRange(lValues);
253
254
0
        for (sal_Int32 i=0; i<nCount; ++i)
255
0
        {
256
0
            lNamesRange [i] = m_lArguments[i].Name;
257
0
            lValuesRange[i] = m_lArguments[i].Value;
258
0
        }
259
260
0
        xArgumentList->setHierarchicalPropertyValues(lNames, lValues);
261
0
    }
262
0
    aConfig.close();
263
0
}
264
265
/**
266
    @short  set a new environment descriptor for this job
267
    @descr  It must(!) be done every time this container is initialized
268
            with new job data e.g.: setAlias()/setEvent()/setService() ...
269
            Otherwise the environment will be unknown!
270
 */
271
void JobData::setEnvironment( EEnvironment eEnvironment )
272
0
{
273
0
    m_eEnvironment = eEnvironment;
274
0
}
275
276
/**
277
    @short      these functions provides access to our internal members
278
    @descr      These member represent any information about the job
279
                and can be used from outside to e.g. start a job.
280
 */
281
JobData::EMode JobData::getMode() const
282
0
{
283
0
    return m_eMode;
284
0
}
285
286
JobData::EEnvironment JobData::getEnvironment() const
287
0
{
288
0
    return m_eEnvironment;
289
0
}
290
291
OUString JobData::getEnvironmentDescriptor() const
292
0
{
293
0
    OUString sDescriptor;
294
0
    switch(m_eEnvironment)
295
0
    {
296
0
        case E_EXECUTION :
297
0
            sDescriptor = "EXECUTOR";
298
0
            break;
299
300
0
        case E_DISPATCH :
301
0
            sDescriptor = "DISPATCH";
302
0
            break;
303
304
0
        case E_DOCUMENTEVENT :
305
0
            sDescriptor = "DOCUMENTEVENT";
306
0
            break;
307
0
        default:
308
0
            break;
309
0
    }
310
0
    return sDescriptor;
311
0
}
312
313
const OUString & JobData::getService() const
314
0
{
315
0
    return m_sService;
316
0
}
317
318
const OUString & JobData::getEvent() const
319
0
{
320
0
    return m_sEvent;
321
0
}
322
323
const std::vector< css::beans::NamedValue > & JobData::getJobConfig() const
324
0
{
325
0
    return m_lArguments;
326
0
}
327
328
css::uno::Sequence< css::beans::NamedValue > JobData::getConfig() const
329
0
{
330
0
    css::uno::Sequence< css::beans::NamedValue > lConfig;
331
0
    if (m_eMode==E_ALIAS)
332
0
    {
333
0
        lConfig = { { u"Alias"_ustr, css::uno::Any(m_sAlias) },
334
0
                    { u"Service"_ustr, css::uno::Any(m_sService) },
335
0
                    { u"Context"_ustr, css::uno::Any(m_sContext) } };
336
0
    }
337
0
    return lConfig;
338
0
}
339
340
/**
341
    @short  return information, if this job is part of the global configuration package
342
            org.openoffice.Office.Jobs
343
    @descr  Because jobs can be executed by the dispatch framework using a uno service name
344
            directly - an executed job must not have any configuration really. Such jobs
345
            must provide the right interfaces only! But after finishing jobs can return
346
            some information (e.g. for updating her configuration ...). We must know
347
            if such request is valid or not then.
348
349
    @return sal_True if the represented job is part of the underlying configuration package.
350
 */
351
bool JobData::hasConfig() const
352
0
{
353
0
    return (m_eMode==E_ALIAS || m_eMode==E_EVENT);
354
0
}
355
356
/**
357
    @short      mark a job as non startable for further requests
358
    @descr      We don't remove the configuration entry! We set a timestamp value only.
359
                And there exist two of them: one for an administrator... and one for the
360
                current user. We change it for the user layer only. So this JobDispatch can't be
361
                started any more... till the administrator change his timestamp.
362
                That can be useful for post setup scenarios, which must run one time only.
363
364
                Note: This method don't do anything, if this represented job doesn't have a configuration!
365
 */
366
void JobData::disableJob()
367
0
{
368
    // No configuration - not used from EXECUTOR and not triggered from an event => no chance!
369
0
    if (m_eMode!=E_EVENT)
370
0
        return;
371
372
    // update the configuration
373
    // It doesn't matter if this config object was already opened before.
374
    // It doesn nothing here then ... or it change the mode automatically, if
375
    // it was opened using another one before.
376
0
    ConfigAccess aConfig(
377
0
        m_xContext,
378
0
        ("/org.openoffice.Office.Jobs/Events/"
379
0
         + utl::wrapConfigurationElementName(m_sEvent) + "/JobList/"
380
0
         + utl::wrapConfigurationElementName(m_sAlias)));
381
0
    aConfig.open(ConfigAccess::E_READWRITE);
382
0
    if (aConfig.getMode()==ConfigAccess::E_CLOSED)
383
0
        return;
384
385
0
    css::uno::Reference< css::beans::XPropertySet > xPropSet(aConfig.cfg(), css::uno::UNO_QUERY);
386
0
    if (xPropSet.is())
387
0
    {
388
        // Convert and write the user timestamp to the configuration.
389
0
        css::uno::Any aValue;
390
0
        aValue <<= Converter::convert_DateTime2ISO8601(DateTime( DateTime::SYSTEM));
391
0
        xPropSet->setPropertyValue(u"UserTime"_ustr, aValue);
392
0
    }
393
394
0
    aConfig.close();
395
0
}
396
397
static bool isEnabled( std::u16string_view sAdminTime ,
398
                    std::u16string_view sUserTime  )
399
0
{
400
    /*Attention!
401
        To prevent interpreting of TriGraphs inside next const string value,
402
        we have to encode all '?' signs. Otherwise e.g. "??-" will be translated
403
        to "~" ...
404
     */
405
0
    WildCard aISOPattern(u"\?\?\?\?-\?\?-\?\?*");
406
407
0
    bool bValidAdmin = aISOPattern.Matches(sAdminTime);
408
0
    bool bValidUser  = aISOPattern.Matches(sUserTime );
409
410
    // We check for "isEnabled()" here only.
411
    // Note further: ISO8601 formatted strings can be compared as strings directly!
412
    //               FIXME: this is not true! "T1215" is the same time as "T12:15" or "T121500"
413
0
    return (
414
0
            (!bValidAdmin && !bValidUser                         ) ||
415
0
            ( bValidAdmin &&  bValidUser && sAdminTime>=sUserTime)
416
0
           );
417
0
}
418
419
void JobData::appendEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >&              rxContext,
420
                                         const OUString&                                                 sEvent ,
421
                                               ::std::vector< JobData::TJob2DocEventBinding >& lJobs  )
422
0
{
423
0
    std::vector< OUString > lAdditionalJobs = JobData::getEnabledJobsForEvent(rxContext, sEvent);
424
0
    sal_Int32                c               = lAdditionalJobs.size();
425
0
    sal_Int32                i               = 0;
426
427
0
    for (i=0; i<c; ++i)
428
0
    {
429
0
        JobData::TJob2DocEventBinding aBinding(lAdditionalJobs[i], sEvent);
430
0
        lJobs.push_back(aBinding);
431
0
    }
432
0
}
433
434
bool JobData::hasCorrectContext(std::u16string_view rModuleIdent) const
435
0
{
436
0
    sal_Int32 nContextLen  = m_sContext.getLength();
437
0
    sal_Int32 nModuleIdLen = rModuleIdent.size();
438
439
0
    if ( nContextLen == 0 )
440
0
        return true;
441
442
0
    if ( nModuleIdLen > 0 )
443
0
    {
444
0
        sal_Int32 nIndex = m_sContext.indexOf( rModuleIdent );
445
0
        if ( nIndex >= 0 && ( nIndex+nModuleIdLen <= nContextLen ))
446
0
        {
447
0
            std::u16string_view sContextModule = m_sContext.subView( nIndex, nModuleIdLen );
448
0
            return sContextModule == rModuleIdent;
449
0
        }
450
0
    }
451
452
0
    return false;
453
0
}
454
455
std::vector< OUString > JobData::getEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
456
                                                                       std::u16string_view                                sEvent )
457
0
{
458
    // create a config access to "/org.openoffice.Office.Jobs/Events"
459
0
    ConfigAccess aConfig(rxContext, u"/org.openoffice.Office.Jobs/Events"_ustr);
460
0
    aConfig.open(ConfigAccess::E_READONLY);
461
0
    if (aConfig.getMode()==ConfigAccess::E_CLOSED)
462
0
        return std::vector< OUString >();
463
464
0
    css::uno::Reference< css::container::XHierarchicalNameAccess > xEventRegistry(aConfig.cfg(), css::uno::UNO_QUERY);
465
0
    if (!xEventRegistry.is())
466
0
        return std::vector< OUString >();
467
468
    // check if the given event exist inside list of registered ones
469
0
    OUString sPath(OUString::Concat(sEvent) + "/JobList");
470
0
    if (!xEventRegistry->hasByHierarchicalName(sPath))
471
0
        return std::vector< OUString >();
472
473
    // step to the job list, which is a child of the event node inside cfg
474
    // e.g. "/org.openoffice.Office.Jobs/Events/<event name>/JobList"
475
0
    css::uno::Any aJobList = xEventRegistry->getByHierarchicalName(sPath);
476
0
    css::uno::Reference< css::container::XNameAccess > xJobList;
477
0
    if (!(aJobList >>= xJobList) || !xJobList.is())
478
0
        return std::vector< OUString >();
479
480
    // get all alias names of jobs, which are part of this job list
481
    // But Some of them can be disabled by its timestamp values.
482
    // We create an additional job name list with the same size, then the original list...
483
    // step over all job entries... check her timestamps... and put only job names to the
484
    // destination list, which represent an enabled job.
485
0
    const css::uno::Sequence< OUString > lAllJobs = xJobList->getElementNames();
486
0
    sal_Int32 c = lAllJobs.getLength();
487
488
0
    std::vector< OUString > lEnabledJobs(c);
489
0
    sal_Int32 d = 0;
490
491
0
    for (OUString const & jobName : lAllJobs)
492
0
    {
493
0
        css::uno::Reference< css::beans::XPropertySet > xJob;
494
0
        if (
495
0
            !(xJobList->getByName(jobName) >>= xJob) ||
496
0
            !(xJob.is()     )
497
0
           )
498
0
        {
499
0
           continue;
500
0
        }
501
502
0
        OUString sAdminTime;
503
0
        xJob->getPropertyValue(u"AdminTime"_ustr) >>= sAdminTime;
504
505
0
        OUString sUserTime;
506
0
        xJob->getPropertyValue(u"UserTime"_ustr) >>= sUserTime;
507
508
0
        if (!isEnabled(sAdminTime, sUserTime))
509
0
            continue;
510
511
0
        lEnabledJobs[d] = jobName;
512
0
        ++d;
513
0
    }
514
0
    lEnabledJobs.resize(d);
515
516
0
    aConfig.close();
517
518
0
    return lEnabledJobs;
519
0
}
520
521
/**
522
    @short      reset all internal structures
523
    @descr      If someone recycles this instance, he can switch from one
524
                using mode to another one. But then we have to reset all currently
525
                used information. Otherwise we mix it and they can make trouble.
526
527
                But note: that does not set defaults for internal used members, which
528
                does not relate to any job property! e.g. the reference to the global
529
                uno service manager. Such information is used for internal processes only
530
                and are necessary for our work.
531
 */
532
void JobData::impl_reset()
533
0
{
534
0
    m_eMode        = E_UNKNOWN_MODE;
535
0
    m_eEnvironment = E_UNKNOWN_ENVIRONMENT;
536
0
    m_sAlias.clear();
537
0
    m_sService.clear();
538
0
    m_sContext.clear();
539
0
    m_sEvent.clear();
540
0
    m_lArguments.clear();
541
0
}
542
543
} // namespace framework
544
545
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */