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_misc.h"
32 #include "dp_backend.h"
33 #include "dp_ucb.h"
34 #include "dp_interact.h"
35 #include "rtl/string.hxx"
36 #include "osl/file.hxx"
37 #include "ucbhelper/content.hxx"
38 #include "comphelper/servicedecl.hxx"
39 #include "svl/inettype.hxx"
40 #include "cppuhelper/implbase1.hxx"
41 #include "dp_executablebackenddb.hxx"
42 
43 using namespace ::com::sun::star;
44 using namespace ::com::sun::star::uno;
45 using namespace ::com::sun::star::ucb;
46 using namespace dp_misc;
47 using ::rtl::OUString;
48 
49 namespace dp_registry {
50 namespace backend {
51 namespace executable {
52 namespace {
53 
54 class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
55 {
56     class ExecutablePackageImpl : public ::dp_registry::backend::Package
57     {
58         BackendImpl * getMyBackend() const;
59 
60         // Package
61         virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
62             ::osl::ResettableMutexGuard & guard,
63             ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
64             Reference<XCommandEnvironment> const & xCmdEnv );
65         virtual void processPackage_(
66             ::osl::ResettableMutexGuard & guard,
67             bool registerPackage,
68             bool startup,
69             ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
70             Reference<XCommandEnvironment> const & xCmdEnv );
71 
72         bool getFileAttributes(sal_uInt64& out_Attributes);
73         bool isUrlTargetInExtension();
74 
75     public:
76         inline ExecutablePackageImpl(
77             ::rtl::Reference<PackageRegistryBackend> const & myBackend,
78             OUString const & url, OUString const & name,
79             Reference<deployment::XPackageTypeInfo> const & xPackageType,
80             bool bRemoved, OUString const & identifier)
81             : Package( myBackend, url, name, name /* display-name */,
82                        xPackageType, bRemoved, identifier)
83             {}
84     };
85     friend class ExecutablePackageImpl;
86 
87     typedef ::std::hash_map< OUString, Reference<XInterface>,
88                              ::rtl::OUStringHash > t_string2object;
89 
90     // PackageRegistryBackend
91     virtual Reference<deployment::XPackage> bindPackage_(
92         OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
93         OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv );
94 
95     void addDataToDb(OUString const & url);
96     bool hasActiveEntry(OUString const & url);
97     void revokeEntryFromDb(OUString const & url);
98 
99     Reference<deployment::XPackageTypeInfo> m_xExecutableTypeInfo;
100     std::auto_ptr<ExecutableBackendDb> m_backendDb;
101 public:
102     BackendImpl( Sequence<Any> const & args,
103                  Reference<XComponentContext> const & xComponentContext );
104 
105     // XPackageRegistry
106     virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
107     getSupportedPackageTypes() throw (RuntimeException);
108     virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType)
109         throw (deployment::DeploymentException,
110                uno::RuntimeException);
111 
112     using PackageRegistryBackend::disposing;
113 };
114 
115 
116 BackendImpl::BackendImpl(
117     Sequence<Any> const & args,
118     Reference<XComponentContext> const & xComponentContext )
119     : PackageRegistryBackend( args, xComponentContext ),
120       m_xExecutableTypeInfo(new Package::TypeInfo(
121                                 OUSTR("application/vnd.sun.star.executable"),
122                                 OUSTR(""),
123                                 OUSTR("Executable"),
124                                 RID_IMG_COMPONENT,
125                                 RID_IMG_COMPONENT_HC ) )
126 {
127     if (!transientMode())
128     {
129         OUString dbFile = makeURL(getCachePath(), OUSTR("backenddb.xml"));
130         m_backendDb.reset(
131             new ExecutableBackendDb(getComponentContext(), dbFile));
132    }
133 }
134 
135 void BackendImpl::addDataToDb(OUString const & url)
136 {
137     if (m_backendDb.get())
138         m_backendDb->addEntry(url);
139 }
140 
141 void BackendImpl::revokeEntryFromDb(OUString const & url)
142 {
143     if (m_backendDb.get())
144         m_backendDb->revokeEntry(url);
145 }
146 
147 bool BackendImpl::hasActiveEntry(OUString const & url)
148 {
149     if (m_backendDb.get())
150         return m_backendDb->hasActiveEntry(url);
151     return false;
152 }
153 
154 
155 // XPackageRegistry
156 Sequence< Reference<deployment::XPackageTypeInfo> >
157 BackendImpl::getSupportedPackageTypes() throw (RuntimeException)
158 {
159     return Sequence<Reference<deployment::XPackageTypeInfo> >(
160         & m_xExecutableTypeInfo, 1);
161 }
162 
163 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
164         throw (deployment::DeploymentException,
165                uno::RuntimeException)
166 {
167     if (m_backendDb.get())
168         m_backendDb->removeEntry(url);
169 }
170 
171 // PackageRegistryBackend
172 Reference<deployment::XPackage> BackendImpl::bindPackage_(
173     OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
174     OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
175 {
176     if (mediaType.getLength() == 0)
177     {
178         throw lang::IllegalArgumentException(
179             StrCannotDetectMediaType::get() + url,
180             static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
181     }
182 
183     String type, subType;
184     INetContentTypeParameterList params;
185     if (INetContentTypes::parse( mediaType, type, subType, &params ))
186     {
187         if (type.EqualsIgnoreCaseAscii("application"))
188         {
189             OUString name;
190             if (!bRemoved)
191             {
192                 ::ucbhelper::Content ucbContent( url, xCmdEnv );
193                 name = ucbContent.getPropertyValue(
194                     dp_misc::StrTitle::get() ).get<OUString>();
195             }
196             if (subType.EqualsIgnoreCaseAscii("vnd.sun.star.executable"))
197             {
198                 return new BackendImpl::ExecutablePackageImpl(
199                     this, url, name,  m_xExecutableTypeInfo, bRemoved,
200                     identifier);
201             }
202         }
203     }
204     return Reference<deployment::XPackage>();
205 }
206 
207 //##############################################################################
208 
209 
210 // Package
211 BackendImpl * BackendImpl::ExecutablePackageImpl::getMyBackend() const
212 {
213     BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
214     if (NULL == pBackend)
215     {
216         //May throw a DisposedException
217         check();
218         //We should never get here...
219         throw RuntimeException(
220             OUSTR("Failed to get the BackendImpl"),
221             static_cast<OWeakObject*>(const_cast<ExecutablePackageImpl *>(this)));
222     }
223     return pBackend;
224 }
225 
226 beans::Optional< beans::Ambiguous<sal_Bool> >
227 BackendImpl::ExecutablePackageImpl::isRegistered_(
228     ::osl::ResettableMutexGuard &,
229     ::rtl::Reference<dp_misc::AbortChannel> const &,
230     Reference<XCommandEnvironment> const & )
231 {
232     bool registered = getMyBackend()->hasActiveEntry(getURL());
233     return beans::Optional< beans::Ambiguous<sal_Bool> >(
234             sal_True /* IsPresent */,
235                 beans::Ambiguous<sal_Bool>(
236                     registered, sal_False /* IsAmbiguous */ ) );
237 }
238 
239 void BackendImpl::ExecutablePackageImpl::processPackage_(
240     ::osl::ResettableMutexGuard &,
241     bool doRegisterPackage,
242     bool /*startup*/,
243     ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
244     Reference<XCommandEnvironment> const & /*xCmdEnv*/ )
245 {
246     checkAborted(abortChannel);
247     if (doRegisterPackage)
248     {
249         if (!isUrlTargetInExtension())
250         {
251             OSL_ASSERT(0);
252             return;
253         }
254         sal_uInt64 attributes = 0;
255         //Setting the executable attribut does not affect executables on Windows
256         if (getFileAttributes(attributes))
257         {
258             if(getMyBackend()->m_context.equals(OUSTR("user")))
259                 attributes |= osl_File_Attribute_OwnExe;
260             else if (getMyBackend()->m_context.equals(OUSTR("shared")))
261                 attributes |= (osl_File_Attribute_OwnExe | osl_File_Attribute_GrpExe
262                                | osl_File_Attribute_OthExe);
263             else if (!getMyBackend()->m_context.equals(OUSTR("bundled"))
264                 && !getMyBackend()->m_context.equals(OUSTR("bundled_prereg")))
265                 //Bundled extension are required to be in the properly
266                 //installed. That is an executable must have the right flags
267                 OSL_ASSERT(0);
268 
269             //This won't have affect on Windows
270             osl::File::setAttributes(
271                     dp_misc::expandUnoRcUrl(m_url), attributes);
272         }
273         getMyBackend()->addDataToDb(getURL());
274     }
275     else
276     {
277         getMyBackend()->revokeEntryFromDb(getURL());
278     }
279 }
280 
281 //We currently cannot check if this XPackage represents a content of a particular extension
282 //But we can check if we are within $UNO_USER_PACKAGES_CACHE etc.
283 //Done for security reasons. For example an extension manifest could contain a path to
284 //an executable outside the extension.
285 bool BackendImpl::ExecutablePackageImpl::isUrlTargetInExtension()
286 {
287     bool bSuccess = false;
288     OUString sExtensionDir;
289     if(getMyBackend()->m_context.equals(OUSTR("user")))
290         sExtensionDir = dp_misc::expandUnoRcTerm(OUSTR("$UNO_USER_PACKAGES_CACHE"));
291     else if (getMyBackend()->m_context.equals(OUSTR("shared")))
292         sExtensionDir = dp_misc::expandUnoRcTerm(OUSTR("$UNO_SHARED_PACKAGES_CACHE"));
293     else if (getMyBackend()->m_context.equals(OUSTR("bundled"))
294         || getMyBackend()->m_context.equals(OUSTR("bundled_prereg")))
295         sExtensionDir = dp_misc::expandUnoRcTerm(OUSTR("$BUNDLED_EXTENSIONS"));
296     else
297         OSL_ASSERT(0);
298     //remove file ellipses
299     if (osl::File::E_None == osl::File::getAbsoluteFileURL(OUString(), sExtensionDir, sExtensionDir))
300     {
301         OUString sFile;
302         if (osl::File::E_None == osl::File::getAbsoluteFileURL(
303             OUString(), dp_misc::expandUnoRcUrl(m_url), sFile))
304         {
305             if (sal_True == sFile.match(sExtensionDir, 0))
306                 bSuccess = true;
307         }
308     }
309     return bSuccess;
310 }
311 
312 bool BackendImpl::ExecutablePackageImpl::getFileAttributes(sal_uInt64& out_Attributes)
313 {
314     bool bSuccess = false;
315     const OUString url(dp_misc::expandUnoRcUrl(m_url));
316     osl::DirectoryItem item;
317     if (osl::FileBase::E_None == osl::DirectoryItem::get(url, item))
318     {
319         osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
320         if( osl::FileBase::E_None == item.getFileStatus(aStatus))
321         {
322             out_Attributes = aStatus.getAttributes();
323             bSuccess = true;
324         }
325     }
326     return bSuccess;
327 }
328 
329 //##############################################################################
330 
331 
332 } // anon namespace
333 
334 namespace sdecl = comphelper::service_decl;
335 sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
336 extern sdecl::ServiceDecl const serviceDecl(
337     serviceBI,
338     "com.sun.star.comp.deployment.executable.PackageRegistryBackend",
339     BACKEND_SERVICE_NAME );
340 
341 } // namespace component
342 } // namespace backend
343 } // namespace dp_registry
344 
345 
346