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