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