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