Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/appl/macroloader.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 <config_features.h>
21
22
#include <macroloader.hxx>
23
24
#include <com/sun/star/frame/DispatchResultState.hpp>
25
#include <basic/basmgr.hxx>
26
#include <basic/sbuno.hxx>
27
#include <basic/sberrors.hxx>
28
#include <cppuhelper/supportsservice.hxx>
29
#include <cppuhelper/weak.hxx>
30
#include <cppuhelper/weakref.hxx>
31
#include <framework/documentundoguard.hxx>
32
#include <sfx2/app.hxx>
33
#include <sfx2/frame.hxx>
34
#include <sfx2/objsh.hxx>
35
#include <tools/urlobj.hxx>
36
#include <vcl/svapp.hxx>
37
38
#include <memory>
39
40
using namespace ::com::sun::star;
41
using namespace ::com::sun::star::frame;
42
using namespace ::com::sun::star::lang;
43
using namespace ::com::sun::star::uno;
44
using namespace ::com::sun::star::util;
45
46
SfxMacroLoader::SfxMacroLoader(const css::uno::Sequence< css::uno::Any >& aArguments)
47
0
{
48
0
    Reference < XFrame > xFrame;
49
0
    if ( aArguments.hasElements() )
50
0
    {
51
0
        aArguments[0] >>= xFrame;
52
0
        m_xFrame = xFrame;
53
0
    }
54
0
}
55
56
OUString SAL_CALL SfxMacroLoader::getImplementationName()
57
0
{
58
0
    return u"com.sun.star.comp.sfx2.SfxMacroLoader"_ustr;
59
0
}
60
61
sal_Bool SAL_CALL SfxMacroLoader::supportsService(OUString const & ServiceName)
62
0
{
63
0
    return cppu::supportsService(this, ServiceName);
64
0
}
65
66
css::uno::Sequence<OUString> SAL_CALL SfxMacroLoader::getSupportedServiceNames()
67
0
{
68
0
    return { u"com.sun.star.frame.ProtocolHandler"_ustr };
69
0
}
70
71
SfxObjectShell* SfxMacroLoader::GetObjectShell(const Reference <XFrame>& xFrame)
72
0
{
73
0
    SfxObjectShell* pDocShell = nullptr;
74
75
0
    if ( xFrame.is() )
76
0
    {
77
0
        SfxFrame* pFrame=nullptr;
78
0
        for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) )
79
0
        {
80
0
            if ( pFrame->GetFrameInterface() == xFrame )
81
0
                break;
82
0
        }
83
84
0
        if ( pFrame )
85
0
            pDocShell = pFrame->GetCurrentDocument();
86
0
    }
87
88
0
    return pDocShell;
89
0
}
90
91
SfxObjectShell* SfxMacroLoader::GetObjectShell_Impl()
92
0
{
93
0
    Reference < XFrame > xFrame( m_xFrame.get(), UNO_QUERY );
94
0
    return SfxMacroLoader::GetObjectShell(xFrame);
95
0
}
96
97
uno::Reference<frame::XDispatch> SAL_CALL SfxMacroLoader::queryDispatch(
98
    const util::URL&   aURL            ,
99
    const OUString&               /*sTargetFrameName*/,
100
    sal_Int32                            /*nSearchFlags*/    )
101
0
{
102
0
    uno::Reference<frame::XDispatch> xDispatcher;
103
0
    if(aURL.Complete.startsWith("macro:"))
104
0
        xDispatcher = this;
105
0
    return xDispatcher;
106
0
}
107
108
109
uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL
110
                SfxMacroLoader::queryDispatches( const uno::Sequence < frame::DispatchDescriptor >& seqDescriptor )
