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, &params ))
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