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