111
0
{
112
0
    sal_Int32 nCount = seqDescriptor.getLength();
113
0
    uno::Sequence< uno::Reference<frame::XDispatch> > lDispatcher(nCount);
114
0
    std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(),
115
0
        [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> {
116
0
            return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
117
0
    return lDispatcher;
118
0
}
119
120
121
void SAL_CALL SfxMacroLoader::dispatchWithNotification(
122
    const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/,
123
    const uno::Reference<frame::XDispatchResultListener>& xListener )
124
0
{
125
0
    SolarMutexGuard aGuard;
126
127
0
    uno::Any aAny;
128
0
    ErrCode nErr = loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() );
129
0
    if( !xListener.is() )
130
0
        return;
131
132
    // always call dispatchFinished(), because we didn't load a document but
133
    // executed a macro instead!
134
0
    frame::DispatchResultEvent aEvent;
135
136
0
    aEvent.Source = getXWeak();
137
0
    if( nErr == ERRCODE_NONE )
138
0
        aEvent.State = frame::DispatchResultState::SUCCESS;
139
0
    else
140
0
        aEvent.State = frame::DispatchResultState::FAILURE;
141
142
0
    xListener->dispatchFinished( aEvent ) ;
143
0
}
144
145
uno::Any SAL_CALL SfxMacroLoader::dispatchWithReturnValue(
146
    const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& )
147
0
{
148
0
    uno::Any aRet;
149
0
    ErrCode nErr = loadMacro( aURL.Complete, aRet, GetObjectShell_Impl() );
150
151
    // aRet gets set to a different value only if nErr == ERRCODE_NONE
152
    // Return it in such case to preserve the original behaviour
153
154
    // In all other cases (nErr != ERRCODE_NONE), the calling code gets
155
    // the actual error code back
156
0
    if ( nErr != ERRCODE_NONE )
157
0
    {
158
0
        beans::PropertyValue aErrorCode;
159
160
0
        aErrorCode.Name = "ErrorCode";
161
0
        aErrorCode.Value <<= sal_uInt32(nErr);
162
163
0
        aRet <<= aErrorCode;
164
0
    }
165
166
0
    return aRet;
167
0
}
168
169
void SAL_CALL SfxMacroLoader::dispatch(
170
    const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/ )
171
0
{
172
0
    SolarMutexGuard aGuard;
173
174
0
    uno::Any aAny;
175
0
    loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() );
176
0
}
177
178
void SAL_CALL SfxMacroLoader::addStatusListener(
179
    const uno::Reference< frame::XStatusListener >& ,
180
    const util::URL&                                                    )
181
0
{
182
    /* TODO
183
            How we can handle different listener for further coming or currently running dispatch() jobs
184
            without any inconsistency!
185
     */
186
0
}
187
188
189
void SAL_CALL SfxMacroLoader::removeStatusListener(
190
    const uno::Reference< frame::XStatusListener >&,
191
    const util::URL&                                                  )
