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