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_registry.hrc"
28 #include "dp_misc.h"
29 #include "dp_resource.h"
30 #include "dp_interact.h"
31 #include "dp_ucb.h"
32 #include "osl/diagnose.h"
33 #include "rtl/ustrbuf.hxx"
34 #include "rtl/uri.hxx"
35 #include "cppuhelper/compbase2.hxx"
36 #include "cppuhelper/exc_hlp.hxx"
37 #include "comphelper/sequence.hxx"
38 #include "ucbhelper/content.hxx"
39 #include "com/sun/star/uno/DeploymentException.hpp"
40 #include "com/sun/star/lang/DisposedException.hpp"
41 #include "com/sun/star/lang/WrappedTargetRuntimeException.hpp"
42 #include "com/sun/star/lang/XServiceInfo.hpp"
43 #include "com/sun/star/lang/XSingleComponentFactory.hpp"
44 #include "com/sun/star/lang/XSingleServiceFactory.hpp"
45 #include "com/sun/star/util/XUpdatable.hpp"
46 #include "com/sun/star/container/XContentEnumerationAccess.hpp"
47 #include "com/sun/star/deployment/PackageRegistryBackend.hpp"
48 #include <hash_map>
49 #include <set>
50 #include <hash_set>
51 #include <memory>
52 
53 using namespace ::dp_misc;
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::ucb;
57 using ::rtl::OUString;
58 
59 
60 namespace dp_registry {
61 
62 namespace backend {
63 namespace bundle {
64 Reference<deployment::XPackageRegistry> create(
65     Reference<deployment::XPackageRegistry> const & xRootRegistry,
66     OUString const & context, OUString const & cachePath, bool readOnly,
67     Reference<XComponentContext> const & xComponentContext );
68 }
69 }
70 
71 namespace {
72 
73 typedef ::cppu::WeakComponentImplHelper2<
74     deployment::XPackageRegistry, util::XUpdatable > t_helper;
75 
76 //==============================================================================
77 class PackageRegistryImpl : private MutexHolder, public t_helper
78 {
79     struct ci_string_hash {
operator ()dp_registry::__anon93dd4b650111::PackageRegistryImpl::ci_string_hash80         ::std::size_t operator () ( OUString const & str ) const {
81             return str.toAsciiLowerCase().hashCode();
82         }
83     };
84     struct ci_string_equals {
operator ()dp_registry::__anon93dd4b650111::PackageRegistryImpl::ci_string_equals85         bool operator () ( OUString const & str1, OUString const & str2 ) const{
86             return str1.equalsIgnoreAsciiCase( str2 );
87         }
88     };
89     typedef ::std::hash_map<
90         OUString, Reference<deployment::XPackageRegistry>,
91         ci_string_hash, ci_string_equals > t_string2registry;
92     typedef ::std::hash_map<
93         OUString, OUString,
94         ci_string_hash, ci_string_equals > t_string2string;
95     typedef ::std::set<
96         Reference<deployment::XPackageRegistry> > t_registryset;
97 
98     t_string2registry m_mediaType2backend;
99     t_string2string m_filter2mediaType;
100     t_registryset m_ambiguousBackends;
101     t_registryset m_allBackends;
102     ::std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
103 
104     void insertBackend(
105         Reference<deployment::XPackageRegistry> const & xBackend );
106 
107 protected:
108     inline void check();
109     virtual void SAL_CALL disposing();
110 
111     virtual ~PackageRegistryImpl();
PackageRegistryImpl()112     PackageRegistryImpl() : t_helper( getMutex() ) {}
113 
114 
115 public:
116     static Reference<deployment::XPackageRegistry> create(
117         OUString const & context,
118         OUString const & cachePath, bool readOnly,
119         Reference<XComponentContext> const & xComponentContext );
120 
121     // XUpdatable
122     virtual void SAL_CALL update() throw (RuntimeException);
123 
124     // XPackageRegistry
125     virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
126         OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
127         OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
128         throw (deployment::DeploymentException,
129                deployment::InvalidRemovedParameterException,
130                CommandFailedException,
131                lang::IllegalArgumentException, RuntimeException);
132     virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
133     getSupportedPackageTypes() throw (RuntimeException);
134     virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType)
135                 throw (deployment::DeploymentException,
136                 RuntimeException);
137 
138 };
139 
140 //______________________________________________________________________________
check()141 inline void PackageRegistryImpl::check()
142 {
143     ::osl::MutexGuard guard( getMutex() );
144     if (rBHelper.bInDispose || rBHelper.bDisposed) {
145         throw lang::DisposedException(
146             OUSTR("PackageRegistry instance has already been disposed!"),
147             static_cast<OWeakObject *>(this) );
148     }
149 }
150 
151 //______________________________________________________________________________
disposing()152 void PackageRegistryImpl::disposing()
153 {
154     // dispose all backends:
155     t_registryset::const_iterator iPos( m_allBackends.begin() );
156     t_registryset::const_iterator const iEnd( m_allBackends.end() );
157     for ( ; iPos != iEnd; ++iPos ) {
158         try_dispose( *iPos );
159     }
160     m_mediaType2backend = t_string2registry();
161     m_ambiguousBackends = t_registryset();
162     m_allBackends = t_registryset();
163 
164     t_helper::disposing();
165 }
166 
167 //______________________________________________________________________________
~PackageRegistryImpl()168 PackageRegistryImpl::~PackageRegistryImpl()
169 {
170 }
171 
172 //______________________________________________________________________________
normalizeMediaType(OUString const & mediaType)173 OUString normalizeMediaType( OUString const & mediaType )
174 {
175     ::rtl::OUStringBuffer buf;
176     sal_Int32 index = 0;
177     for (;;) {
178         buf.append( mediaType.getToken( 0, '/', index ).trim() );
179         if (index < 0)
180             break;
181         buf.append( static_cast< sal_Unicode >('/') );
182     }
183     return buf.makeStringAndClear();
184 }
185 
186 //______________________________________________________________________________
187 
packageRemoved(::rtl::OUString const & url,::rtl::OUString const & mediaType)188 void PackageRegistryImpl::packageRemoved(
189     ::rtl::OUString const & url, ::rtl::OUString const & mediaType)
190     throw (css::deployment::DeploymentException,
191            css::uno::RuntimeException)
192 {
193     const t_string2registry::const_iterator i =
194         m_mediaType2backend.find(mediaType);
195 
196     if (i != m_mediaType2backend.end())
197     {
198         i->second->packageRemoved(url, mediaType);
199     }
200 }
201 
insertBackend(Reference<deployment::XPackageRegistry> const & xBackend)202 void PackageRegistryImpl::insertBackend(
203     Reference<deployment::XPackageRegistry> const & xBackend )
204 {
205     m_allBackends.insert( xBackend );
206     typedef ::std::hash_set<OUString, ::rtl::OUStringHash> t_stringset;
207     t_stringset ambiguousFilters;
208 
209     const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
210         xBackend->getSupportedPackageTypes() );
211     for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos )
212     {
213         Reference<deployment::XPackageTypeInfo> const & xPackageType =
214             packageTypes[ pos ];
215         m_typesInfos.push_back( xPackageType );
216 
217         const OUString mediaType( normalizeMediaType(
218                                       xPackageType->getMediaType() ) );
219         ::std::pair<t_string2registry::iterator, bool> mb_insertion(
220             m_mediaType2backend.insert( t_string2registry::value_type(
221                                             mediaType, xBackend ) ) );
222         if (mb_insertion.second)
223         {
224             // add parameterless media-type, too:
225             sal_Int32 semi = mediaType.indexOf( ';' );
226             if (semi >= 0) {
227                 m_mediaType2backend.insert(
228                     t_string2registry::value_type(
229                         mediaType.copy( 0, semi ), xBackend ) );
230             }
231             const OUString fileFilter( xPackageType->getFileFilter() );
232             //The package backend shall also be called to determine the mediatype
233             //(XPackageRegistry.bindPackage) when the URL points to a directory.
234             const bool bExtension = mediaType.equals(OUSTR("application/vnd.sun.star.package-bundle"));
235             if (fileFilter.getLength() == 0 ||
236                 fileFilter.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*.*") ) ||
237                 fileFilter.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*") ) ||
238                 bExtension)
239             {
240                 m_ambiguousBackends.insert( xBackend );
241             }
242             else
243             {
244                 sal_Int32 nIndex = 0;
245                 do {
246                     OUString token( fileFilter.getToken( 0, ';', nIndex ) );
247                     if (token.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("*.") ))
248                         token = token.copy( 1 );
249                     if (token.getLength() == 0)
250                         continue;
251                     // mark any further wildcards ambig:
252                     bool ambig = (token.indexOf('*') >= 0 ||
253                                   token.indexOf('?') >= 0);
254                     if (! ambig) {
255                         ::std::pair<t_string2string::iterator, bool> ins(
256                             m_filter2mediaType.insert(
257                                 t_string2string::value_type(
258                                     token, mediaType ) ) );
259                         ambig = !ins.second;
260                         if (ambig) {
261                             // filter has already been in: add previously
262                             // added backend to ambig set
263                             const t_string2registry::const_iterator iFind(
264                                 m_mediaType2backend.find(
265                                     /* media-type of pr. added backend */
266                                     ins.first->second ) );
267                             OSL_ASSERT(
268                                 iFind != m_mediaType2backend.end() );
269                             if (iFind != m_mediaType2backend.end())
270                                 m_ambiguousBackends.insert( iFind->second );
271                         }
272                     }
273                     if (ambig) {
274                         m_ambiguousBackends.insert( xBackend );
275                         // mark filter to be removed later from filters map:
276                         ambiguousFilters.insert( token );
277                     }
278                 }
279                 while (nIndex >= 0);
280             }
281         }
282 #if OSL_DEBUG_LEVEL > 0
283         else {
284             ::rtl::OUStringBuffer buf;
285             buf.appendAscii(
286                 RTL_CONSTASCII_STRINGPARAM(
287                     "more than one PackageRegistryBackend for "
288                     "media-type=\"") );
289             buf.append( mediaType );
290             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" => ") );
291             buf.append( Reference<lang::XServiceInfo>(
292                             xBackend, UNO_QUERY_THROW )->
293                         getImplementationName() );
294             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") );
295             OSL_ENSURE( 0, ::rtl::OUStringToOString(
296                             buf.makeStringAndClear(),
297                             RTL_TEXTENCODING_UTF8 ) );
298         }
299 #endif
300     }
301 
302     // cut out ambiguous filters:
303     t_stringset::const_iterator iPos( ambiguousFilters.begin() );
304     const t_stringset::const_iterator iEnd( ambiguousFilters.end() );
305     for ( ; iPos != iEnd; ++iPos ) {
306         m_filter2mediaType.erase( *iPos );
307     }
308 }
309 
310 //______________________________________________________________________________
create(OUString const & context,OUString const & cachePath,bool readOnly,Reference<XComponentContext> const & xComponentContext)311 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
312     OUString const & context,
313     OUString const & cachePath, bool readOnly,
314     Reference<XComponentContext> const & xComponentContext )
315 {
316     PackageRegistryImpl * that = new PackageRegistryImpl;
317     Reference<deployment::XPackageRegistry> xRet(that);
318 
319     // auto-detect all registered package registries:
320     Reference<container::XEnumeration> xEnum(
321         Reference<container::XContentEnumerationAccess>(
322             xComponentContext->getServiceManager(),
323             UNO_QUERY_THROW )->createContentEnumeration(
324                 OUSTR("com.sun.star.deployment.PackageRegistryBackend") ) );
325     if (xEnum.is())
326     {
327         while (xEnum->hasMoreElements())
328         {
329             Any element( xEnum->nextElement() );
330             Sequence<Any> registryArgs(
331                 cachePath.getLength() == 0 ? 1 : 3 );
332             registryArgs[ 0 ] <<= context;
333             if (cachePath.getLength() > 0)
334             {
335                 Reference<lang::XServiceInfo> xServiceInfo(
336                     element, UNO_QUERY_THROW );
337                 OUString registryCachePath(
338                     makeURL( cachePath,
339                              ::rtl::Uri::encode(
340                                  xServiceInfo->getImplementationName(),
341                                  rtl_UriCharClassPchar,
342                                  rtl_UriEncodeIgnoreEscapes,
343                                  RTL_TEXTENCODING_UTF8 ) ) );
344                 registryArgs[ 1 ] <<= registryCachePath;
345                 registryArgs[ 2 ] <<= readOnly;
346                 if (! readOnly)
347                     create_folder( 0, registryCachePath,
348                                    Reference<XCommandEnvironment>() );
349             }
350 
351             Reference<deployment::XPackageRegistry> xBackend;
352             Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
353             if (xFac.is()) {
354                 xBackend.set(
355                     xFac->createInstanceWithArgumentsAndContext(
356                         registryArgs, xComponentContext ), UNO_QUERY );
357             }
358             else {
359                 Reference<lang::XSingleServiceFactory> xSingleServiceFac(
360                     element, UNO_QUERY_THROW );
361                 xBackend.set(
362                     xSingleServiceFac->createInstanceWithArguments(
363                         registryArgs ), UNO_QUERY );
364             }
365             if (! xBackend.is()) {
366                 throw DeploymentException(
367                     OUSTR("cannot instantiate PackageRegistryBackend service: ")
368                     + Reference<lang::XServiceInfo>(
369                         element, UNO_QUERY_THROW )->getImplementationName(),
370                     static_cast<OWeakObject *>(that) );
371             }
372 
373             that->insertBackend( xBackend );
374         }
375     }
376 
377     // Insert bundle back-end.
378     // Always register as last, because we want to add extensions also as folders
379     // and as a default we accept every folder, which was not recognized by the other
380     // backends.
381     Reference<deployment::XPackageRegistry> extensionBackend =
382         ::dp_registry::backend::bundle::create(
383             that, context, cachePath, readOnly, xComponentContext);
384     that->insertBackend(extensionBackend);
385 
386     Reference<lang::XServiceInfo> xServiceInfo(
387         extensionBackend, UNO_QUERY_THROW );
388 
389     OSL_ASSERT(xServiceInfo.is());
390     OUString registryCachePath(
391         makeURL( cachePath,
392                  ::rtl::Uri::encode(
393                      xServiceInfo->getImplementationName(),
394                      rtl_UriCharClassPchar,
395                      rtl_UriEncodeIgnoreEscapes,
396                      RTL_TEXTENCODING_UTF8 ) ) );
397     create_folder( 0, registryCachePath, Reference<XCommandEnvironment>());
398 
399 
400 #if OSL_DEBUG_LEVEL > 1
401     // dump tables:
402     {
403         t_registryset allBackends;
404         dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
405         for ( t_string2string::const_iterator iPos(
406                   that->m_filter2mediaType.begin() );
407               iPos != that->m_filter2mediaType.end(); ++iPos )
408         {
409             ::rtl::OUStringBuffer buf;
410             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("extension \"") );
411             buf.append( iPos->first );
412             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(
413                                  "\" maps to media-type \"") );
414             buf.append( iPos->second );
415             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(
416                                  "\" maps to backend ") );
417             const Reference<deployment::XPackageRegistry> xBackend(
418                 that->m_mediaType2backend.find( iPos->second )->second );
419             allBackends.insert( xBackend );
420             buf.append( Reference<lang::XServiceInfo>(
421                             xBackend, UNO_QUERY_THROW )
422                         ->getImplementationName() );
423             dp_misc::writeConsole( buf.makeStringAndClear() + OUSTR("\n"));
424         }
425         dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
426         for ( t_registryset::const_iterator iPos(
427                   that->m_ambiguousBackends.begin() );
428               iPos != that->m_ambiguousBackends.end(); ++iPos )
429         {
430             ::rtl::OUStringBuffer buf;
431             buf.append(
432                 Reference<lang::XServiceInfo>(
433                     *iPos, UNO_QUERY_THROW )->getImplementationName() );
434             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(": ") );
435             const Sequence< Reference<deployment::XPackageTypeInfo> > types(
436                 (*iPos)->getSupportedPackageTypes() );
437             for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
438                 Reference<deployment::XPackageTypeInfo> const & xInfo =
439                     types[ pos ];
440                 buf.append( xInfo->getMediaType() );
441                 const OUString filter( xInfo->getFileFilter() );
442                 if (filter.getLength() > 0) {
443                     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" (") );
444                     buf.append( filter );
445                     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(")") );
446                 }
447                 if (pos < (types.getLength() - 1))
448                     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(", ") );
449             }
450             dp_misc::TRACE(buf.makeStringAndClear() + OUSTR("\n\n"));
451         }
452         allBackends.insert( that->m_ambiguousBackends.begin(),
453                             that->m_ambiguousBackends.end() );
454         OSL_ASSERT( allBackends == that->m_allBackends );
455     }
456 #endif
457 
458     return xRet;
459 }
460 
461 // XUpdatable: broadcast to backends
462 //______________________________________________________________________________
update()463 void PackageRegistryImpl::update() throw (RuntimeException)
464 {
465     check();
466     t_registryset::const_iterator iPos( m_allBackends.begin() );
467     const t_registryset::const_iterator iEnd( m_allBackends.end() );
468     for ( ; iPos != iEnd; ++iPos ) {
469         const Reference<util::XUpdatable> xUpdatable( *iPos, UNO_QUERY );
470         if (xUpdatable.is())
471             xUpdatable->update();
472     }
473 }
474 
475 // XPackageRegistry
476 //______________________________________________________________________________
bindPackage(OUString const & url,OUString const & mediaType_,sal_Bool bRemoved,OUString const & identifier,Reference<XCommandEnvironment> const & xCmdEnv)477 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
478     OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
479     OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
480     throw (deployment::DeploymentException, deployment::InvalidRemovedParameterException,
481            CommandFailedException,
482            lang::IllegalArgumentException, RuntimeException)
483 {
484     check();
485     OUString mediaType(mediaType_);
486     if (mediaType.getLength() == 0)
487     {
488         ::ucbhelper::Content ucbContent;
489         if (create_ucb_content(
490                 &ucbContent, url, xCmdEnv, false /* no throw */ )
491                 && !ucbContent.isFolder())
492         {
493             OUString title( ucbContent.getPropertyValue(
494                                 StrTitle::get() ).get<OUString>() );
495             for (;;)
496             {
497                 const t_string2string::const_iterator iFind(
498                     m_filter2mediaType.find(title) );
499                 if (iFind != m_filter2mediaType.end()) {
500                     mediaType = iFind->second;
501                     break;
502                 }
503                 sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
504                 if (point < 0)
505                     break;
506                 title = title.copy(point);
507             }
508         }
509     }
510     if (mediaType.getLength() == 0)
511     {
512         // try ambiguous backends:
513         t_registryset::const_iterator iPos( m_ambiguousBackends.begin() );
514         const t_registryset::const_iterator iEnd( m_ambiguousBackends.end() );
515         for ( ; iPos != iEnd; ++iPos )
516         {
517             try {
518                 return (*iPos)->bindPackage( url, mediaType, bRemoved,
519                     identifier, xCmdEnv );
520             }
521             catch (lang::IllegalArgumentException &) {
522             }
523         }
524         throw lang::IllegalArgumentException(
525             getResourceString(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
526             static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
527     }
528     else
529     {
530         // get backend by media-type:
531         t_string2registry::const_iterator iFind(
532             m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
533         if (iFind == m_mediaType2backend.end()) {
534             // xxx todo: more sophisticated media-type argument parsing...
535             sal_Int32 q = mediaType.indexOf( ';' );
536             if (q >= 0) {
537                 iFind = m_mediaType2backend.find(
538                     normalizeMediaType(
539                         // cut parameters:
540                         mediaType.copy( 0, q ) ) );
541             }
542         }
543         if (iFind == m_mediaType2backend.end()) {
544             throw lang::IllegalArgumentException(
545                 getResourceString(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
546                 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
547         }
548         return iFind->second->bindPackage( url, mediaType, bRemoved,
549             identifier, xCmdEnv );
550     }
551 }
552 
553 //______________________________________________________________________________
554 Sequence< Reference<deployment::XPackageTypeInfo> >
getSupportedPackageTypes()555 PackageRegistryImpl::getSupportedPackageTypes() throw (RuntimeException)
556 {
557     return comphelper::containerToSequence(m_typesInfos);
558 }
559 } // anon namespace
560 
561 //==============================================================================
create(OUString const & context,OUString const & cachePath,bool readOnly,Reference<XComponentContext> const & xComponentContext)562 Reference<deployment::XPackageRegistry> SAL_CALL create(
563     OUString const & context,
564     OUString const & cachePath, bool readOnly,
565     Reference<XComponentContext> const & xComponentContext )
566 {
567     return PackageRegistryImpl::create(
568         context, cachePath, readOnly, xComponentContext );
569 }
570 
571 } // namespace dp_registry
572 
573