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
21 #include <strings.hrc>
22 #include <dp_services.hxx>
23 #include "dp_lib_container.h"
24 #include <dp_backend.h>
25 #include <dp_ucb.h>
26 #include <rtl/uri.hxx>
27 #include <ucbhelper/content.hxx>
28 #include <cppuhelper/exc_hlp.hxx>
29 #include <cppuhelper/implbase.hxx>
30 #include <comphelper/servicedecl.hxx>
31 #include <svl/inettype.hxx>
32 #include <com/sun/star/util/XUpdatable.hpp>
33 #include <com/sun/star/script/XLibraryContainer3.hpp>
34 #include <com/sun/star/util/XMacroExpander.hpp>
35 #include <memory>
36 #include "dp_scriptbackenddb.hxx"
37
38 using namespace ::dp_misc;
39 using namespace ::com::sun::star;
40 using namespace ::com::sun::star::uno;
41 using namespace ::com::sun::star::ucb;
42
43 namespace dp_registry {
44 namespace backend {
45 namespace script {
46 namespace {
47
48 typedef ::cppu::ImplInheritanceHelper<
49 ::dp_registry::backend::PackageRegistryBackend, util::XUpdatable > t_helper;
50
51 class BackendImpl : public t_helper
52 {
53 class PackageImpl : public ::dp_registry::backend::Package
54 {
55 BackendImpl * getMyBackend() const;
56
57 const OUString m_scriptURL;
58 const OUString m_dialogURL;
59 OUString m_dialogName;
60
61 // Package
62 virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
63 ::osl::ResettableMutexGuard & guard,
64 ::rtl::Reference<AbortChannel> const & abortChannel,
65 Reference<XCommandEnvironment> const & xCmdEnv ) override;
66 virtual void processPackage_(
67 ::osl::ResettableMutexGuard & guard,
68 bool registerPackage,
69 bool startup,
70 ::rtl::Reference<AbortChannel> const & abortChannel,
71 Reference<XCommandEnvironment> const & xCmdEnv ) override;
72
73 public:
74 PackageImpl(
75 ::rtl::Reference<BackendImpl> const & myBackend,
76 OUString const & url,
77 Reference<XCommandEnvironment> const &xCmdEnv,
78 OUString const & scriptURL, OUString const & dialogURL,
79 bool bRemoved, OUString const & identifier);
80 };
81 friend class PackageImpl;
82
83 // PackageRegistryBackend
84 virtual Reference<deployment::XPackage> bindPackage_(
85 OUString const & url, OUString const & mediaType,
86 bool bRemoved, OUString const & identifier,
87 Reference<XCommandEnvironment> const & xCmdEnv ) override;
88
89 void addDataToDb(OUString const & url);
90 bool hasActiveEntry(OUString const & url);
91 void revokeEntryFromDb(OUString const & url);
92
93 const Reference<deployment::XPackageTypeInfo> m_xBasicLibTypeInfo;
94 const Reference<deployment::XPackageTypeInfo> m_xDialogLibTypeInfo;
95 Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
96 std::unique_ptr<ScriptBackendDb> m_backendDb;
97 public:
98 BackendImpl( Sequence<Any> const & args,
99 Reference<XComponentContext> const & xComponentContext );
100
101 // XUpdatable
102 virtual void SAL_CALL update() override;
103
104 // XPackageRegistry
105 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
106 getSupportedPackageTypes() override;
107 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
108
109 };
110
111
PackageImpl(::rtl::Reference<BackendImpl> const & myBackend,OUString const & url,Reference<XCommandEnvironment> const & xCmdEnv,OUString const & scriptURL,OUString const & dialogURL,bool bRemoved,OUString const & identifier)112 BackendImpl::PackageImpl::PackageImpl(
113 ::rtl::Reference<BackendImpl> const & myBackend,
114 OUString const & url,
115 Reference<XCommandEnvironment> const &xCmdEnv,
116 OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
117 OUString const & identifier)
118 : Package( myBackend.get(), url,
119 OUString(), OUString(), // will be late-initialized
120 !scriptURL.isEmpty() ? myBackend->m_xBasicLibTypeInfo
121 : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
122 m_scriptURL( scriptURL ),
123 m_dialogURL( dialogURL )
124 {
125 // name, displayName:
126 if (!dialogURL.isEmpty()) {
127 m_dialogName = LibraryContainer::get_libname(
128 dialogURL, xCmdEnv, myBackend->getComponentContext() );
129 }
130 if (!scriptURL.isEmpty()) {
131 assert(m_name.pData);
132 m_name = LibraryContainer::get_libname(
133 scriptURL, xCmdEnv, myBackend->getComponentContext() );
134 }
135 else
136 m_name = m_dialogName;
137 m_displayName = m_name;
138 }
139
140
BackendImpl(Sequence<Any> const & args,Reference<XComponentContext> const & xComponentContext)141 BackendImpl::BackendImpl(
142 Sequence<Any> const & args,
143 Reference<XComponentContext> const & xComponentContext )
144 : t_helper( args, xComponentContext ),
145 m_xBasicLibTypeInfo( new Package::TypeInfo(
146 "application/vnd.sun.star.basic-library",
147 OUString() /* no file filter */,
148 DpResId(RID_STR_BASIC_LIB)
149 ) ),
150 m_xDialogLibTypeInfo( new Package::TypeInfo(
151 "application/vnd.sun.star.dialog-library",
152 OUString() /* no file filter */,
153 DpResId(RID_STR_DIALOG_LIB)
154 ) ),
155 m_typeInfos( 2 )
156 {
157 m_typeInfos[ 0 ] = m_xBasicLibTypeInfo;
158 m_typeInfos[ 1 ] = m_xDialogLibTypeInfo;
159
160 OSL_ASSERT( ! transientMode() );
161
162 if (!transientMode())
163 {
164 OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
165 m_backendDb.reset(
166 new ScriptBackendDb(getComponentContext(), dbFile));
167 }
168
169 }
addDataToDb(OUString const & url)170 void BackendImpl::addDataToDb(OUString const & url)
171 {
172 if (m_backendDb)
173 m_backendDb->addEntry(url);
174 }
175
hasActiveEntry(OUString const & url)176 bool BackendImpl::hasActiveEntry(OUString const & url)
177 {
178 if (m_backendDb)
179 return m_backendDb->hasActiveEntry(url);
180 return false;
181 }
182
183 // XUpdatable
184
update()185 void BackendImpl::update()
186 {
187 // Nothing to do here after fixing i70283!?
188 }
189
190 // XPackageRegistry
191
192 Sequence< Reference<deployment::XPackageTypeInfo> >
getSupportedPackageTypes()193 BackendImpl::getSupportedPackageTypes()
194 {
195 return m_typeInfos;
196 }
revokeEntryFromDb(OUString const & url)197 void BackendImpl::revokeEntryFromDb(OUString const & url)
198 {
199 if (m_backendDb)
200 m_backendDb->revokeEntry(url);
201 }
202
packageRemoved(OUString const & url,OUString const &)203 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
204 {
205 if (m_backendDb)
206 m_backendDb->removeEntry(url);
207 }
208
209 // PackageRegistryBackend
210
bindPackage_(OUString const & url,OUString const & mediaType_,bool bRemoved,OUString const & identifier,Reference<XCommandEnvironment> const & xCmdEnv)211 Reference<deployment::XPackage> BackendImpl::bindPackage_(
212 OUString const & url, OUString const & mediaType_,
213 bool bRemoved, OUString const & identifier,
214 Reference<XCommandEnvironment> const & xCmdEnv )
215 {
216 OUString mediaType( mediaType_ );
217 if (mediaType.isEmpty())
218 {
219 // detect media-type:
220 ::ucbhelper::Content ucbContent;
221 if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
222 ucbContent.isFolder())
223 {
224 // probe for script.xlb:
225 if (create_ucb_content(
226 nullptr, makeURL( url, "script.xlb" ),
227 xCmdEnv, false /* no throw */ ))
228 mediaType = "application/vnd.sun.star.basic-library";
229 // probe for dialog.xlb:
230 else if (create_ucb_content(
231 nullptr, makeURL( url, "dialog.xlb" ),
232 xCmdEnv, false /* no throw */ ))
233 mediaType = "application/vnd.sun.star.dialog-library";
234 }
235 if (mediaType.isEmpty())
236 throw lang::IllegalArgumentException(
237 StrCannotDetectMediaType() + url,
238 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
239 }
240
241 OUString type, subType;
242 INetContentTypeParameterList params;
243 if (INetContentTypes::parse( mediaType, type, subType, ¶ms ))
244 {
245 if (type.equalsIgnoreAsciiCase("application"))
246 {
247 OUString dialogURL( makeURL( url, "dialog.xlb" ) );
248 if (! create_ucb_content(
249 nullptr, dialogURL, xCmdEnv, false /* no throw */ )) {
250 dialogURL.clear();
251 }
252
253 if (subType.equalsIgnoreAsciiCase("vnd.sun.star.basic-library"))
254 {
255 OUString scriptURL( makeURL( url, "script.xlb"));
256 if (! create_ucb_content(
257 nullptr, scriptURL, xCmdEnv, false /* no throw */ )) {
258 scriptURL.clear();
259 }
260
261 return new PackageImpl(
262 this, url, xCmdEnv, scriptURL,
263 dialogURL, bRemoved, identifier);
264 }
265 else if (subType.equalsIgnoreAsciiCase(
266 "vnd.sun.star.dialog-library")) {
267 return new PackageImpl(
268 this, url, xCmdEnv,
269 OUString() /* no script lib */,
270 dialogURL,
271 bRemoved, identifier);
272 }
273 }
274 }
275 throw lang::IllegalArgumentException(
276 StrUnsupportedMediaType() + mediaType,
277 static_cast<OWeakObject *>(this),
278 static_cast<sal_Int16>(-1) );
279 }
280
281
282 // Package
getMyBackend() const283 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
284 {
285 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
286 if (nullptr == pBackend)
287 {
288 //May throw a DisposedException
289 check();
290 //We should never get here...
291 throw RuntimeException(
292 "Failed to get the BackendImpl",
293 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
294 }
295 return pBackend;
296 }
297
298 beans::Optional< beans::Ambiguous<sal_Bool> >
isRegistered_(::osl::ResettableMutexGuard &,::rtl::Reference<AbortChannel> const &,Reference<XCommandEnvironment> const &)299 BackendImpl::PackageImpl::isRegistered_(
300 ::osl::ResettableMutexGuard & /* guard */,
301 ::rtl::Reference<AbortChannel> const & /* abortChannel */,
302 Reference<XCommandEnvironment> const & /* xCmdEnv */ )
303 {
304 BackendImpl * that = getMyBackend();
305 Reference< deployment::XPackage > xThisPackage( this );
306
307 bool registered = that->hasActiveEntry(getURL());
308 return beans::Optional< beans::Ambiguous<sal_Bool> >(
309 true /* IsPresent */,
310 beans::Ambiguous<sal_Bool>( registered, false /* IsAmbiguous */ ) );
311 }
312
313 void
lcl_maybeRemoveScript(bool const bExists,OUString const & rName,OUString const & rScriptURL,Reference<css::script::XLibraryContainer3> const & xScriptLibs)314 lcl_maybeRemoveScript(
315 bool const bExists,
316 OUString const& rName,
317 OUString const& rScriptURL,
318 Reference<css::script::XLibraryContainer3> const& xScriptLibs)
319 {
320 if (bExists && xScriptLibs.is() && xScriptLibs->hasByName(rName))
321 {
322 const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
323 if (sScriptUrl == rScriptURL)
324 xScriptLibs->removeLibrary(rName);
325 }
326 }
327
328 bool
lcl_maybeAddScript(bool const bExists,OUString const & rName,OUString const & rScriptURL,Reference<css::script::XLibraryContainer3> const & xScriptLibs)329 lcl_maybeAddScript(
330 bool const bExists,
331 OUString const& rName,
332 OUString const& rScriptURL,
333 Reference<css::script::XLibraryContainer3> const& xScriptLibs)
334 {
335 if (bExists && xScriptLibs.is())
336 {
337 bool bCanAdd = true;
338 if (xScriptLibs->hasByName(rName))
339 {
340 const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
341 //We assume here that library names in extensions are unique, which may not be the case
342 //ToDo: If the script exist in another extension, then both extensions must have the
343 //same id
344 if (sOriginalUrl.match("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE")
345 || sOriginalUrl.match("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE")
346 || sOriginalUrl.match("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")
347 || sOriginalUrl.match("$(INST)/share/basic/Access2Base/"))
348 {
349 xScriptLibs->removeLibrary(rName);
350 bCanAdd = true;
351 }
352 else
353 {
354 bCanAdd = false;
355 }
356 }
357
358 if (bCanAdd)
359 {
360 xScriptLibs->createLibraryLink(rName, rScriptURL, false);
361 return xScriptLibs->hasByName(rName);
362 }
363 }
364
365 return false;
366 }
367
processPackage_(::osl::ResettableMutexGuard &,bool doRegisterPackage,bool startup,::rtl::Reference<AbortChannel> const &,Reference<XCommandEnvironment> const &)368 void BackendImpl::PackageImpl::processPackage_(
369 ::osl::ResettableMutexGuard & /* guard */,
370 bool doRegisterPackage,
371 bool startup,
372 ::rtl::Reference<AbortChannel> const & /* abortChannel */,
373 Reference<XCommandEnvironment> const & /* xCmdEnv */ )
374 {
375 BackendImpl * that = getMyBackend();
376
377 Reference< deployment::XPackage > xThisPackage( this );
378 Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
379
380 bool bScript = !m_scriptURL.isEmpty();
381 Reference<css::script::XLibraryContainer3> xScriptLibs;
382
383 bool bDialog = !m_dialogURL.isEmpty();
384 Reference<css::script::XLibraryContainer3> xDialogLibs;
385
386 bool bRunning = !startup && office_is_running();
387 if( bRunning )
388 {
389 if( bScript )
390 {
391 xScriptLibs.set(
392 xComponentContext->getServiceManager()->createInstanceWithContext(
393 "com.sun.star.script.ApplicationScriptLibraryContainer",
394 xComponentContext ), UNO_QUERY_THROW );
395 }
396
397 if( bDialog )
398 {
399 xDialogLibs.set(
400 xComponentContext->getServiceManager()->createInstanceWithContext(
401 "com.sun.star.script.ApplicationDialogLibraryContainer",
402 xComponentContext ), UNO_QUERY_THROW );
403 }
404 }
405 bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
406 if( !doRegisterPackage )
407 {
408 //We cannot just call removeLibrary(name) because this could remove a
409 //script which was added by an extension in a different repository. For
410 //example, extension foo is contained in the bundled repository and then
411 //the user adds it to the user repository. The extension manager will
412 //then register the new script and revoke the script from the bundled
413 //extension. removeLibrary(name) would now remove the script from the
414 //user repository. That is, the script of the newly added user extension does
415 //not work anymore. Therefore we must check if the currently active
416 //script comes in fact from the currently processed extension.
417
418 if (bRegistered)
419 {
420 //we also prevent and live deployment at startup
421 if (!isRemoved() && !startup)
422 {
423 lcl_maybeRemoveScript(bScript, m_name, m_scriptURL, xScriptLibs);
424 lcl_maybeRemoveScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
425 }
426 getMyBackend()->revokeEntryFromDb(getURL());
427 return;
428 }
429 }
430 if (bRegistered)
431 return; // Already registered
432
433 // Update LibraryContainer
434 bool bScriptSuccess = false;
435 bool bDialogSuccess = false;
436 if (!startup)
437 {
438 //If there is a bundled extension, and the user installs the same extension
439 //then the script from the bundled extension must be removed. If this does not work
440 //then live deployment does not work for scripts.
441 bScriptSuccess = lcl_maybeAddScript(bScript, m_name, m_scriptURL, xScriptLibs);
442 bDialogSuccess = lcl_maybeAddScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
443 }
444 bool bSuccess = bScript || bDialog; // Something must have happened
445 if( bRunning )
446 if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
447 bSuccess = false;
448
449 if (bSuccess)
450 getMyBackend()->addDataToDb(getURL());
451 }
452
453 } // anon namespace
454
455 namespace sdecl = comphelper::service_decl;
456 sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
457 sdecl::ServiceDecl const serviceDecl(
458 serviceBI,
459 "com.sun.star.comp.deployment.script.PackageRegistryBackend",
460 BACKEND_SERVICE_NAME );
461
462 } // namespace script
463 } // namespace backend
464 } // namespace dp_registry
465
466 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
467