192
0
{
193
0
}
194
195
ErrCode SfxMacroLoader::loadMacro( const OUString& rURL, css::uno::Any& rRetval, SfxObjectShell* pSh )
196
0
{
197
0
#if !HAVE_FEATURE_SCRIPTING
198
0
    (void) rURL;
199
0
    (void) rRetval;
200
0
    (void) pSh;
201
0
    return ERRCODE_BASIC_PROC_UNDEFINED;
202
#else
203
    SfxObjectShell* pCurrent = pSh;
204
    if ( !pCurrent )
205
        // all not full qualified names use the BASIC of the given or current document
206
        pCurrent = SfxObjectShell::Current();
207
208
    // 'macro:///lib.mod.proc(args)' => macro of App-BASIC
209
    // 'macro://[docname|.]/lib.mod.proc(args)' => macro of current or qualified document
210
    // 'macro://obj.method(args)' => direct API call, execute it via App-BASIC
211
    const OUString& aMacro( rURL );
212
    sal_Int32 nThirdSlashPos = aMacro.indexOf( '/', 8 );
213
    sal_Int32 nArgsPos = aMacro.indexOf( '(' );
214
    BasicManager *pAppMgr = SfxApplication::GetBasicManager();
215
    BasicManager *pBasMgr = nullptr;
216
    ErrCode nErr = ERRCODE_NONE;
217
218
    // should a macro function be executed ( no direct API call)?
219
    if ( -1 != nThirdSlashPos && ( -1 == nArgsPos || nThirdSlashPos < nArgsPos ) )
220
    {
221
        // find BasicManager
222
        SfxObjectShell* pDoc = nullptr;
223
        OUString aBasMgrName( INetURLObject::decode(aMacro.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset) );
224
        if ( aBasMgrName.isEmpty() )
225
            pBasMgr = pAppMgr;
226
        else if ( aBasMgrName == "." )
227
        {
228
            // current/actual document
229
            pDoc = pCurrent;
230
            if (pDoc)
231
                pBasMgr = pDoc->GetBasicManager();
232
        }
233
        else
234
        {
235
            // full qualified name, find document by name
236
            for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst();
237
                    pObjSh && !pBasMgr;
238
                    pObjSh = SfxObjectShell::GetNext(*pObjSh) )
239
                if ( aBasMgrName == pObjSh->GetTitle(SFX_TITLE_APINAME) )
240
                {
241
                    pDoc = pObjSh;
242
                    pBasMgr = pDoc->GetBasicManager();
243
                }
244
        }
245
246
        if ( pBasMgr )
247
        {
248
            const bool bIsAppBasic = ( pBasMgr == pAppMgr );
249
            const bool bIsDocBasic = ( pBasMgr != pAppMgr );
250
251
            if ( pDoc )
252
            {
253
                // security check for macros from document basic if an SFX doc is given
254
                if ( !pDoc->AdjustMacroMode() )
255
                    // check forbids execution
256
                    return ERRCODE_IO_ACCESSDENIED;
257
            }
258
259
            // find BASIC method
260
            OUString aQualifiedMethod( INetURLObject::decode(aMacro.subView( nThirdSlashPos+1 ), INetURLObject::DecodeMechanism::WithCharset) );
261
            OUString aArgs;
262
            if ( -1 != nArgsPos )
263
            {
264
                // remove arguments from macro name
265
                aArgs = aQualifiedMethod.copy( nArgsPos - nThirdSlashPos - 1 );
266
                aQualifiedMethod = aQualifiedMethod.copy( 0, nArgsPos - nThirdSlashPos - 1 );
267
            }
268
269
            if ( pBasMgr->HasMacro( aQualifiedMethod ) )
270
            {
271
                Any aOldThisComponent;
272
                const bool bSetDocMacroMode = ( pDoc != nullptr ) && bIsDocBasic;
273
                const bool bSetGlobalThisComponent = ( pDoc != nullptr ) && bIsAppBasic;
274
                if ( bSetDocMacroMode )
275
                {
276
                    // mark document: it executes an own macro, so it's in a modal mode
277
                    pDoc->SetMacroMode_Impl();
278
                }
279
280
                if ( bSetGlobalThisComponent )
281
                {
282
                    // document is executed via AppBASIC, adjust ThisComponent variable
283
                    pAppMgr->SetGlobalUNOConstant( u"ThisComponent"_ustr, Any( pDoc->GetModel() ), &aOldThisComponent );
284
                }
285
286
                // just to let the shell be alive
287
                SfxObjectShellRef xKeepDocAlive = pDoc;
288
289
                {
290
                    // attempt to protect the document against the script tampering with its Undo Context
291
                    std::optional< ::framework::DocumentUndoGuard > pUndoGuard;
292
                    if ( bIsDocBasic )
293
                        pUndoGuard.emplace( pDoc->GetModel() );
294
295
                    // execute the method
296
                    SbxVariableRef retValRef = new SbxVariable;
297
                    nErr = pBasMgr->ExecuteMacro( aQualifiedMethod, aArgs, retValRef.get() );
298
                    if ( nErr == ERRCODE_NONE )
299
                        rRetval = sbxToUnoValue( retValRef.get() );
300
                }
301
302
                if ( bSetGlobalThisComponent )
303
                {
304
                    pAppMgr->SetGlobalUNOConstant( u"ThisComponent"_ustr, aOldThisComponent );
305
                }
306
307
                if ( bSetDocMacroMode )
308
                {
309
                    // remove flag for modal mode
310
                    pDoc->SetMacroMode_Impl( false );
311
                }
312
            }
313
            else
314
                nErr = ERRCODE_BASIC_PROC_UNDEFINED;
315
        }
316
        else
317
            nErr = ERRCODE_IO_NOTEXISTS;
318
    }
319
    else
320
    {
321
        // direct API call on a specified object
322
        OUString aCall =
323
            "[" +
324
            INetURLObject::decode(aMacro.subView(6),
325
                    INetURLObject::DecodeMechanism::WithCharset) +
326
            "]";
327
        pAppMgr->GetLib(0)->Execute(aCall);
328
        nErr = SbxBase::GetError();
329
    }
330
331
    SbxBase::ResetError();
332
    return nErr;
333
#endif
334
0
}
335
336
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
337
com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation(
338
    css::uno::XComponentContext *,
339
    css::uno::Sequence<css::uno::Any> const &arguments)
340
0
{
341
0
    return cppu::acquire(new SfxMacroLoader(arguments));
342
0
}
343
344
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */