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