/src/libreoffice/sfx2/source/notify/eventsupplier.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 <com/sun/star/beans/PropertyValue.hpp> |
21 | | |
22 | | #include <com/sun/star/util/URL.hpp> |
23 | | #include <com/sun/star/frame/Desktop.hpp> |
24 | | #include <com/sun/star/uno/Sequence.hxx> |
25 | | #include <com/sun/star/util/URLTransformer.hpp> |
26 | | #include <com/sun/star/util/XURLTransformer.hpp> |
27 | | #include <tools/urlobj.hxx> |
28 | | #include <svl/macitem.hxx> |
29 | | #include <sfx2/objsh.hxx> |
30 | | #include <sfx2/evntconf.hxx> |
31 | | #include <unotools/eventcfg.hxx> |
32 | | #include <sal/log.hxx> |
33 | | |
34 | | #include <comphelper/processfactory.hxx> |
35 | | #include <comphelper/namedvaluecollection.hxx> |
36 | | #include <comphelper/sequence.hxx> |
37 | | #include <officecfg/Office/Common.hxx> |
38 | | #include <eventsupplier.hxx> |
39 | | |
40 | | #include <sfx2/app.hxx> |
41 | | |
42 | | #include <sfx2/viewfrm.hxx> |
43 | | #include <sfx2/frame.hxx> |
44 | | #include <macroloader.hxx> |
45 | | |
46 | | #include <unicode/errorcode.h> |
47 | | #include <unicode/regex.h> |
48 | | #include <unicode/unistr.h> |
49 | | |
50 | | using namespace css; |
51 | | using namespace ::com::sun::star; |
52 | | |
53 | | |
54 | | |
55 | | // --- XNameReplace --- |
56 | | |
57 | | void SAL_CALL SfxEvents_Impl::replaceByName( const OUString & aName, const uno::Any & rElement ) |
58 | 17 | { |
59 | 17 | std::unique_lock aGuard( maMutex ); |
60 | | |
61 | | // find the event in the list and replace the data |
62 | 17 | auto nIndex = comphelper::findValue(maEventNames, aName); |
63 | 17 | if (nIndex == -1) |
64 | 0 | throw container::NoSuchElementException(); |
65 | | |
66 | | // check for correct type of the element |
67 | 17 | if ( !::comphelper::NamedValueCollection::canExtractFrom( rElement ) ) |
68 | 0 | throw lang::IllegalArgumentException(); |
69 | 17 | ::comphelper::NamedValueCollection const aEventDescriptor( rElement ); |
70 | | |
71 | | // create Configuration at first, creation might call this method also and that would overwrite everything |
72 | | // we might have stored before! |
73 | 17 | if ( mpObjShell && !mpObjShell->IsLoading() ) |
74 | 0 | { |
75 | | // SetModified will end up calling into our documentEventOccured method |
76 | 0 | aGuard.unlock(); |
77 | 0 | mpObjShell->SetModified(); |
78 | 0 | aGuard.lock(); |
79 | 0 | } |
80 | | |
81 | 17 | ::comphelper::NamedValueCollection aNormalizedDescriptor; |
82 | 17 | NormalizeMacro( aEventDescriptor, aNormalizedDescriptor, mpObjShell ); |
83 | | |
84 | 17 | OUString sType; |
85 | 17 | if ( ( aNormalizedDescriptor.size() == 1 ) |
86 | 0 | && !aNormalizedDescriptor.has( PROP_EVENT_TYPE ) //TODO |
87 | 0 | && ( aNormalizedDescriptor.get( PROP_EVENT_TYPE ) >>= sType ) |
88 | 0 | && ( sType.isEmpty() ) |
89 | 17 | ) |
90 | 0 | { |
91 | | // An empty event type means no binding. Therefore reset data |
92 | | // to reflect that state. |
93 | | // (that's for compatibility only. Nowadays, the Tools/Customize dialog should |
94 | | // set an empty sequence to indicate the request for resetting the assignment.) |
95 | 0 | OSL_ENSURE( false, "legacy event assignment format detected" ); |
96 | 0 | aNormalizedDescriptor.clear(); |
97 | 0 | } |
98 | | |
99 | 17 | if ( !aNormalizedDescriptor.empty() ) |
100 | 17 | { |
101 | 17 | maEventData[nIndex] = aNormalizedDescriptor.getPropertyValues(); |
102 | 17 | } |
103 | 0 | else |
104 | 0 | { |
105 | 0 | maEventData[nIndex] = {}; |
106 | 0 | } |
107 | 17 | } |
108 | | |
109 | | |
110 | | // --- XNameAccess --- |
111 | | |
112 | | uno::Any SAL_CALL SfxEvents_Impl::getByName( const OUString& aName ) |
113 | 168 | { |
114 | 168 | std::unique_lock aGuard( maMutex ); |
115 | | |
116 | | // find the event in the list and return the data |
117 | | |
118 | 168 | auto nIndex = comphelper::findValue(maEventNames, aName); |
119 | 168 | if (nIndex != -1) |
120 | 168 | return uno::Any(maEventData[nIndex]); |
121 | | |
122 | 0 | throw container::NoSuchElementException(); |
123 | 168 | } |
124 | | |
125 | | |
126 | | uno::Sequence< OUString > SAL_CALL SfxEvents_Impl::getElementNames() |
127 | 6 | { |
128 | 6 | return maEventNames; |
129 | 6 | } |
130 | | |
131 | | |
132 | | sal_Bool SAL_CALL SfxEvents_Impl::hasByName( const OUString& aName ) |
133 | 17 | { |
134 | 17 | std::unique_lock aGuard( maMutex ); |
135 | | |
136 | | // find the event in the list and return the data |
137 | | |
138 | 17 | return comphelper::findValue(maEventNames, aName) != -1; |
139 | 17 | } |
140 | | |
141 | | |
142 | | // --- XElementAccess ( parent of XNameAccess ) --- |
143 | | |
144 | | uno::Type SAL_CALL SfxEvents_Impl::getElementType() |
145 | 0 | { |
146 | 0 | uno::Type aElementType = cppu::UnoType<uno::Sequence < beans::PropertyValue >>::get(); |
147 | 0 | return aElementType; |
148 | 0 | } |
149 | | |
150 | | |
151 | | sal_Bool SAL_CALL SfxEvents_Impl::hasElements() |
152 | 0 | { |
153 | 0 | std::unique_lock aGuard( maMutex ); |
154 | |
|
155 | 0 | return maEventNames.hasElements(); |
156 | 0 | } |
157 | | |
158 | | bool SfxEvents_Impl::isScriptURLAllowed(const OUString& aScriptURL) |
159 | 0 | { |
160 | 0 | std::optional<css::uno::Sequence<OUString>> allowedEvents( |
161 | 0 | officecfg::Office::Common::Security::Scripting::AllowedDocumentEventURLs::get()); |
162 | | // When AllowedDocumentEventURLs is empty, all event URLs are allowed |
163 | 0 | if (!allowedEvents) |
164 | 0 | return true; |
165 | | |
166 | 0 | icu::ErrorCode status; |
167 | 0 | const uint32_t rMatcherFlags = UREGEX_CASE_INSENSITIVE; |
168 | 0 | icu::UnicodeString usInput(aScriptURL.getStr()); |
169 | 0 | const css::uno::Sequence<OUString>& rAllowedEvents = *allowedEvents; |
170 | 0 | for (auto const& allowedEvent : rAllowedEvents) |
171 | 0 | { |
172 | 0 | icu::UnicodeString usRegex(allowedEvent.getStr()); |
173 | 0 | icu::RegexMatcher rmatch1(usRegex, usInput, rMatcherFlags, status); |
174 | 0 | if (aScriptURL.startsWith(allowedEvent) || rmatch1.matches(status)) |
175 | 0 | { |
176 | 0 | return true; |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | 0 | return false; |
181 | 0 | } |
182 | | |
183 | | void SfxEvents_Impl::Execute( css::uno::Sequence < css::beans::PropertyValue > const & aProperties, const document::DocumentEvent& aTrigger, SfxObjectShell* pDoc ) |
184 | 0 | { |
185 | 0 | OUString aType; |
186 | 0 | OUString aScript; |
187 | 0 | OUString aLibrary; |
188 | 0 | OUString aMacroName; |
189 | |
|
190 | 0 | if ( !aProperties.hasElements() ) |
191 | 0 | return; |
192 | | |
193 | 0 | for (const auto& rProp : aProperties) |
194 | 0 | { |
195 | 0 | if ( rProp.Name == PROP_EVENT_TYPE ) |
196 | 0 | rProp.Value >>= aType; |
197 | 0 | else if ( rProp.Name == PROP_SCRIPT ) |
198 | 0 | rProp.Value >>= aScript; |
199 | 0 | else if ( rProp.Name == PROP_LIBRARY ) |
200 | 0 | rProp.Value >>= aLibrary; |
201 | 0 | else if ( rProp.Name == PROP_MACRO_NAME ) |
202 | 0 | rProp.Value >>= aMacroName; |
203 | 0 | else { |
204 | 0 | OSL_FAIL("Unknown property value!"); |
205 | 0 | } |
206 | 0 | } |
207 | |
|
208 | 0 | if (aType.isEmpty()) |
209 | 0 | { |
210 | | // Empty type means no active binding for the event. Just ignore do nothing. |
211 | 0 | return; |
212 | 0 | } |
213 | | |
214 | 0 | if (aScript.isEmpty()) |
215 | 0 | return; |
216 | | |
217 | 0 | if (!isScriptURLAllowed(aScript)) |
218 | 0 | return; |
219 | | |
220 | 0 | if (!pDoc) |
221 | 0 | pDoc = SfxObjectShell::Current(); |
222 | |
|
223 | 0 | if (pDoc && !SfxObjectShell::isScriptAccessAllowed(pDoc->GetModel())) |
224 | 0 | return; |
225 | | |
226 | 0 | if (aType == STAR_BASIC) |
227 | 0 | { |
228 | 0 | uno::Any aAny; |
229 | 0 | SfxMacroLoader::loadMacro( aScript, aAny, pDoc ); |
230 | 0 | } |
231 | 0 | else if (aType == "Service" || aType == "Script") |
232 | 0 | { |
233 | 0 | util::URL aURL; |
234 | 0 | uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); |
235 | |
|
236 | 0 | aURL.Complete = aScript; |
237 | 0 | xTrans->parseStrict( aURL ); |
238 | |
|
239 | 0 | bool bAllowed = !SfxObjectShell::UnTrustedScript(aURL.Complete); |
240 | |
|
241 | 0 | if (bAllowed) |
242 | 0 | { |
243 | 0 | SfxViewFrame* pView = SfxViewFrame::GetFirst(pDoc); |
244 | |
|
245 | 0 | uno::Reference |
246 | 0 | < frame::XDispatchProvider > xProv; |
247 | |
|
248 | 0 | if ( pView != nullptr ) |
249 | 0 | { |
250 | 0 | xProv = uno::Reference |
251 | 0 | < frame::XDispatchProvider > ( |
252 | 0 | pView->GetFrame().GetFrameInterface(), uno::UNO_QUERY ); |
253 | 0 | } |
254 | 0 | else |
255 | 0 | { |
256 | 0 | xProv = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); |
257 | 0 | } |
258 | |
|
259 | 0 | uno::Reference < frame::XDispatch > xDisp; |
260 | 0 | if ( xProv.is() ) |
261 | 0 | xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); |
262 | |
|
263 | 0 | if ( xDisp.is() ) |
264 | 0 | { |
265 | 0 | beans::PropertyValue aEventParam; |
266 | 0 | aEventParam.Value <<= aTrigger; |
267 | 0 | uno::Sequence< beans::PropertyValue > aDispatchArgs( &aEventParam, 1 ); |
268 | 0 | xDisp->dispatch( aURL, aDispatchArgs ); |
269 | 0 | } |
270 | 0 | } |
271 | 0 | } |
272 | 0 | else |
273 | 0 | { |
274 | 0 | SAL_WARN( "sfx.notify", "notifyEvent(): Unsupported event type" ); |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | | |
279 | | // --- ::document::XEventListener --- |
280 | | |
281 | | void SAL_CALL SfxEvents_Impl::documentEventOccured( const document::DocumentEvent& aEvent ) |
282 | 0 | { |
283 | 0 | std::unique_lock aGuard( maMutex ); |
284 | | |
285 | | // get the event name, find the corresponding data, execute the data |
286 | |
|
287 | 0 | auto nIndex = comphelper::findValue(maEventNames, aEvent.EventName); |
288 | 0 | if ( nIndex == -1 ) |
289 | 0 | return; |
290 | | |
291 | 0 | css::uno::Sequence < css::beans::PropertyValue > aEventData = maEventData[ nIndex ]; |
292 | 0 | aGuard.unlock(); |
293 | 0 | Execute( aEventData, aEvent, mpObjShell ); |
294 | 0 | } |
295 | | |
296 | | |
297 | | // --- ::lang::XEventListener --- |
298 | | |
299 | | void SAL_CALL SfxEvents_Impl::disposing( const lang::EventObject& /*Source*/ ) |
300 | 101 | { |
301 | 101 | std::unique_lock aGuard( maMutex ); |
302 | | |
303 | 101 | if ( mxBroadcaster.is() ) |
304 | 101 | { |
305 | 101 | mxBroadcaster->removeDocumentEventListener( this ); |
306 | 101 | mxBroadcaster = nullptr; |
307 | 101 | } |
308 | 101 | } |
309 | | |
310 | | |
311 | | SfxEvents_Impl::SfxEvents_Impl( SfxObjectShell* pShell, |
312 | | uno::Reference< document::XDocumentEventBroadcaster > const & xBroadcaster ) |
313 | 101 | { |
314 | | // get the list of supported events and store it |
315 | 101 | if ( pShell ) |
316 | 101 | maEventNames = pShell->GetEventNames(); |
317 | 0 | else |
318 | 0 | maEventNames = rtl::Reference<GlobalEventConfig>(new GlobalEventConfig)->getElementNames(); |
319 | | |
320 | 101 | maEventData.resize( maEventNames.getLength() ); |
321 | | |
322 | 101 | mpObjShell = pShell; |
323 | 101 | mxBroadcaster = xBroadcaster; |
324 | | |
325 | 101 | if ( mxBroadcaster.is() ) |
326 | 101 | mxBroadcaster->addDocumentEventListener( this ); |
327 | 101 | } |
328 | | |
329 | | |
330 | | SfxEvents_Impl::~SfxEvents_Impl() |
331 | 101 | { |
332 | 101 | } |
333 | | |
334 | | |
335 | | std::unique_ptr<SvxMacro> SfxEvents_Impl::ConvertToMacro( const uno::Any& rElement, SfxObjectShell* pObjShell ) |
336 | 0 | { |
337 | 0 | std::unique_ptr<SvxMacro> pMacro; |
338 | 0 | uno::Sequence < beans::PropertyValue > aProperties; |
339 | 0 | uno::Any aAny; |
340 | 0 | NormalizeMacro( rElement, aAny, pObjShell ); |
341 | |
|
342 | 0 | if ( aAny >>= aProperties ) |
343 | 0 | { |
344 | 0 | OUString aType; |
345 | 0 | OUString aScriptURL; |
346 | 0 | OUString aLibrary; |
347 | 0 | OUString aMacroName; |
348 | |
|
349 | 0 | if ( !aProperties.hasElements() ) |
350 | 0 | return pMacro; |
351 | | |
352 | 0 | for (const auto& rProp : aProperties) |
353 | 0 | { |
354 | 0 | if ( rProp.Name == PROP_EVENT_TYPE ) |
355 | 0 | rProp.Value >>= aType; |
356 | 0 | else if ( rProp.Name == PROP_SCRIPT ) |
357 | 0 | rProp.Value >>= aScriptURL; |
358 | 0 | else if ( rProp.Name == PROP_LIBRARY ) |
359 | 0 | rProp.Value >>= aLibrary; |
360 | 0 | else if ( rProp.Name == PROP_MACRO_NAME ) |
361 | 0 | rProp.Value >>= aMacroName; |
362 | 0 | else { |
363 | 0 | OSL_FAIL("Unknown property value!"); |
364 | 0 | } |
365 | 0 | } |
366 | | |
367 | | // Get the type |
368 | 0 | ScriptType eType( STARBASIC ); |
369 | 0 | if ( aType == STAR_BASIC ) |
370 | 0 | eType = STARBASIC; |
371 | 0 | else if (aType == "Script" && !aScriptURL.isEmpty()) |
372 | 0 | eType = EXTENDED_STYPE; |
373 | 0 | else if ( aType == SVX_MACRO_LANGUAGE_JAVASCRIPT ) |
374 | 0 | eType = JAVASCRIPT; |
375 | 0 | else { |
376 | 0 | SAL_WARN( "sfx.notify", "ConvertToMacro: Unknown macro type" ); |
377 | 0 | } |
378 | | |
379 | 0 | if ( !aMacroName.isEmpty() ) |
380 | 0 | { |
381 | 0 | if ( aLibrary == "application" ) |
382 | 0 | aLibrary = SfxGetpApp()->GetName(); |
383 | 0 | else |
384 | 0 | aLibrary.clear(); |
385 | 0 | pMacro.reset(new SvxMacro( aMacroName, aLibrary, eType )); |
386 | 0 | } |
387 | 0 | else if ( eType == EXTENDED_STYPE ) |
388 | 0 | pMacro.reset(new SvxMacro( aScriptURL, aType )); |
389 | 0 | } |
390 | | |
391 | 0 | return pMacro; |
392 | 0 | } |
393 | | |
394 | | void SfxEvents_Impl::NormalizeMacro( const uno::Any& rEvent, uno::Any& rRet, SfxObjectShell* pDoc ) |
395 | 0 | { |
396 | 0 | const ::comphelper::NamedValueCollection aEventDescriptor( rEvent ); |
397 | 0 | ::comphelper::NamedValueCollection aEventDescriptorOut; |
398 | |
|
399 | 0 | NormalizeMacro( aEventDescriptor, aEventDescriptorOut, pDoc ); |
400 | |
|
401 | 0 | rRet <<= aEventDescriptorOut.getPropertyValues(); |
402 | 0 | } |
403 | | |
404 | | void SfxEvents_Impl::NormalizeMacro( const ::comphelper::NamedValueCollection& i_eventDescriptor, |
405 | | ::comphelper::NamedValueCollection& o_normalizedDescriptor, SfxObjectShell* i_document ) |
406 | 17 | { |
407 | 17 | SfxObjectShell* pDoc = i_document; |
408 | 17 | if ( !pDoc ) |
409 | 0 | pDoc = SfxObjectShell::Current(); |
410 | | |
411 | 17 | OUString aType = i_eventDescriptor.getOrDefault( PROP_EVENT_TYPE, OUString() ); |
412 | 17 | OUString aScript = i_eventDescriptor.getOrDefault( PROP_SCRIPT, OUString() ); |
413 | 17 | OUString aLibrary = i_eventDescriptor.getOrDefault( PROP_LIBRARY, OUString() ); |
414 | 17 | OUString aMacroName = i_eventDescriptor.getOrDefault( PROP_MACRO_NAME, OUString() ); |
415 | | |
416 | 17 | if ( !aType.isEmpty() ) |
417 | 17 | o_normalizedDescriptor.put( PROP_EVENT_TYPE, aType ); |
418 | 17 | if ( !aScript.isEmpty() ) |
419 | 17 | o_normalizedDescriptor.put( PROP_SCRIPT, aScript ); |
420 | | |
421 | 17 | if ( aType != STAR_BASIC ) |
422 | 17 | return; |
423 | | |
424 | 0 | if ( !aScript.isEmpty() ) |
425 | 0 | { |
426 | 0 | if ( aMacroName.isEmpty() || aLibrary.isEmpty() ) |
427 | 0 | { |
428 | 0 | sal_Int32 nThirdSlashPos = aScript.indexOf( '/', 8 ); |
429 | 0 | sal_Int32 nArgsPos = aScript.indexOf( '(' ); |
430 | 0 | if ( ( nThirdSlashPos != -1 ) && ( nArgsPos == -1 || nThirdSlashPos < nArgsPos ) ) |
431 | 0 | { |
432 | 0 | OUString aBasMgrName( INetURLObject::decode( aScript.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset ) ); |
433 | 0 | if (pDoc && aBasMgrName == ".") |
434 | 0 | aLibrary = pDoc->GetTitle(); |
435 | 0 | else |
436 | 0 | aLibrary = SfxGetpApp()->GetName(); |
437 | | |
438 | | // Get the macro name |
439 | 0 | aMacroName = aScript.copy( nThirdSlashPos+1, nArgsPos - nThirdSlashPos - 1 ); |
440 | 0 | } |
441 | 0 | else |
442 | 0 | { |
443 | 0 | SAL_WARN( "sfx.notify", "ConvertToMacro: Unknown macro url format" ); |
444 | 0 | } |
445 | 0 | } |
446 | 0 | } |
447 | 0 | else if ( !aMacroName.isEmpty() ) |
448 | 0 | { |
449 | 0 | aScript = "macro://"; |
450 | 0 | if ( aLibrary != SfxGetpApp()->GetName() && aLibrary != "StarDesktop" && aLibrary != "application" ) |
451 | 0 | aScript += "."; |
452 | 0 | aScript += "/" + aMacroName + "()"; |
453 | 0 | } |
454 | 0 | else |
455 | | // wrong properties |
456 | 0 | return; |
457 | | |
458 | 0 | if (aLibrary != "document") |
459 | 0 | { |
460 | 0 | if ( aLibrary.isEmpty() || (pDoc && ( aLibrary == pDoc->GetTitle( SFX_TITLE_APINAME ) || aLibrary == pDoc->GetTitle() )) ) |
461 | 0 | aLibrary = "document"; |
462 | 0 | else |
463 | 0 | aLibrary = "application"; |
464 | 0 | } |
465 | |
|
466 | 0 | o_normalizedDescriptor.put( PROP_SCRIPT, aScript ); |
467 | 0 | o_normalizedDescriptor.put( PROP_LIBRARY, aLibrary ); |
468 | 0 | o_normalizedDescriptor.put( PROP_MACRO_NAME, aMacroName ); |
469 | 0 | } |
470 | | |
471 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |