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