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