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