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() throw (RuntimeException);
113 
114     // XPackageRegistry
115     virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
116     getSupportedPackageTypes() throw (RuntimeException);
117     virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType)
118         throw (deployment::DeploymentException,
119                uno::RuntimeException);
120 
121 };
122 
123 //______________________________________________________________________________
PackageImpl(::rtl::Reference<BackendImpl> const & myBackend,OUString const & url,Reference<XCommandEnvironment> const & xCmdEnv,OUString const & scriptURL,OUString const & dialogURL,bool bRemoved,OUString const & identifier)124 BackendImpl::PackageImpl::PackageImpl(
125     ::rtl::Reference<BackendImpl> const & myBackend,
126     OUString const & url,
127     Reference<XCommandEnvironment> const &xCmdEnv,
128     OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
129     OUString const & identifier)
130     : Package( myBackend.get(), url,
131                OUString(), OUString(), // will be late-initialized
132                scriptURL.getLength() > 0 ? myBackend->m_xBasicLibTypeInfo
133                : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
134       m_scriptURL( scriptURL ),
135       m_dialogURL( dialogURL )
136 {
137     // name, displayName:
138     if (dialogURL.getLength() > 0) {
139         m_dialogName = LibraryContainer::get_libname(
140             dialogURL, xCmdEnv, myBackend->getComponentContext() );
141     }
142     if (scriptURL.getLength() > 0) {
143         m_name = LibraryContainer::get_libname(
144             scriptURL, xCmdEnv, myBackend->getComponentContext() );
145     }
146     else
147         m_name = m_dialogName;
148     m_displayName = m_name;
149 }
150 
151 //______________________________________________________________________________
BackendImpl(Sequence<Any> const & args,Reference<XComponentContext> const & xComponentContext)152 BackendImpl::BackendImpl(
153     Sequence<Any> const & args,
154     Reference<XComponentContext> const & xComponentContext )
155     : t_helper( args, xComponentContext ),
156       m_xBasicLibTypeInfo( new Package::TypeInfo(
157                                OUSTR("application/"
158                                      "vnd.sun.star.basic-library"),
159                                OUString() /* no file filter */,
160                                getResourceString(RID_STR_BASIC_LIB),
161                                RID_IMG_SCRIPTLIB, RID_IMG_SCRIPTLIB_HC ) ),
162       m_xDialogLibTypeInfo( new Package::TypeInfo(
163                                 OUSTR("application/"
164                                       "vnd.sun.star.dialog-library"),
165                                 OUString() /* no file filter */,
166                                 getResourceString(RID_STR_DIALOG_LIB),
167                                 RID_IMG_DIALOGLIB, RID_IMG_DIALOGLIB_HC ) ),
168       m_typeInfos( 2 )
169 {
170     m_typeInfos[ 0 ] = m_xBasicLibTypeInfo;
171     m_typeInfos[ 1 ] = m_xDialogLibTypeInfo;
172 
173     OSL_ASSERT( ! transientMode() );
174 
175     if (!transientMode())
176     {
177         OUString dbFile = makeURL(getCachePath(), OUSTR("backenddb.xml"));
178         m_backendDb.reset(
179             new ScriptBackendDb(getComponentContext(), dbFile));
180     }
181 
182 }
addDataToDb(OUString const & url)183 void BackendImpl::addDataToDb(OUString const & url)
184 {
185     if (m_backendDb.get())
186         m_backendDb->addEntry(url);
187 }
188 
hasActiveEntry(OUString const & url)189 bool BackendImpl::hasActiveEntry(OUString const & url)
190 {
191     if (m_backendDb.get())
192         return m_backendDb->hasActiveEntry(url);
193     return false;
194 }
195 
196 // XUpdatable
197 //______________________________________________________________________________
update()198 void BackendImpl::update() throw (RuntimeException)
199 {
200 	// Nothing to do here after fixing i70283!?
201 }
202 
203 // XPackageRegistry
204 //______________________________________________________________________________
205 Sequence< Reference<deployment::XPackageTypeInfo> >
getSupportedPackageTypes()206 BackendImpl::getSupportedPackageTypes() throw (RuntimeException)
207 {
208     return m_typeInfos;
209 }
revokeEntryFromDb(OUString const & url)210 void BackendImpl::revokeEntryFromDb(OUString const & url)
211 {
212     if (m_backendDb.get())
213         m_backendDb->revokeEntry(url);
214 }
215 
packageRemoved(OUString const & url,OUString const &)216 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
217         throw (deployment::DeploymentException,
218                uno::RuntimeException)
219 {
220     if (m_backendDb.get())
221         m_backendDb->removeEntry(url);
222 }
223 
224 // PackageRegistryBackend
225 //______________________________________________________________________________
bindPackage_(OUString const & url,OUString const & mediaType_,sal_Bool bRemoved,OUString const & identifier,Reference<XCommandEnvironment> const & xCmdEnv)226 Reference<deployment::XPackage> BackendImpl::bindPackage_(
227     OUString const & url, OUString const & mediaType_,
228     sal_Bool bRemoved, OUString const & identifier,
229     Reference<XCommandEnvironment> const & xCmdEnv )
230 {
231     OUString mediaType( mediaType_ );
232     if (mediaType.getLength() == 0)
233     {
234         // detect media-type:
235         ::ucbhelper::Content ucbContent;
236         if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
237             ucbContent.isFolder())
238         {
239             // probe for script.xlb:
240             if (create_ucb_content(
241                     0, makeURL( url, OUSTR("script.xlb") ),
242                     xCmdEnv, false /* no throw */ ))
243                 mediaType = OUSTR("application/vnd.sun.star.basic-library");
244             // probe for dialog.xlb:
245             else if (create_ucb_content(
246                          0, makeURL( url, OUSTR("dialog.xlb") ),
247                          xCmdEnv, false /* no throw */ ))
248                 mediaType = OUSTR("application/vnd.sun.star.dialog-library");
249         }
250         if (mediaType.getLength() == 0)
251             throw lang::IllegalArgumentException(
252                 StrCannotDetectMediaType::get() + url,
253                 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
254     }
255 
256     String type, subType;
257     INetContentTypeParameterList params;
258     if (INetContentTypes::parse( mediaType, type, subType, &params ))
259     {
260         if (type.EqualsIgnoreCaseAscii("application"))
261         {
262             OUString dialogURL( makeURL( url, OUSTR("dialog.xlb") ) );
263             if (! create_ucb_content(
264                     0, dialogURL, xCmdEnv, false /* no throw */ )) {
265                 dialogURL = OUString();
266             }
267 
268             if (subType.EqualsIgnoreCaseAscii("vnd.sun.star.basic-library"))
269             {
270                 OUString scriptURL( makeURL( url, OUSTR("script.xlb")));
271                 if (! create_ucb_content(
272                         0, scriptURL, xCmdEnv, false /* no throw */ )) {
273                     scriptURL = OUString();
274                 }
275 
276                 return new PackageImpl(
277                     this, url, xCmdEnv, scriptURL,
278                     dialogURL, bRemoved, identifier);
279             }
280             else if (subType.EqualsIgnoreCaseAscii(
281                          "vnd.sun.star.dialog-library")) {
282                 return new PackageImpl(
283                     this, url, xCmdEnv,
284                     OUString() /* no script lib */,
285                     dialogURL,
286                     bRemoved, identifier);
287             }
288         }
289     }
290     throw lang::IllegalArgumentException(
291         StrUnsupportedMediaType::get() + mediaType,
292         static_cast<OWeakObject *>(this),
293         static_cast<sal_Int16>(-1) );
294 }
295 
296 //##############################################################################
297 
298 // Package
getMyBackend() const299 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
300 {
301     BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
302     if (NULL == pBackend)
303     {
304         //May throw a DisposedException
305         check();
306         //We should never get here...
307         throw RuntimeException(
308             OUSTR("Failed to get the BackendImpl"),
309             static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
310     }
311     return pBackend;
312 }
313 //______________________________________________________________________________
314 beans::Optional< beans::Ambiguous<sal_Bool> >
isRegistered_(::osl::ResettableMutexGuard &,::rtl::Reference<AbortChannel> const &,Reference<XCommandEnvironment> const & xCmdEnv)315 BackendImpl::PackageImpl::isRegistered_(
316     ::osl::ResettableMutexGuard &,
317     ::rtl::Reference<AbortChannel> const &,
318     Reference<XCommandEnvironment> const & xCmdEnv )
319 {
320 	(void)xCmdEnv;
321 
322     BackendImpl * that = getMyBackend();
323 	Reference< deployment::XPackage > xThisPackage( this );
324 
325     bool registered = that->hasActiveEntry(getURL());
326     return beans::Optional< beans::Ambiguous<sal_Bool> >(
327         true /* IsPresent */,
328         beans::Ambiguous<sal_Bool>( registered, false /* IsAmbiguous */ ) );
329 }
330 
331 //______________________________________________________________________________
processPackage_(::osl::ResettableMutexGuard &,bool doRegisterPackage,bool startup,::rtl::Reference<AbortChannel> const &,Reference<XCommandEnvironment> const & xCmdEnv)332 void BackendImpl::PackageImpl::processPackage_(
333     ::osl::ResettableMutexGuard &,
334     bool doRegisterPackage,
335     bool startup,
336     ::rtl::Reference<AbortChannel> const &,
337     Reference<XCommandEnvironment> const & xCmdEnv )
338 {
339 	(void)xCmdEnv;
340 
341     BackendImpl * that = getMyBackend();
342 
343 	Reference< deployment::XPackage > xThisPackage( this );
344 	Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
345 
346 	bool bScript = (m_scriptURL.getLength() > 0);
347     Reference<css::script::XLibraryContainer3> xScriptLibs;
348 
349 	bool bDialog = (m_dialogURL.getLength() > 0);
350     Reference<css::script::XLibraryContainer3> xDialogLibs;
351 
352 	bool bRunning = office_is_running();
353     if( bRunning )
354 	{
355 		if( bScript )
356 		{
357 			xScriptLibs.set(
358 				xComponentContext->getServiceManager()->createInstanceWithContext(
359 					OUSTR("com.sun.star.script.ApplicationScriptLibraryContainer"),
360 					xComponentContext ), UNO_QUERY_THROW );
361 		}
362 
363 		if( bDialog )
364 		{
365 			xDialogLibs.set(
366 				xComponentContext->getServiceManager()->createInstanceWithContext(
367 					OUSTR("com.sun.star.script.ApplicationDialogLibraryContainer"),
368 					xComponentContext ), UNO_QUERY_THROW );
369 		}
370 	}
371     bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
372 	if( !doRegisterPackage )
373 	{
374         //We cannot just call removeLibrary(name) because this could remove a
375         //script which was added by an extension in a different repository. For
376         //example, extension foo is contained in the bundled repository and then
377         //the user adds it it to the user repository. The extension manager will
378         //then register the new script and revoke the script from the bundled
379         //extension. removeLibrary(name) would now remove the script from the
380         //user repository. That is, the script of the newly added user extension does
381         //not work anymore. Therefore we must check if the currently active
382         //script comes in fact from the currently processed extension.
383 
384 		if (bRegistered)
385 		{
386             //we also prevent and live deployment at startup
387             if (!isRemoved() && !startup)
388             {
389                 if (bScript && xScriptLibs.is() && xScriptLibs->hasByName(m_name))
390                 {
391                     const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(m_name);
392                     if (sScriptUrl.equals(m_scriptURL))
393                         xScriptLibs->removeLibrary(m_name);
394                 }
395 
396                 if (bDialog && xDialogLibs.is() && xDialogLibs->hasByName(m_dialogName))
397                 {
398                     const OUString sDialogUrl = xDialogLibs->getOriginalLibraryLinkURL(m_dialogName);
399                     if (sDialogUrl.equals(m_dialogURL))
400                         xDialogLibs->removeLibrary(m_dialogName);
401                 }
402             }
403             getMyBackend()->revokeEntryFromDb(getURL());
404             return;
405         }
406     }
407 	if (bRegistered)
408 		return;		// Already registered
409 
410 	// Update LibraryContainer
411 	bool bScriptSuccess = false;
412 	const bool bReadOnly = false;
413 
414     bool bDialogSuccess = false;
415     if (!startup)
416     {
417         //If there is a bundled extension, and the user installes the same extension
418         //then the script from the bundled extension must be removed. If this does not work
419         //then live deployment does not work for scripts.
420         if (bScript && xScriptLibs.is())
421         {
422             bool bCanAdd = true;
423             if (xScriptLibs->hasByName(m_name))
424             {
425                 const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(m_name);
426                 //We assume here that library names in extensions are unique, which may not be the case
427                 //ToDo: If the script exist in another extension, then both extensions must have the
428                 //same id
429                 if (sOriginalUrl.match(OUSTR("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE"))
430                     || sOriginalUrl.match(OUSTR("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE"))
431                     || sOriginalUrl.match(OUSTR("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")))
432                 {
433                     xScriptLibs->removeLibrary(m_name);
434                     bCanAdd = true;
435                 }
436                 else
437                 {
438                     bCanAdd = false;
439                 }
440             }
441 
442             if (bCanAdd)
443             {
444                 xScriptLibs->createLibraryLink( m_name, m_scriptURL, bReadOnly );
445                 bScriptSuccess = xScriptLibs->hasByName( m_name );
446             }
447         }
448 
449 
450         if (bDialog && xDialogLibs.is())
451         {
452             bool bCanAdd = true;
453             if (xDialogLibs->hasByName(m_dialogName))
454             {
455                 const OUString sOriginalUrl = xDialogLibs->getOriginalLibraryLinkURL(m_dialogName);
456                 //We assume here that library names in extensions are unique, which may not be the case
457                 //ToDo: If the script exist in another extension, then both extensions must have the
458                 //same id
459                 if (sOriginalUrl.match(OUSTR("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE"))
460                     || sOriginalUrl.match(OUSTR("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE"))
461                     || sOriginalUrl.match(OUSTR("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")))
462                 {
463                     xDialogLibs->removeLibrary(m_dialogName);
464                     bCanAdd = true;
465                 }
466                 else
467                 {
468                     bCanAdd = false;
469                 }
470             }
471 
472             if (bCanAdd)
473             {
474                 xDialogLibs->createLibraryLink( m_dialogName, m_dialogURL, bReadOnly );
475                 bDialogSuccess = xDialogLibs->hasByName(m_dialogName);
476             }
477         }
478     }
479 	bool bSuccess = bScript || bDialog;		// Something must have happened
480 	if( bRunning && !startup)
481 		if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
482 			bSuccess = false;
483 
484 	if (bSuccess)
485         getMyBackend()->addDataToDb(getURL());
486 }
487 
488 } // anon namespace
489 
490 namespace sdecl = comphelper::service_decl;
491 sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
492 extern sdecl::ServiceDecl const serviceDecl(
493     serviceBI,
494     "com.sun.star.comp.deployment.script.PackageRegistryBackend",
495     BACKEND_SERVICE_NAME );
496 
497 } // namespace script
498 } // namespace backend
499 } // namespace dp_registry
500 
501