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 <set>
46 : #include <unordered_map>
47 : #include <unordered_set>
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 12171 : ::std::size_t operator () ( OUString const & str ) const {
67 12171 : return str.toAsciiLowerCase().hashCode();
68 : }
69 : };
70 : struct ci_string_equals {
71 2367 : bool operator () ( OUString const & str1, OUString const & str2 ) const{
72 2367 : return str1.equalsIgnoreAsciiCase( str2 );
73 : }
74 : };
75 : typedef std::unordered_map<
76 : OUString, Reference<deployment::XPackageRegistry>,
77 : ci_string_hash, ci_string_equals > t_string2registry;
78 : typedef std::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 417 : 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 67 : inline void PackageRegistryImpl::check()
128 : {
129 67 : ::osl::MutexGuard guard( getMutex() );
130 67 : if (rBHelper.bInDispose || rBHelper.bDisposed) {
131 : throw lang::DisposedException(
132 : "PackageRegistry instance has already been disposed!",
133 0 : static_cast<OWeakObject *>(this) );
134 67 : }
135 67 : }
136 :
137 :
138 417 : void PackageRegistryImpl::disposing()
139 : {
140 : // dispose all backends:
141 417 : t_registryset::const_iterator iPos( m_allBackends.begin() );
142 417 : t_registryset::const_iterator const iEnd( m_allBackends.end() );
143 3161 : for ( ; iPos != iEnd; ++iPos ) {
144 2744 : try_dispose( *iPos );
145 : }
146 417 : m_mediaType2backend = t_string2registry();
147 417 : m_ambiguousBackends = t_registryset();
148 417 : m_allBackends = t_registryset();
149 :
150 417 : t_helper::disposing();
151 417 : }
152 :
153 :
154 834 : PackageRegistryImpl::~PackageRegistryImpl()
155 : {
156 834 : }
157 :
158 :
159 5891 : OUString normalizeMediaType( OUString const & mediaType )
160 : {
161 5891 : OUStringBuffer buf;
162 5891 : sal_Int32 index = 0;
163 : for (;;) {
164 11782 : buf.append( mediaType.getToken( 0, '/', index ).trim() );
165 11782 : if (index < 0)
166 5891 : break;
167 5891 : buf.append( '/' );
168 : }
169 5891 : return buf.makeStringAndClear();
170 : }
171 :
172 :
173 :
174 4 : 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 4 : m_mediaType2backend.find(mediaType);
181 :
182 4 : if (i != m_mediaType2backend.end())
183 : {
184 4 : i->second->packageRemoved(url, mediaType);
185 : }
186 4 : }
187 :
188 2744 : void PackageRegistryImpl::insertBackend(
189 : Reference<deployment::XPackageRegistry> const & xBackend )
190 : {
191 2744 : m_allBackends.insert( xBackend );
192 : typedef std::unordered_set<OUString, OUStringHash> t_stringset;
193 2744 : t_stringset ambiguousFilters;
194 :
195 : const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
196 5488 : xBackend->getSupportedPackageTypes() );
197 8624 : for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos )
198 : {
199 : Reference<deployment::XPackageTypeInfo> const & xPackageType =
200 5880 : packageTypes[ pos ];
201 5880 : m_typesInfos.push_back( xPackageType );
202 :
203 : const OUString mediaType( normalizeMediaType(
204 5880 : xPackageType->getMediaType() ) );
205 : ::std::pair<t_string2registry::iterator, bool> mb_insertion(
206 : m_mediaType2backend.insert( t_string2registry::value_type(
207 5880 : mediaType, xBackend ) ) );
208 5880 : if (mb_insertion.second)
209 : {
210 : // add parameterless media-type, too:
211 5880 : sal_Int32 semi = mediaType.indexOf( ';' );
212 5880 : if (semi >= 0) {
213 : m_mediaType2backend.insert(
214 : t_string2registry::value_type(
215 1960 : mediaType.copy( 0, semi ), xBackend ) );
216 : }
217 5880 : 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 5880 : const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
221 5880 : if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
222 : {
223 2352 : m_ambiguousBackends.insert( xBackend );
224 : }
225 : else
226 : {
227 3528 : sal_Int32 nIndex = 0;
228 3528 : do {
229 3528 : OUString token( fileFilter.getToken( 0, ';', nIndex ) );
230 3528 : if (token.match( "*." ))
231 3528 : token = token.copy( 1 );
232 3528 : if (token.isEmpty())
233 0 : continue;
234 : // mark any further wildcards ambig:
235 7056 : bool ambig = (token.indexOf('*') >= 0 ||
236 7056 : token.indexOf('?') >= 0);
237 3528 : if (! ambig) {
238 : ::std::pair<t_string2string::iterator, bool> ins(
239 : m_filter2mediaType.insert(
240 : t_string2string::value_type(
241 3528 : token, mediaType ) ) );
242 3528 : ambig = !ins.second;
243 3528 : 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 392 : ins.first->second ) );
250 : OSL_ASSERT(
251 : iFind != m_mediaType2backend.end() );
252 392 : if (iFind != m_mediaType2backend.end())
253 392 : m_ambiguousBackends.insert( iFind->second );
254 : }
255 : }
256 3528 : if (ambig) {
257 392 : m_ambiguousBackends.insert( xBackend );
258 : // mark filter to be removed later from filters map:
259 392 : ambiguousFilters.insert( token );
260 3528 : }
261 : }
262 3528 : while (nIndex >= 0);
263 5880 : }
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 5880 : }
282 :
283 : // cut out ambiguous filters:
284 2744 : t_stringset::const_iterator iPos( ambiguousFilters.begin() );
285 2744 : const t_stringset::const_iterator iEnd( ambiguousFilters.end() );
286 3136 : for ( ; iPos != iEnd; ++iPos ) {
287 392 : m_filter2mediaType.erase( *iPos );
288 2744 : }
289 2744 : }
290 :
291 :
292 417 : Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
293 : OUString const & context,
294 : OUString const & cachePath, bool readOnly,
295 : Reference<XComponentContext> const & xComponentContext )
296 : {
297 417 : PackageRegistryImpl * that = new PackageRegistryImpl;
298 417 : Reference<deployment::XPackageRegistry> xRet(that);
299 :
300 : // auto-detect all registered package registries:
301 : Reference<container::XEnumeration> xEnum(
302 : Reference<container::XContentEnumerationAccess>(
303 417 : xComponentContext->getServiceManager(),
304 1251 : UNO_QUERY_THROW )->createContentEnumeration(
305 1251 : "com.sun.star.deployment.PackageRegistryBackend" ) );
306 417 : if (xEnum.is())
307 : {
308 3186 : while (xEnum->hasMoreElements())
309 : {
310 2377 : Any element( xEnum->nextElement() );
311 4754 : Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
312 2377 : registryArgs[ 0 ] <<= context;
313 2377 : if (!cachePath.isEmpty())
314 : {
315 : Reference<lang::XServiceInfo> xServiceInfo(
316 2377 : element, UNO_QUERY_THROW );
317 : OUString registryCachePath(
318 : makeURL( cachePath,
319 : ::rtl::Uri::encode(
320 2377 : xServiceInfo->getImplementationName(),
321 : rtl_UriCharClassPchar,
322 : rtl_UriEncodeIgnoreEscapes,
323 4754 : RTL_TEXTENCODING_UTF8 ) ) );
324 2377 : registryArgs[ 1 ] <<= registryCachePath;
325 2377 : registryArgs[ 2 ] <<= readOnly;
326 2377 : if (! readOnly)
327 : create_folder( 0, registryCachePath,
328 4779 : Reference<XCommandEnvironment>() );
329 : }
330 :
331 4704 : Reference<deployment::XPackageRegistry> xBackend;
332 4704 : Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
333 2352 : if (xFac.is()) {
334 : xBackend.set(
335 2352 : xFac->createInstanceWithArgumentsAndContext(
336 2352 : 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 2352 : 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 2352 : that->insertBackend( xBackend );
354 2377 : }
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 784 : that, context, cachePath, readOnly, xComponentContext);
364 392 : that->insertBackend(extensionBackend);
365 :
366 : Reference<lang::XServiceInfo> xServiceInfo(
367 784 : extensionBackend, UNO_QUERY_THROW );
368 :
369 : OSL_ASSERT(xServiceInfo.is());
370 : OUString registryCachePath(
371 : makeURL( cachePath,
372 : ::rtl::Uri::encode(
373 392 : xServiceInfo->getImplementationName(),
374 : rtl_UriCharClassPchar,
375 : rtl_UriEncodeIgnoreEscapes,
376 784 : RTL_TEXTENCODING_UTF8 ) ) );
377 392 : 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 784 : return xRet;
437 : }
438 :
439 : // XUpdatable: broadcast to backends
440 :
441 53 : void PackageRegistryImpl::update() throw (RuntimeException, std::exception)
442 : {
443 53 : check();
444 53 : t_registryset::const_iterator iPos( m_allBackends.begin() );
445 53 : const t_registryset::const_iterator iEnd( m_allBackends.end() );
446 424 : for ( ; iPos != iEnd; ++iPos ) {
447 371 : const Reference<util::XUpdatable> xUpdatable( *iPos, UNO_QUERY );
448 371 : if (xUpdatable.is())
449 53 : xUpdatable->update();
450 371 : }
451 53 : }
452 :
453 : // XPackageRegistry
454 :
455 14 : 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 14 : check();
463 14 : OUString mediaType(mediaType_);
464 14 : if (mediaType.isEmpty())
465 : {
466 3 : ::ucbhelper::Content ucbContent;
467 3 : bool bOk=true;
468 :
469 : try
470 : {
471 : bOk = create_ucb_content(
472 3 : &ucbContent, url, xCmdEnv, false /* no throw */ )
473 3 : && !ucbContent.isFolder();
474 : }
475 0 : catch (const css::ucb::ContentCreationException&)
476 : {
477 0 : bOk = false;
478 : }
479 :
480 3 : if (bOk)
481 : {
482 2 : OUString title( StrTitle::getTitle( ucbContent ) );
483 : for (;;)
484 : {
485 : const t_string2string::const_iterator iFind(
486 4 : m_filter2mediaType.find(title) );
487 4 : if (iFind != m_filter2mediaType.end()) {
488 0 : mediaType = iFind->second;
489 0 : break;
490 : }
491 4 : sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
492 4 : if (point < 0)
493 2 : break;
494 2 : title = title.copy(point);
495 4 : }
496 3 : }
497 : }
498 14 : if (mediaType.isEmpty())
499 : {
500 : // try ambiguous backends:
501 3 : t_registryset::const_iterator iPos( m_ambiguousBackends.begin() );
502 3 : const t_registryset::const_iterator iEnd( m_ambiguousBackends.end() );
503 18 : for ( ; iPos != iEnd; ++iPos )
504 : {
505 : try {
506 18 : return (*iPos)->bindPackage( url, mediaType, bRemoved,
507 18 : identifier, xCmdEnv );
508 : }
509 15 : 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 11 : m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
521 11 : 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 11 : 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 11 : return iFind->second->bindPackage( url, mediaType, bRemoved,
537 11 : identifier, xCmdEnv );
538 14 : }
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 417 : 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 417 : context, cachePath, readOnly, xComponentContext );
557 : }
558 :
559 : } // namespace dp_registry
560 :
561 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|