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