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_package.hrc"
28 #include "dp_backend.h"
29 #include "dp_ucb.h"
30 #include "dp_interact.h"
31 #include "dp_dependencies.hxx"
32 #include "dp_platform.hxx"
33 #include "dp_descriptioninfoset.hxx"
34 #include "dp_identifier.hxx"
35 #include "rtl/uri.hxx"
36 #include "cppuhelper/exc_hlp.hxx"
37 #include "cppuhelper/implbase1.hxx"
38 #include "ucbhelper/content.hxx"
39 #include "svl/inettype.hxx"
40 #include "comphelper/anytostring.hxx"
41 #include "comphelper/makesequence.hxx"
42 #include "comphelper/sequence.hxx"
43 #include "com/sun/star/lang/WrappedTargetException.hpp"
44 #include "com/sun/star/lang/XServiceInfo.hpp"
45 #include "com/sun/star/beans/UnknownPropertyException.hpp"
46 #include "com/sun/star/graphic/XGraphic.hpp"
47 #include "com/sun/star/graphic/XGraphicProvider.hpp"
48 #include "com/sun/star/io/XOutputStream.hpp"
49 #include "com/sun/star/io/XInputStream.hpp"
50 #include "com/sun/star/task/InteractionClassification.hpp"
51 #include "com/sun/star/task/XInteractionApprove.hpp"
52 #include "com/sun/star/ucb/XInteractionReplaceExistingData.hpp"
53 #include "com/sun/star/ucb/NameClashResolveRequest.hpp"
54 #include "com/sun/star/ucb/XContentAccess.hpp"
55 #include "com/sun/star/ucb/NameClash.hpp"
56 #include "com/sun/star/ucb/UnsupportedCommandException.hpp"
57 #include "com/sun/star/sdbc/XResultSet.hpp"
58 #include "com/sun/star/sdbc/XRow.hpp"
59 #include "com/sun/star/packages/manifest/XManifestReader.hpp"
60 #include "com/sun/star/packages/manifest/XManifestWriter.hpp"
61 #include "com/sun/star/deployment/DependencyException.hpp"
62 #include "com/sun/star/deployment/LicenseException.hpp"
63 #include "com/sun/star/deployment/PlatformException.hpp"
64 #include "com/sun/star/deployment/Prerequisites.hpp"
65 #include "com/sun/star/xml/dom/XDocumentBuilder.hpp"
66 #include "com/sun/star/xml/xpath/XXPathAPI.hpp"
67 #include "com/sun/star/deployment/XPackageManager.hpp"
68 #include "boost/optional.hpp"
69 #include <vector>
70 #include <stdio.h>
71
72 #include "dp_extbackenddb.hxx"
73 using namespace ::dp_misc;
74 using namespace ::com::sun::star;
75 using namespace ::com::sun::star::uno;
76
77 namespace css = ::com::sun::star;
78
79 using ::rtl::OUString;
80
81 namespace dp_registry {
82 namespace backend {
83 namespace bundle {
84 namespace {
85
86 typedef cppu::ImplInheritanceHelper1<PackageRegistryBackend,
87 lang::XServiceInfo> ImplBaseT;
88
89 //==============================================================================
90 class BackendImpl : public ImplBaseT
91 {
92 class PackageImpl : public ::dp_registry::backend::Package
93 {
94 BackendImpl * getMyBackend() const;
95 /** constains the old tooltip description for the Extension Manager GUI in OOo v.2.x
96 We keep it for backward compatibility.
97 */
98 OUString m_oldDescription;
99 OUString m_url_expanded;
100 const bool m_legacyBundle;
101 Sequence< Reference<deployment::XPackage> > m_bundle;
102 Sequence< Reference<deployment::XPackage> > * m_pBundle;
103
104 ExtensionBackendDb::Data m_dbData;
105
106 Reference<deployment::XPackage> bindBundleItem(
107 OUString const & url, OUString const & mediaType,
108 sal_Bool bRemoved, //that is, using data base information
109 OUString const & identifier,
110 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
111 bool notifyDetectionError = true );
112
113 typedef ::std::vector< Reference<deployment::XPackage> > t_packagevec;
114 void scanBundle(
115 t_packagevec & bundle,
116 ::rtl::Reference<AbortChannel> const & abortChannel,
117 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
118 void scanLegacyBundle(
119 t_packagevec & bundle,
120 OUString const & url,
121 ::rtl::Reference<AbortChannel> const & abortChannel,
122 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
123 bool skip_registration = false );
124 ::std::vector<Reference<deployment::XPackage> > getPackagesFromDb(
125 Reference<ucb::XCommandEnvironment> const & xCmdEnv);
126 bool checkPlatform(
127 Reference<ucb::XCommandEnvironment > const & environment);
128
129 bool checkDependencies(
130 Reference<ucb::XCommandEnvironment > const &
131 environment,
132 DescriptionInfoset const & description);
133 // throws css::uno::RuntimeException,
134 // css::deployment::DeploymentException
135
136 ::sal_Bool checkLicense(
137 Reference< ucb::XCommandEnvironment > const & xCmdEnv,
138 DescriptionInfoset const & description, bool bNoLicenseChecking);
139 // @throws DeploymentException
140 OUString getTextFromURL(
141 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
142 const OUString& licenseUrl);
143
144 DescriptionInfoset getDescriptionInfoset();
145
146 // Package
147 virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
148 ::osl::ResettableMutexGuard & guard,
149 ::rtl::Reference<AbortChannel> const & abortChannel,
150 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
151 virtual void processPackage_(
152 ::osl::ResettableMutexGuard & guard,
153 bool registerPackage,
154 bool startup,
155 ::rtl::Reference<AbortChannel> const & abortChannel,
156 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
157
158 virtual void SAL_CALL disposing();
159
160
161
162 public:
163 PackageImpl(
164 ::rtl::Reference<PackageRegistryBackend> const & myBackend,
165 OUString const & url,
166 OUString const & name,
167 Reference<deployment::XPackageTypeInfo> const & xPackageType,
168 bool legacyBundle,
169 bool bRemoved,
170 OUString const & identifier);
171
172 // XPackage
173 virtual sal_Bool SAL_CALL isBundle();
174
175 virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle(
176 Reference<task::XAbortChannel> const & xAbortChannel,
177 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
178 virtual OUString SAL_CALL getDescription();
179
180 virtual OUString SAL_CALL getLicenseText();
181
182 virtual void SAL_CALL exportTo(
183 OUString const & destFolderURL, OUString const & newTitle,
184 sal_Int32 nameClashAction,
185 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
186
187 virtual ::sal_Int32 SAL_CALL checkPrerequisites(
188 const Reference< task::XAbortChannel >& xAbortChannel,
189 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
190 ::sal_Bool noLicenseChecking);
191
192 virtual ::sal_Bool SAL_CALL checkDependencies(
193 const Reference< ucb::XCommandEnvironment >& xCmdEnv );
194
195 virtual beans::Optional<OUString> SAL_CALL getIdentifier();
196
197 virtual OUString SAL_CALL getVersion();
198
199 virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs();
200
201 virtual beans::StringPair SAL_CALL getPublisherInfo();
202
203 virtual OUString SAL_CALL getDisplayName();
204
205 virtual Reference< graphic::XGraphic > SAL_CALL
206 getIcon( ::sal_Bool bHighContrast );
207 };
208 friend class PackageImpl;
209
210 Reference<deployment::XPackageRegistry> m_xRootRegistry;
211 const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo;
212 const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo;
213 Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
214
215 std::auto_ptr<ExtensionBackendDb> m_backendDb;
216
217 void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data);
218 ExtensionBackendDb::Data readDataFromDb(OUString const & url);
219 void revokeEntryFromDb(OUString const & url);
220
221 // PackageRegistryBackend
222 virtual Reference<deployment::XPackage> bindPackage_(
223 OUString const & url, OUString const & mediaType,
224 sal_Bool bRemoved, OUString const & identifier,
225 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
226
227 virtual void SAL_CALL disposing();
228
229 public:
230 BackendImpl(
231 Sequence<Any> const & args,
232 Reference<XComponentContext> const & xComponentContext,
233 Reference<deployment::XPackageRegistry> const & xRootRegistry );
234
235 // XServiceInfo
236 virtual OUString SAL_CALL getImplementationName();
237 virtual sal_Bool SAL_CALL supportsService( OUString const& name );
238 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames();
239
240 // XPackageRegistry
241 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
242 getSupportedPackageTypes();
243 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType);
244
245 using ImplBaseT::disposing;
246 };
247
248 //Used to find a XPackage with a particular URL
249 class XPackage_eq : public std::unary_function<Reference<deployment::XPackage>, bool>
250 {
251 OUString m_URL;
252 public:
XPackage_eq(const OUString & s)253 explicit XPackage_eq(const OUString & s) : m_URL(s) {}
operator ()(const Reference<deployment::XPackage> & p) const254 bool operator() (const Reference<deployment::XPackage> & p) const
255 {
256 return m_URL.equals(p->getURL());
257 }
258 };
259
260 //______________________________________________________________________________
BackendImpl(Sequence<Any> const & args,Reference<XComponentContext> const & xComponentContext,Reference<deployment::XPackageRegistry> const & xRootRegistry)261 BackendImpl::BackendImpl(
262 Sequence<Any> const & args,
263 Reference<XComponentContext> const & xComponentContext,
264 Reference<deployment::XPackageRegistry> const & xRootRegistry )
265 : ImplBaseT( args, xComponentContext ),
266 m_xRootRegistry( xRootRegistry ),
267 m_xBundleTypeInfo( new Package::TypeInfo(
268 OUSTR("application/vnd.sun.star.package-bundle"),
269 OUSTR("*.oxt;*.uno.pkg"),
270 getResourceString(RID_STR_PACKAGE_BUNDLE),
271 RID_IMG_DEF_PACKAGE_BUNDLE,
272 RID_IMG_DEF_PACKAGE_BUNDLE_HC ) ),
273 m_xLegacyBundleTypeInfo( new Package::TypeInfo(
274 OUSTR("application/"
275 "vnd.sun.star.legacy-package-bundle"),
276 OUSTR("*.zip"),
277 m_xBundleTypeInfo->getShortDescription(),
278 RID_IMG_DEF_PACKAGE_BUNDLE,
279 RID_IMG_DEF_PACKAGE_BUNDLE_HC ) ),
280 m_typeInfos(2)
281 {
282 m_typeInfos[ 0 ] = m_xBundleTypeInfo;
283 m_typeInfos[ 1 ] = m_xLegacyBundleTypeInfo;
284
285 if (!transientMode())
286 {
287 OUString dbFile = makeURL(getCachePath(), getImplementationName());
288 dbFile = makeURL(dbFile, OUSTR("backenddb.xml"));
289 m_backendDb.reset(
290 new ExtensionBackendDb(getComponentContext(), dbFile));
291 }
292 }
293
294 //______________________________________________________________________________
disposing()295 void BackendImpl::disposing()
296 {
297 m_xRootRegistry.clear();
298 PackageRegistryBackend::disposing();
299 }
300
301 // XServiceInfo
getImplementationName()302 OUString BackendImpl::getImplementationName()
303 {
304 return OUSTR("com.sun.star.comp.deployment.bundle.PackageRegistryBackend");
305 }
306
supportsService(OUString const & name)307 sal_Bool BackendImpl::supportsService( OUString const& name )
308 {
309 return getSupportedServiceNames()[0].equals(name);
310 }
311
getSupportedServiceNames()312 Sequence<OUString> BackendImpl::getSupportedServiceNames()
313 {
314 return comphelper::makeSequence(
315 OUString::createFromAscii(BACKEND_SERVICE_NAME) );
316 }
317
318 // XPackageRegistry
319 //______________________________________________________________________________
320 Sequence< Reference<deployment::XPackageTypeInfo> >
getSupportedPackageTypes()321 BackendImpl::getSupportedPackageTypes()
322 {
323 return m_typeInfos;
324 }
325
packageRemoved(OUString const & url,OUString const &)326 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
327 {
328 //Notify the backend responsible for processing the different media
329 //types that this extension was removed.
330 ExtensionBackendDb::Data data = readDataFromDb(url);
331 for (ExtensionBackendDb::Data::ITC_ITEMS i = data.items.begin(); i != data.items.end(); i++)
332 {
333 m_xRootRegistry->packageRemoved(i->first, i->second);
334 }
335
336 if (m_backendDb.get())
337 m_backendDb->removeEntry(url);
338 }
339
340
341 // PackageRegistryBackend
342 //______________________________________________________________________________
bindPackage_(OUString const & url,OUString const & mediaType_,sal_Bool bRemoved,OUString const & identifier,Reference<ucb::XCommandEnvironment> const & xCmdEnv)343 Reference<deployment::XPackage> BackendImpl::bindPackage_(
344 OUString const & url, OUString const & mediaType_,
345 sal_Bool bRemoved, OUString const & identifier,
346 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
347 {
348 OUString mediaType( mediaType_ );
349 if (mediaType.getLength() == 0)
350 {
351 // detect media-type:
352 ::ucbhelper::Content ucbContent;
353 if (create_ucb_content( &ucbContent, url, xCmdEnv ))
354 {
355 if (ucbContent.isFolder())
356 {
357 //Every .oxt, uno.pkg file must contain a META-INF folder
358 ::ucbhelper::Content metaInfContent;
359 if (create_ucb_content(
360 &metaInfContent, makeURL( url, OUSTR("META-INF") ),
361 xCmdEnv, false /* no throw */ ))
362 {
363 mediaType = OUSTR("application/vnd.sun.star.package-bundle");
364 }
365 //No support of legacy bundles, because every folder could be one.
366 }
367 else
368 {
369 const OUString title( ucbContent.getPropertyValue(
370 StrTitle::get() ).get<OUString>() );
371 if (title.endsWithIgnoreAsciiCaseAsciiL(
372 RTL_CONSTASCII_STRINGPARAM(".oxt") ) ||
373 title.endsWithIgnoreAsciiCaseAsciiL(
374 RTL_CONSTASCII_STRINGPARAM(".uno.pkg") ))
375 mediaType = OUSTR("application/vnd.sun.star.package-bundle");
376 else if (title.endsWithIgnoreAsciiCaseAsciiL(
377 RTL_CONSTASCII_STRINGPARAM(".zip") ))
378 mediaType =
379 OUSTR("application/vnd.sun.star.legacy-package-bundle");
380 }
381 }
382 if (mediaType.getLength() == 0)
383 throw lang::IllegalArgumentException(
384 StrCannotDetectMediaType::get() + url,
385 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
386 }
387
388 String type, subType;
389 INetContentTypeParameterList params;
390 if (INetContentTypes::parse( mediaType, type, subType, ¶ms ))
391 {
392 if (type.EqualsIgnoreCaseAscii("application"))
393 {
394
395 //In case a XPackage is created for a removed extension, we cannot
396 //obtain the name
397 OUString name;
398 if (!bRemoved)
399 {
400 ::ucbhelper::Content ucbContent( url, xCmdEnv );
401 name = ucbContent.getPropertyValue(
402 StrTitle::get() ).get<OUString>();
403 }
404 if (subType.EqualsIgnoreCaseAscii("vnd.sun.star.package-bundle")) {
405 return new PackageImpl(
406 this, url, name, m_xBundleTypeInfo, false, bRemoved,
407 identifier);
408 }
409 else if (subType.EqualsIgnoreCaseAscii(
410 "vnd.sun.star.legacy-package-bundle")) {
411 return new PackageImpl(
412 this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
413 identifier);
414 }
415 }
416 }
417 throw lang::IllegalArgumentException(
418 StrUnsupportedMediaType::get() + mediaType,
419 static_cast<OWeakObject *>(this),
420 static_cast<sal_Int16>(-1) );
421 }
422
addDataToDb(OUString const & url,ExtensionBackendDb::Data const & data)423 void BackendImpl::addDataToDb(
424 OUString const & url, ExtensionBackendDb::Data const & data)
425 {
426 if (m_backendDb.get())
427 m_backendDb->addEntry(url, data);
428 }
429
readDataFromDb(OUString const & url)430 ExtensionBackendDb::Data BackendImpl::readDataFromDb(
431 OUString const & url)
432 {
433 ExtensionBackendDb::Data data;
434 if (m_backendDb.get())
435 data = m_backendDb->getEntry(url);
436 return data;
437 }
438
revokeEntryFromDb(OUString const & url)439 void BackendImpl::revokeEntryFromDb(OUString const & url)
440 {
441 if (m_backendDb.get())
442 m_backendDb->revokeEntry(url);
443 }
444
445
446 //##############################################################################
447
PackageImpl(::rtl::Reference<PackageRegistryBackend> const & myBackend,OUString const & url,OUString const & name,Reference<deployment::XPackageTypeInfo> const & xPackageType,bool legacyBundle,bool bRemoved,OUString const & identifier)448 BackendImpl::PackageImpl::PackageImpl(
449 ::rtl::Reference<PackageRegistryBackend> const & myBackend,
450 OUString const & url,
451 OUString const & name,
452 Reference<deployment::XPackageTypeInfo> const & xPackageType,
453 bool legacyBundle, bool bRemoved, OUString const & identifier)
454 : Package( myBackend, url, name, name /* display-name */,
455 xPackageType, bRemoved, identifier),
456 m_url_expanded( expandUnoRcUrl( url ) ),
457 m_legacyBundle( legacyBundle ),
458 m_pBundle( 0 )
459 {
460 if (bRemoved)
461 m_dbData = getMyBackend()->readDataFromDb(url);
462 }
463
getMyBackend() const464 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
465 {
466 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
467 if (NULL == pBackend)
468 {
469 //May throw a DisposedException
470 check();
471 //We should never get here...
472 throw RuntimeException(
473 OUSTR("Failed to get the BackendImpl"),
474 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
475 }
476 return pBackend;
477 }
478 //______________________________________________________________________________
disposing()479 void BackendImpl::PackageImpl::disposing()
480 {
481 sal_Int32 len = m_bundle.getLength();
482 Reference<deployment::XPackage> const * p = m_bundle.getConstArray();
483 for ( sal_Int32 pos = 0; pos < len; ++pos )
484 try_dispose( p[ pos ] );
485 m_bundle.realloc( 0 );
486
487 Package::disposing();
488 }
489
490 // Package
491 //______________________________________________________________________________
492 beans::Optional< beans::Ambiguous<sal_Bool> >
isRegistered_(::osl::ResettableMutexGuard &,::rtl::Reference<AbortChannel> const & abortChannel,Reference<ucb::XCommandEnvironment> const & xCmdEnv)493 BackendImpl::PackageImpl::isRegistered_(
494 ::osl::ResettableMutexGuard &,
495 ::rtl::Reference<AbortChannel> const & abortChannel,
496 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
497 {
498 //In case the object was created for a removed extension (m_bRemoved = true)
499 //but the extension is not registered, then bundle will be empty. Then
500 //the return value will be Optional<...>.IsPresent= false. Althoug this is
501 //not true, this does not matter. Then registerPackage or revokePackage
502 //would never be called for the items. But since the extension is removed
503 //and not registered anyway, this does not matter.
504 const Sequence< Reference<deployment::XPackage> > bundle(
505 getBundle( abortChannel.get(), xCmdEnv ) );
506
507 bool reg = false;
508 bool present = false;
509 bool ambig = false;
510 for ( sal_Int32 pos = bundle.getLength(); pos--; )
511 {
512 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
513 Reference<task::XAbortChannel> xSubAbortChannel(
514 xPackage->createAbortChannel() );
515 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
516 beans::Optional< beans::Ambiguous<sal_Bool> > option(
517 xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
518
519 //present = true if at least one bundle item has this value.
520 //reg = true if all bundle items have an option value (option.IsPresent == 1)
521 //and all have value of true (option.Value.Value == true)
522 //If not, then the bundle has the status of not registered and ambiguous.
523 if (option.IsPresent)
524 {
525 beans::Ambiguous<sal_Bool> const & status = option.Value;
526 if (present)
527 {
528 //we never come here in the first iteration
529 if (reg != (status.Value != sal_False)) {
530
531 ambig = true;
532 reg = false;
533 break;
534 }
535 }
536 else
537 {
538 //we always come here in the first iteration
539 reg = status.Value;
540 present = true;
541 }
542 }
543 }
544 return beans::Optional< beans::Ambiguous<sal_Bool> >(
545 present, beans::Ambiguous<sal_Bool>(reg, ambig) );
546 }
547
getTextFromURL(const css::uno::Reference<css::ucb::XCommandEnvironment> & xCmdEnv,const OUString & licenseUrl)548 OUString BackendImpl::PackageImpl::getTextFromURL(
549 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
550 const OUString& licenseUrl)
551 {
552 try
553 {
554 ::ucbhelper::Content descContent(licenseUrl, xCmdEnv);
555 ::rtl::ByteSequence seq = dp_misc::readFile(descContent);
556 return OUString( reinterpret_cast<sal_Char const *>(
557 seq.getConstArray()), seq.getLength(), RTL_TEXTENCODING_UTF8);
558 }
559 catch (css::uno::Exception&)
560 {
561 Any exc( ::cppu::getCaughtException() );
562 throw css::deployment::DeploymentException(
563 OUSTR("Could not read file ") + licenseUrl, 0, exc);
564 }
565
566 }
567
getDescriptionInfoset()568 DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset()
569 {
570 return dp_misc::getDescriptionInfoset(m_url_expanded);
571 }
572
checkPlatform(css::uno::Reference<css::ucb::XCommandEnvironment> const & environment)573 bool BackendImpl::PackageImpl::checkPlatform(
574 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
575 {
576 bool ret = false;
577 DescriptionInfoset info(getDescriptionInfoset());
578 Sequence<OUString> platforms(info.getSupportedPlaforms());
579 if (hasValidPlatform(platforms))
580 {
581 ret = true;
582 }
583 else
584 {
585 ret = false;
586 rtl::OUString msg(
587 RTL_CONSTASCII_USTRINGPARAM("unsupported platform"));
588 Any e(
589 css::deployment::PlatformException(
590 msg, static_cast<OWeakObject *>(this), this));
591 if (!interactContinuation(
592 e, cppu::UnoType< css::task::XInteractionApprove >::get(),
593 environment, NULL, NULL))
594 {
595 throw css::deployment::DeploymentException(
596 msg, static_cast<OWeakObject *>(this), e);
597 }
598 }
599 return ret;
600 }
601
602
checkDependencies(css::uno::Reference<css::ucb::XCommandEnvironment> const & environment,DescriptionInfoset const & description)603 bool BackendImpl::PackageImpl::checkDependencies(
604 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
605 DescriptionInfoset const & description)
606 {
607 css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
608 unsatisfied(dp_misc::Dependencies::check(description));
609
610 if (unsatisfied.getLength() == 0) {
611 return true;
612 } else {
613 rtl::OUString msg(
614 RTL_CONSTASCII_USTRINGPARAM("unsatisfied dependencies"));
615 Any e(
616 css::deployment::DependencyException(
617 msg, static_cast<OWeakObject *>(this), unsatisfied));
618 if (!interactContinuation(
619 e, cppu::UnoType< css::task::XInteractionApprove >::get(),
620 environment, NULL, NULL))
621 {
622 throw css::deployment::DeploymentException(
623 msg, static_cast<OWeakObject *>(this), e);
624 }
625 return false;
626 }
627 }
628
checkLicense(css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,DescriptionInfoset const & info,bool alreadyInstalled)629 ::sal_Bool BackendImpl::PackageImpl::checkLicense(
630 css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
631 DescriptionInfoset const & info, bool alreadyInstalled)
632 {
633 try
634 {
635 ::boost::optional<SimpleLicenseAttributes> simplLicAttr
636 = info.getSimpleLicenseAttributes();
637 if (! simplLicAttr)
638 return true;
639 OUString sLic = info.getLocalizedLicenseURL();
640 //If we do not get a localized licence then there is an error in the description.xml
641 //This should be handled by using a validating parser. Therefore we assume that no
642 //license is available.
643 if (sLic.getLength() == 0)
644 throw css::deployment::DeploymentException(
645 OUSTR("Could not obtain path to license. Possible error in description.xml"), 0, Any());
646 OUString sHref = m_url_expanded + OUSTR("/") + sLic;
647 OUString sLicense = getTextFromURL(xCmdEnv, sHref);
648 ////determine who has to agree to the license
649 //check correct value for attribute
650 if ( ! (simplLicAttr->acceptBy.equals(OUSTR("user")) || simplLicAttr->acceptBy.equals(OUSTR("admin"))))
651 throw css::deployment::DeploymentException(
652 OUSTR("Could not obtain attribute simple-lincense@accept-by or it has no valid value"), 0, Any());
653
654
655 //Only use interaction if there is no version of this extension already installed
656 //and the suppress-on-update flag is not set for the new extension
657 // alreadyInstalled | bSuppressOnUpdate | show license
658 //----------------------------------------
659 // 0 | 0 | 1
660 // 0 | 1 | 1
661 // 1 | 0 | 1
662 // 1 | 1 | 0
663
664 if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
665 {
666 css::deployment::LicenseException licExc(
667 OUString(), 0, getDisplayName(), sLicense,
668 simplLicAttr->acceptBy);
669 bool approve = false;
670 bool abort = false;
671 if (! interactContinuation(
672 Any(licExc), task::XInteractionApprove::static_type(), xCmdEnv, &approve, &abort ))
673 throw css::deployment::DeploymentException(
674 OUSTR("Could not interact with user."), 0, Any());
675
676 if (approve == true)
677 return true;
678 else
679 return false;
680 //throw css::deployment::DeploymentException(
681 // OUSTR("Extension Manager: User declined the license."),
682 // static_cast<OWeakObject*>(this),
683 // Any( css::deployment::LicenseException(OUSTR("User declined the license."), 0, m_name, sLicense)));
684 }
685 return true;
686 } catch (css::ucb::CommandFailedException&) {
687 throw;
688 } catch (css::ucb::CommandAbortedException&) {
689 throw;
690 } catch (css::deployment::DeploymentException&) {
691 throw;
692 } catch (css::uno::RuntimeException&) {
693 throw;
694 } catch (css::uno::Exception&) {
695 Any anyExc = cppu::getCaughtException();
696 throw css::deployment::DeploymentException(OUSTR("Unexpected exception"), 0, anyExc);
697 }
698 }
699
checkPrerequisites(const css::uno::Reference<css::task::XAbortChannel> &,const css::uno::Reference<css::ucb::XCommandEnvironment> & xCmdEnv,sal_Bool alreadyInstalled)700 ::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
701 const css::uno::Reference< css::task::XAbortChannel >&,
702 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
703 sal_Bool alreadyInstalled)
704 {
705 if (m_bRemoved)
706 throw deployment::ExtensionRemovedException();
707 DescriptionInfoset info = getDescriptionInfoset();
708 if (!info.hasDescription())
709 return 0;
710
711 //always return LICENSE as long as the user did not accept the license
712 //so that XExtensonManager::checkPrerequisitesAndEnable will again
713 //check the license
714 if (!checkPlatform(xCmdEnv))
715 return deployment::Prerequisites::PLATFORM |
716 deployment::Prerequisites::LICENSE;
717 else if(!checkDependencies(xCmdEnv, info))
718 return deployment::Prerequisites::DEPENDENCIES |
719 deployment::Prerequisites::LICENSE;
720 else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
721 return deployment::Prerequisites::LICENSE;
722 else
723 return 0;
724 }
725
checkDependencies(const css::uno::Reference<css::ucb::XCommandEnvironment> & xCmdEnv)726 ::sal_Bool BackendImpl::PackageImpl::checkDependencies(
727 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
728 {
729 if (m_bRemoved)
730 throw deployment::ExtensionRemovedException();
731 DescriptionInfoset info = getDescriptionInfoset();
732 if (!info.hasDescription())
733 return sal_True;
734
735 return checkDependencies(xCmdEnv, info);
736 }
737
getIdentifier()738 beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
739 {
740 OUString identifier;
741 if (m_bRemoved)
742 identifier = m_identifier;
743 else
744 identifier = dp_misc::generateIdentifier(
745 getDescriptionInfoset().getIdentifier(), m_name);
746
747 return beans::Optional<OUString>(
748 true, identifier);
749 }
750
getVersion()751 OUString BackendImpl::PackageImpl::getVersion()
752 {
753 if (m_bRemoved)
754 throw deployment::ExtensionRemovedException();
755 return getDescriptionInfoset().getVersion();
756 }
757
getUpdateInformationURLs()758 Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
759 {
760 if (m_bRemoved)
761 throw deployment::ExtensionRemovedException();
762 return getDescriptionInfoset().getUpdateInformationUrls();
763 }
764
getPublisherInfo()765 beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
766 {
767 if (m_bRemoved)
768 throw deployment::ExtensionRemovedException();
769 ::std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
770 beans::StringPair aStrPair( aInfo.first, aInfo.second );
771 return aStrPair;
772 }
773
774 //______________________________________________________________________________
getIcon(sal_Bool bHighContrast)775 uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
776 {
777 if (m_bRemoved)
778 throw deployment::ExtensionRemovedException();
779
780 uno::Reference< graphic::XGraphic > xGraphic;
781
782 OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
783 if ( aIconURL.getLength() )
784 {
785 OUString aFullIconURL = m_url_expanded + OUSTR("/") + aIconURL;
786
787 uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() );
788 uno::Reference< graphic::XGraphicProvider > xGraphProvider(
789 xContext->getServiceManager()->createInstanceWithContext( OUSTR( "com.sun.star.graphic.GraphicProvider" ), xContext ),
790 uno::UNO_QUERY );
791
792 if ( xGraphProvider.is() )
793 {
794 uno::Sequence< beans::PropertyValue > aMediaProps( 1 );
795 aMediaProps[0].Name = OUSTR( "URL" );
796 aMediaProps[0].Value <<= aFullIconURL;
797
798 xGraphic = xGraphProvider->queryGraphic( aMediaProps );
799 }
800 }
801
802 return xGraphic;
803 }
804
805 //______________________________________________________________________________
processPackage_(::osl::ResettableMutexGuard &,bool doRegisterPackage,bool startup,::rtl::Reference<AbortChannel> const & abortChannel,Reference<ucb::XCommandEnvironment> const & xCmdEnv)806 void BackendImpl::PackageImpl::processPackage_(
807 ::osl::ResettableMutexGuard &,
808 bool doRegisterPackage,
809 bool startup,
810 ::rtl::Reference<AbortChannel> const & abortChannel,
811 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
812 {
813 const Sequence< Reference<deployment::XPackage> > bundle(
814 getBundle( abortChannel.get(), xCmdEnv ) );
815
816 if (doRegisterPackage)
817 {
818 ExtensionBackendDb::Data data;
819 const sal_Int32 len = bundle.getLength();
820 for ( sal_Int32 pos = 0; pos < len; ++pos )
821 {
822 checkAborted(abortChannel);
823 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
824 Reference<task::XAbortChannel> xSubAbortChannel(
825 xPackage->createAbortChannel() );
826 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
827 try {
828 xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
829 }
830 catch (Exception &)
831 {
832 //We even try a rollback if the user cancelled the action (CommandAbortedException)
833 //in order to prevent invalid database entries.
834 Any exc( ::cppu::getCaughtException() );
835 // try to handle exception, notify:
836 bool approve = false, abort = false;
837 if (! interactContinuation(
838 Any( lang::WrappedTargetException(
839 OUSTR("bundle item registration error!"),
840 static_cast<OWeakObject *>(this), exc ) ),
841 task::XInteractionApprove::static_type(), xCmdEnv,
842 &approve, &abort )) {
843 OSL_ASSERT( !approve && !abort );
844 if (m_legacyBundle) // default for legacy packages: ignore
845 continue;
846 // no selection at all, so rethrow;
847 // no C++ rethrow after getCaughtException(),
848 // see cppuhelper/exc_hlp.hxx:
849 ::cppu::throwException(exc);
850 }
851 if (approve && !abort) // ignore error, just continue
852 continue;
853
854 {
855 ProgressLevel progress(
856 xCmdEnv, OUSTR("rollback...") );
857 // try rollback
858 for ( ; pos--; )
859 {
860 try {
861 bundle[ pos ]->revokePackage(
862 xSubAbortChannel, xCmdEnv );
863 }
864 catch (Exception &)
865 {
866 OSL_ENSURE( 0, ::rtl::OUStringToOString(
867 ::comphelper::anyToString(
868 ::cppu::getCaughtException() ),
869 RTL_TEXTENCODING_UTF8 ).getStr() );
870 // ignore any errors of rollback
871 }
872 }
873 progress.update( OUSTR("rollback finished.") );
874 }
875
876 deployment::DeploymentException dpExc;
877 if (exc >>= dpExc) {
878 throw ucb::CommandFailedException(
879 dpExc.Message, dpExc.Context, dpExc.Cause );
880 }
881 else {
882 // rethrow CommandFailedException
883 ::cppu::throwException(exc);
884 }
885 }
886 data.items.push_back(
887 ::std::make_pair(xPackage->getURL(),
888 xPackage->getPackageType()->getMediaType()));
889 }
890 getMyBackend()->addDataToDb(getURL(), data);
891 }
892 else
893 {
894 // revoke in reverse order:
895 for ( sal_Int32 pos = bundle.getLength(); pos--; )
896 {
897 checkAborted(abortChannel);
898 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
899 Reference<task::XAbortChannel> xSubAbortChannel(
900 xPackage->createAbortChannel() );
901 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
902 try {
903 bundle[ pos ]->revokePackage( xSubAbortChannel, xCmdEnv );
904 }
905 catch (RuntimeException &) {
906 throw;
907 }
908 catch (ucb::CommandAbortedException &) {
909 throw;
910 }
911 catch (Exception &) {
912 // CommandFailedException, DeploymentException:
913 Any exc( ::cppu::getCaughtException() );
914 // try to handle exception, notify:
915 bool approve = false, abort = false;
916 if (! interactContinuation(
917 Any( lang::WrappedTargetException(
918 OUSTR("bundle item revocation error!"),
919 static_cast<OWeakObject *>(this), exc ) ),
920 task::XInteractionApprove::static_type(), xCmdEnv,
921 &approve, &abort )) {
922 OSL_ASSERT( !approve && !abort );
923 if (m_legacyBundle) // default for legacy packages: ignore
924 continue;
925 // no selection at all, so rethrow
926 // no C++ rethrow after getCaughtException(),
927 // see cppuhelper/exc_hlp.hxx:
928 ::cppu::throwException(exc);
929 }
930 // ignore errors when revoking, although abort may have been
931 // selected
932 }
933 }
934 getMyBackend()->revokeEntryFromDb(getURL());
935 }
936 }
937
938 //______________________________________________________________________________
getDescription()939 OUString BackendImpl::PackageImpl::getDescription()
940 {
941 if (m_bRemoved)
942 throw deployment::ExtensionRemovedException();
943
944 const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
945 OUString sDescription;
946 if (sRelativeURL.getLength())
947 {
948 OUString sURL = m_url_expanded + OUSTR("/") + sRelativeURL;
949
950 try
951 {
952 sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
953 }
954 catch ( css::deployment::DeploymentException& )
955 {
956 OSL_ENSURE( 0, ::rtl::OUStringToOString( ::comphelper::anyToString( ::cppu::getCaughtException() ), RTL_TEXTENCODING_UTF8 ).getStr() );
957 }
958 }
959
960 if (sDescription.getLength())
961 return sDescription;
962 return m_oldDescription;
963 }
964
965 //______________________________________________________________________________
getLicenseText()966 OUString BackendImpl::PackageImpl::getLicenseText()
967 {
968 if (m_bRemoved)
969 throw deployment::ExtensionRemovedException();
970
971 OUString sLicense;
972 DescriptionInfoset aInfo = getDescriptionInfoset();
973
974 ::boost::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
975 if ( aSimplLicAttr )
976 {
977 OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
978
979 if ( aLicenseURL.getLength() )
980 {
981 OUString aFullURL = m_url_expanded + OUSTR("/") + aLicenseURL;
982 sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
983 }
984 }
985
986 return sLicense;
987 }
988
989 //______________________________________________________________________________
exportTo(OUString const & destFolderURL,OUString const & newTitle,sal_Int32 nameClashAction,Reference<ucb::XCommandEnvironment> const & xCmdEnv)990 void BackendImpl::PackageImpl::exportTo(
991 OUString const & destFolderURL, OUString const & newTitle,
992 sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
993 {
994 if (m_bRemoved)
995 throw deployment::ExtensionRemovedException();
996
997 ::ucbhelper::Content sourceContent( m_url_expanded, xCmdEnv );
998 OUString title(newTitle);
999 if (title.getLength() == 0)
1000 sourceContent.getPropertyValue( StrTitle::get() ) >>= title;
1001 OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
1002 title, rtl_UriCharClassPchar,
1003 rtl_UriEncodeIgnoreEscapes,
1004 RTL_TEXTENCODING_UTF8 ) ) );
1005
1006 if (nameClashAction == ucb::NameClash::ASK)
1007 {
1008 if (create_ucb_content(
1009 0, destURL, xCmdEnv, false /* no throw */ )) {
1010 bool replace = false, abort = false;
1011 if (! interactContinuation(
1012 Any( ucb::NameClashResolveRequest(
1013 OUSTR("file already exists: ") + title,
1014 static_cast<OWeakObject *>(this),
1015 task::InteractionClassification_QUERY,
1016 destFolderURL, title, OUString() ) ),
1017 ucb::XInteractionReplaceExistingData::static_type(), xCmdEnv,
1018 &replace, &abort ) || !replace) {
1019 return;
1020 }
1021 }
1022 }
1023 else if (nameClashAction != ucb::NameClash::OVERWRITE) {
1024 throw ucb::CommandFailedException(
1025 OUSTR("unsupported nameClashAction!"),
1026 static_cast<OWeakObject *>(this), Any() );
1027 }
1028 erase_path( destURL, xCmdEnv );
1029
1030 ::rtl::OUStringBuffer buf;
1031 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.zip://") );
1032 buf.append( ::rtl::Uri::encode( destURL,
1033 rtl_UriCharClassRegName,
1034 rtl_UriEncodeIgnoreEscapes,
1035 RTL_TEXTENCODING_UTF8 ) );
1036 buf.append( static_cast<sal_Unicode>('/') );
1037 OUString destFolder( buf.makeStringAndClear() );
1038
1039 ::ucbhelper::Content destFolderContent( destFolder, xCmdEnv );
1040 {
1041 // transfer every item of folder into zip:
1042 Reference<sdbc::XResultSet> xResultSet(
1043 sourceContent.createCursor(
1044 Sequence<OUString>(),
1045 ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
1046 ProgressLevel progress( xCmdEnv, OUString() );
1047 while (xResultSet->next())
1048 {
1049 ::ucbhelper::Content subContent(
1050 Reference<ucb::XContentAccess>(
1051 xResultSet, UNO_QUERY_THROW )->queryContent(), xCmdEnv );
1052 if (! destFolderContent.transferContent(
1053 subContent, ::ucbhelper::InsertOperation_COPY,
1054 OUString(), ucb::NameClash::OVERWRITE ))
1055 throw RuntimeException( OUSTR("UCB transferContent() failed!"),
1056 static_cast<OWeakObject *>(this) );
1057 progress.update( Any() ); // animating progress bar
1058 }
1059 }
1060
1061 // assure META-INF folder:
1062 ::ucbhelper::Content metainfFolderContent;
1063 create_folder( &metainfFolderContent,
1064 makeURL( destFolderContent.getURL(), OUSTR("META-INF") ),
1065 xCmdEnv );
1066
1067 if (m_legacyBundle)
1068 {
1069 // easy to migrate legacy bundles to new format:
1070 // just export them once using a .oxt name!
1071 // set detected media-types of any bundle item:
1072
1073 // collect all manifest entries:
1074 Sequence< Reference<deployment::XPackage> > bundle;
1075 try {
1076 bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
1077 }
1078 // xxx todo: think about exception specs:
1079 catch (deployment::DeploymentException &) {
1080 OSL_ENSURE( 0, ::rtl::OUStringToOString(
1081 ::comphelper::anyToString(
1082 ::cppu::getCaughtException() ),
1083 RTL_TEXTENCODING_UTF8 ).getStr() );
1084 }
1085 catch (lang::IllegalArgumentException & exc) {
1086 (void) exc;
1087 OSL_ENSURE( 0, ::rtl::OUStringToOString(
1088 exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
1089 }
1090
1091 ::std::vector< Sequence<beans::PropertyValue> > manifest;
1092 manifest.reserve( bundle.getLength() );
1093 sal_Int32 baseURLlen = m_url_expanded.getLength();
1094 Reference<deployment::XPackage> const *pbundle = bundle.getConstArray();
1095 const OUString strMediaType = OUSTR("MediaType");
1096 const OUString strFullPath = OUSTR("FullPath");
1097 const OUString strIsFolder = OUSTR("IsFolder");
1098 for ( sal_Int32 pos = bundle.getLength(); pos--; )
1099 {
1100 Reference<deployment::XPackage> const & xPackage = pbundle[ pos ];
1101 OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
1102 OSL_ASSERT( url_.getLength() >= baseURLlen );
1103 OUString fullPath;
1104 if (url_.getLength() > baseURLlen)
1105 fullPath = url_.copy( baseURLlen + 1 );
1106 ::ucbhelper::Content ucbContent( url_, xCmdEnv );
1107 if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
1108 fullPath += OUSTR("/");
1109 Sequence<beans::PropertyValue> attribs( 2 );
1110 beans::PropertyValue * pattribs = attribs.getArray();
1111 pattribs[ 0 ].Name = strFullPath;
1112 pattribs[ 0 ].Value <<= fullPath;
1113 pattribs[ 1 ].Name = strMediaType;
1114 const Reference<deployment::XPackageTypeInfo> xPackageType(
1115 xPackage->getPackageType() );
1116 OUString mediaType;
1117 OSL_ASSERT( xPackageType.is() );
1118 if (xPackageType.is())
1119 mediaType = xPackageType->getMediaType();
1120 else
1121 mediaType = OUSTR("unknown");
1122 pattribs[ 1 ].Value <<= mediaType;
1123 manifest.push_back( attribs );
1124 }
1125
1126 // write into pipe:
1127 Reference<XComponentContext> xContext(
1128 getMyBackend()->getComponentContext() );
1129 Reference<packages::manifest::XManifestWriter> xManifestWriter(
1130 xContext->getServiceManager()->createInstanceWithContext(
1131 OUSTR("com.sun.star.packages.manifest.ManifestWriter"),
1132 xContext ), UNO_QUERY_THROW );
1133 Reference<io::XOutputStream> xPipe(
1134 xContext->getServiceManager()->createInstanceWithContext(
1135 OUSTR("com.sun.star.io.Pipe"), xContext ), UNO_QUERY_THROW );
1136 xManifestWriter->writeManifestSequence(
1137 xPipe, comphelper::containerToSequence(manifest) );
1138
1139 // write buffered pipe data to content:
1140 ::ucbhelper::Content manifestContent(
1141 makeURL( metainfFolderContent.getURL(), OUSTR("manifest.xml") ),
1142 xCmdEnv );
1143 manifestContent.writeStream(
1144 Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
1145 true /* replace existing */ );
1146 }
1147 else
1148 {
1149 // overwrite manifest.xml:
1150 ::ucbhelper::Content manifestContent;
1151 if ( ! create_ucb_content(
1152 &manifestContent,
1153 makeURL( m_url_expanded, OUSTR("META-INF/manifest.xml") ),
1154 xCmdEnv, false ) )
1155 {
1156 OSL_ENSURE( 0, "### missing META-INF/manifest.xml file!" );
1157 return;
1158 }
1159
1160 if (! metainfFolderContent.transferContent(
1161 manifestContent, ::ucbhelper::InsertOperation_COPY,
1162 OUString(), ucb::NameClash::OVERWRITE ))
1163 throw RuntimeException( OUSTR("UCB transferContent() failed!"),
1164 static_cast<OWeakObject *>(this) );
1165 }
1166
1167 // xxx todo: maybe obsolete in the future
1168 try {
1169 destFolderContent.executeCommand( OUSTR("flush"), Any() );
1170 }
1171 catch (ucb::UnsupportedCommandException &) {
1172 }
1173 }
1174
1175 //______________________________________________________________________________
isBundle()1176 sal_Bool BackendImpl::PackageImpl::isBundle()
1177 {
1178 return true;
1179 }
1180
1181 //______________________________________________________________________________
getBundle(Reference<task::XAbortChannel> const & xAbortChannel,Reference<ucb::XCommandEnvironment> const & xCmdEnv)1182 Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
1183 Reference<task::XAbortChannel> const & xAbortChannel,
1184 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1185 {
1186 Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
1187 if (pBundle == 0)
1188 {
1189 t_packagevec bundle;
1190 if (m_bRemoved)
1191 {
1192 bundle = getPackagesFromDb(xCmdEnv);
1193 }
1194 else
1195 {
1196 try {
1197 if (m_legacyBundle)
1198 {
1199 // .zip legacy packages allow script.xlb, dialog.xlb in bundle
1200 // root folder:
1201 OUString mediaType;
1202 // probe for script.xlb:
1203 if (create_ucb_content(
1204 0, makeURL( m_url_expanded, OUSTR("script.xlb") ),
1205 xCmdEnv, false /* no throw */ )) {
1206 mediaType = OUSTR("application/vnd.sun.star.basic-library");
1207 }
1208 // probe for dialog.xlb:
1209 else if (create_ucb_content(
1210 0, makeURL( m_url_expanded, OUSTR("dialog.xlb") ),
1211 xCmdEnv, false /* no throw */ ))
1212 mediaType = OUSTR("application/vnd.sun.star."
1213 "dialog-library");
1214
1215 if (mediaType.getLength() > 0) {
1216 const Reference<deployment::XPackage> xPackage(
1217 bindBundleItem( getURL(), mediaType, false, OUString(),
1218 xCmdEnv ) );
1219 if (xPackage.is())
1220 bundle.push_back( xPackage );
1221 // continue scanning:
1222 }
1223 scanLegacyBundle( bundle, getURL(),
1224 AbortChannel::get(xAbortChannel), xCmdEnv );
1225 }
1226 else
1227 {
1228 // .oxt:
1229 scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
1230 }
1231
1232 }
1233 catch (RuntimeException &) {
1234 throw;
1235 }
1236 catch (ucb::CommandFailedException &) {
1237 throw;
1238 }
1239 catch (ucb::CommandAbortedException &) {
1240 throw;
1241 }
1242 catch (deployment::DeploymentException &) {
1243 throw;
1244 }
1245 catch (Exception &) {
1246 Any exc( ::cppu::getCaughtException() );
1247 throw deployment::DeploymentException(
1248 OUSTR("error scanning bundle: ") + getURL(),
1249 static_cast<OWeakObject *>(this), exc );
1250 }
1251 }
1252
1253 // sort: schema before config data, typelibs before components:
1254 Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
1255 Reference<deployment::XPackage> * pret = ret.getArray();
1256 sal_Int32 lower_end = 0;
1257 sal_Int32 upper_end = ret.getLength();
1258 t_packagevec::const_iterator iPos( bundle.begin() );
1259 t_packagevec::const_iterator const iEnd( bundle.end() );
1260 for ( ; iPos != iEnd; ++iPos )
1261 {
1262 const Reference<deployment::XPackageTypeInfo> xPackageType(
1263 (*iPos)->getPackageType() );
1264 OSL_ASSERT( xPackageType.is() );
1265 if (xPackageType.is()) {
1266 const OUString mediaType( xPackageType->getMediaType() );
1267 String type, subType;
1268 INetContentTypeParameterList params;
1269 if (INetContentTypes::parse(
1270 mediaType, type, subType, ¶ms ) &&
1271 type.EqualsIgnoreCaseAscii("application") &&
1272 (subType.EqualsIgnoreCaseAscii(
1273 "vnd.sun.star.uno-component") ||
1274 subType.EqualsIgnoreCaseAscii(
1275 "vnd.sun.star.configuration-data")))
1276 {
1277 --upper_end;
1278 pret[ upper_end ] = *iPos;
1279 continue;
1280 }
1281 }
1282 pret[ lower_end ] = *iPos;
1283 ++lower_end;
1284 }
1285 OSL_ASSERT( lower_end == upper_end );
1286
1287 const ::osl::MutexGuard guard( getMutex() );
1288 pBundle = m_pBundle;
1289 if (pBundle == 0) {
1290 m_bundle = ret;
1291 pBundle = &m_bundle;
1292 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1293 m_pBundle = pBundle;
1294 }
1295 }
1296 else {
1297 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1298 }
1299 return *pBundle;
1300 }
1301
isBundle_(OUString const & mediaType)1302 inline bool isBundle_( OUString const & mediaType )
1303 {
1304 // xxx todo: additional parsing?
1305 return mediaType.getLength() > 0 &&
1306 (mediaType.matchIgnoreAsciiCaseAsciiL(
1307 RTL_CONSTASCII_STRINGPARAM(
1308 "application/vnd.sun.star.package-bundle") ) ||
1309 mediaType.matchIgnoreAsciiCaseAsciiL(
1310 RTL_CONSTASCII_STRINGPARAM(
1311 "application/vnd.sun.star.legacy-package-bundle") ));
1312 }
1313
1314 //______________________________________________________________________________
bindBundleItem(OUString const & url,OUString const & mediaType,sal_Bool bRemoved,OUString const & identifier,Reference<ucb::XCommandEnvironment> const & xCmdEnv,bool notifyDetectionError)1315 Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
1316 OUString const & url, OUString const & mediaType,
1317 sal_Bool bRemoved, OUString const & identifier,
1318 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1319 bool notifyDetectionError )
1320 {
1321 // ignore any nested bundles:
1322 if (isBundle_(mediaType))
1323 return Reference<deployment::XPackage>();
1324
1325 Reference<deployment::XPackage>xPackage;
1326 try {
1327 xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
1328 url, mediaType, bRemoved, identifier, xCmdEnv ) );
1329 OSL_ASSERT( xPackage.is() );
1330 }
1331 catch (RuntimeException &) {
1332 throw;
1333 }
1334 catch (ucb::CommandFailedException &) {
1335 // ignore already handled error
1336 }
1337 catch (Exception &) {
1338 const Any exc( ::cppu::getCaughtException() );
1339 if (notifyDetectionError ||
1340 !exc.isExtractableTo(
1341 ::getCppuType( reinterpret_cast<
1342 lang::IllegalArgumentException const *>(0) ) ))
1343 {
1344 interactContinuation(
1345 Any( lang::WrappedTargetException(
1346 OUSTR("bundle item error!"),
1347 static_cast<OWeakObject *>(this), exc ) ),
1348 task::XInteractionApprove::static_type(), xCmdEnv, 0, 0 );
1349 }
1350 }
1351
1352 if (xPackage.is()) {
1353 const Reference<deployment::XPackageTypeInfo> xPackageType(
1354 xPackage->getPackageType() );
1355 OSL_ASSERT( xPackageType.is() );
1356 // ignore any nested bundles:
1357 if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
1358 xPackage.clear();
1359 }
1360 return xPackage;
1361 }
1362
1363 //______________________________________________________________________________
scanBundle(t_packagevec & bundle,::rtl::Reference<AbortChannel> const & abortChannel,Reference<ucb::XCommandEnvironment> const & xCmdEnv)1364 void BackendImpl::PackageImpl::scanBundle(
1365 t_packagevec & bundle,
1366 ::rtl::Reference<AbortChannel> const & abortChannel,
1367 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1368 {
1369 OSL_ASSERT( !m_legacyBundle );
1370
1371 ::ucbhelper::Content manifestContent;
1372 if (! create_ucb_content(
1373 &manifestContent,
1374 makeURL( m_url_expanded, OUSTR("META-INF/manifest.xml") ),
1375 xCmdEnv, false /* no throw */ ))
1376 {
1377 OSL_ENSURE( 0, "### missing META-INF/manifest.xml file!" );
1378 return;
1379 }
1380
1381
1382 const lang::Locale officeLocale = getOfficeLocale();
1383 OUString descrFile;
1384 lang::Locale descrFileLocale;
1385
1386 const Reference<XComponentContext> xContext(
1387 getMyBackend()->getComponentContext() );
1388 Reference<packages::manifest::XManifestReader> xManifestReader(
1389 xContext->getServiceManager()->createInstanceWithContext(
1390 OUSTR("com.sun.star.packages.manifest.ManifestReader"),
1391 xContext ), UNO_QUERY_THROW );
1392 const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
1393 xManifestReader->readManifestSequence( manifestContent.openStream() ) );
1394 const OUString packageRootURL( getURL() );
1395 for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
1396 {
1397 OUString fullPath, mediaType;
1398 Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
1399 for ( sal_Int32 i = attribs.getLength(); i--; )
1400 {
1401 if (fullPath.getLength() > 0 && mediaType.getLength() > 0)
1402 break;
1403 if (attribs[i].Name.equalsAsciiL(
1404 RTL_CONSTASCII_STRINGPARAM("FullPath") ))
1405 attribs[i].Value >>= fullPath;
1406 else if (attribs[i].Name.equalsAsciiL(
1407 RTL_CONSTASCII_STRINGPARAM("MediaType") ))
1408 attribs[i].Value >>= mediaType;
1409 }
1410
1411 if (fullPath.getLength() == 0 || mediaType.getLength() == 0 ||
1412 mediaType.equalsAsciiL( // opt: exclude common text/xml
1413 RTL_CONSTASCII_STRINGPARAM("text/xml") ))
1414 continue;
1415
1416 String type, subType;
1417 INetContentTypeParameterList params;
1418 if (! INetContentTypes::parse( mediaType, type, subType, ¶ms ))
1419 continue;
1420
1421 INetContentTypeParameter const * param = params.find(
1422 ByteString("platform") );
1423 if (param != 0 && !platform_fits( param->m_sValue ))
1424 continue;
1425 const OUString url( makeURL( packageRootURL, fullPath ) );
1426
1427 // check for bundle description:
1428 if (type.EqualsIgnoreCaseAscii("application") &&
1429 subType.EqualsIgnoreCaseAscii(
1430 "vnd.sun.star.package-bundle-description"))
1431 {
1432 // check locale:
1433 param = params.find( ByteString("locale") );
1434 if (param == 0) {
1435 if (descrFile.getLength() == 0)
1436 descrFile = url;
1437 }
1438 else {
1439 // match best locale:
1440 lang::Locale locale( toLocale(param->m_sValue) );
1441 if (locale.Language == officeLocale.Language)
1442 {
1443 if (descrFileLocale.Country == officeLocale.Country
1444 && locale.Country != officeLocale.Country)
1445 continue;
1446 if (descrFileLocale.Variant == officeLocale.Variant
1447 && locale.Variant != officeLocale.Variant)
1448 continue;
1449 descrFile = url;
1450 descrFileLocale = locale;
1451 }
1452 }
1453 continue;
1454 }
1455
1456 checkAborted( abortChannel );
1457
1458 //We make sure that we only create one XPackage for a particular URL.
1459 //Sometime programmers insert the same URL several times in the manifest
1460 //which may lead to DisposedExceptions.
1461 if (bundle.end() == std::find_if(bundle.begin(), bundle.end(), XPackage_eq(url)))
1462 {
1463 const Reference<deployment::XPackage> xPackage(
1464 bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
1465 if (xPackage.is())
1466 bundle.push_back( xPackage );
1467 }
1468 else
1469 {
1470 fprintf(stderr, "manifest.xml contains a duplicate entry!\n");
1471 }
1472 }
1473
1474 if (descrFile.getLength() > 0)
1475 {
1476 ::ucbhelper::Content descrFileContent;
1477 if (create_ucb_content( &descrFileContent, descrFile,
1478 xCmdEnv, false /* no throw */ ))
1479 {
1480 // patch description:
1481 ::rtl::ByteSequence bytes( readFile( descrFileContent ) );
1482 ::rtl::OUStringBuffer buf;
1483 if ( bytes.getLength() )
1484 {
1485 buf.append( OUString( reinterpret_cast<sal_Char const *>(
1486 bytes.getConstArray() ),
1487 bytes.getLength(), RTL_TEXTENCODING_UTF8 ) );
1488 }
1489 else
1490 {
1491 buf.append( Package::getDescription() );
1492 }
1493 m_oldDescription = buf.makeStringAndClear();
1494 }
1495 }
1496 }
1497
1498 //______________________________________________________________________________
scanLegacyBundle(t_packagevec & bundle,OUString const & url,::rtl::Reference<AbortChannel> const & abortChannel,Reference<ucb::XCommandEnvironment> const & xCmdEnv,bool skip_registration)1499 void BackendImpl::PackageImpl::scanLegacyBundle(
1500 t_packagevec & bundle,
1501 OUString const & url,
1502 ::rtl::Reference<AbortChannel> const & abortChannel,
1503 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1504 bool skip_registration )
1505 {
1506 ::ucbhelper::Content ucbContent( url, xCmdEnv );
1507
1508 // check for platform paths:
1509 const OUString title( ucbContent.getPropertyValue(
1510 StrTitle::get() ).get<OUString>() );
1511 if (title.endsWithIgnoreAsciiCaseAsciiL(
1512 RTL_CONSTASCII_STRINGPARAM(".plt") ) &&
1513 !platform_fits( title.copy( 0, title.getLength() - 4 ) )) {
1514 return;
1515 }
1516 if (title.endsWithIgnoreAsciiCaseAsciiL(
1517 RTL_CONSTASCII_STRINGPARAM("skip_registration") ))
1518 skip_registration = true;
1519
1520 OUString ar [] = { StrTitle::get(), OUSTR("IsFolder") };
1521 Reference<sdbc::XResultSet> xResultSet(
1522 ucbContent.createCursor(
1523 Sequence<OUString>( ar, ARLEN(ar) ),
1524 ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
1525 while (xResultSet->next())
1526 {
1527 checkAborted( abortChannel );
1528
1529 const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
1530 const OUString title_enc( ::rtl::Uri::encode(
1531 xRow->getString( 1 /* Title */ ),
1532 rtl_UriCharClassPchar,
1533 rtl_UriEncodeIgnoreEscapes,
1534 RTL_TEXTENCODING_UTF8 ) );
1535 const OUString path( makeURL( url, title_enc ) );
1536
1537 OUString mediaType;
1538 const Reference<deployment::XPackage> xPackage(
1539 bindBundleItem( path, OUString() /* detect */, false, OUString(),
1540 xCmdEnv, false /* ignore detection errors */ ) );
1541 if (xPackage.is()) {
1542 const Reference<deployment::XPackageTypeInfo> xPackageType(
1543 xPackage->getPackageType() );
1544 OSL_ASSERT( xPackageType.is() );
1545 if (xPackageType.is())
1546 mediaType = xPackageType->getMediaType();
1547
1548 if (skip_registration &&
1549 // xxx todo: additional parsing?
1550 mediaType.matchIgnoreAsciiCaseAsciiL(
1551 RTL_CONSTASCII_STRINGPARAM(
1552 "application/vnd.sun.star.uno-component") ))
1553 continue;
1554
1555 bundle.push_back( xPackage );
1556 }
1557
1558 if (mediaType.getLength() == 0 ||
1559 // script.xlb, dialog.xlb can be met everywhere:
1560 mediaType.matchIgnoreAsciiCaseAsciiL(
1561 RTL_CONSTASCII_STRINGPARAM(
1562 "application/vnd.sun.star.basic-library") ) ||
1563 mediaType.matchIgnoreAsciiCaseAsciiL(
1564 RTL_CONSTASCII_STRINGPARAM(
1565 "application/vnd.sun.star.dialog-library") ))
1566 {
1567 if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
1568 scanLegacyBundle(
1569 bundle, path, abortChannel, xCmdEnv, skip_registration );
1570 }
1571 }
1572 }
1573 }
1574
getDisplayName()1575 OUString BackendImpl::PackageImpl::getDisplayName()
1576 {
1577 if (m_bRemoved)
1578 throw deployment::ExtensionRemovedException();
1579
1580 OUString sName = getDescriptionInfoset().getLocalizedDisplayName();
1581 if (sName.getLength() == 0)
1582 return m_displayName;
1583 else
1584 return sName;
1585 }
1586
1587 ::std::vector<Reference<deployment::XPackage> >
getPackagesFromDb(Reference<ucb::XCommandEnvironment> const & xCmdEnv)1588 BackendImpl::PackageImpl::getPackagesFromDb(
1589 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
1590 {
1591 ::std::vector<Reference<deployment::XPackage> > retVector;
1592
1593 typedef ::std::vector< ::std::pair<OUString, OUString> >::const_iterator ITC;
1594 for (ITC i = m_dbData.items.begin(); i != m_dbData.items.end(); i++)
1595 {
1596 Reference<deployment::XPackage> xExtension =
1597 bindBundleItem(i->first, i->second, true, m_identifier, xCmdEnv);
1598 OSL_ASSERT(xExtension.is());
1599 retVector.push_back(xExtension);
1600 }
1601
1602 return retVector;
1603 }
1604
1605 } // anon namespace
1606
1607 //==============================================================================
create(Reference<deployment::XPackageRegistry> const & xRootRegistry,OUString const & context,OUString const & cachePath,bool readOnly,Reference<XComponentContext> const & xComponentContext)1608 Reference<deployment::XPackageRegistry> create(
1609 Reference<deployment::XPackageRegistry> const & xRootRegistry,
1610 OUString const & context, OUString const & cachePath, bool readOnly,
1611 Reference<XComponentContext> const & xComponentContext )
1612 {
1613 Sequence<Any> args(
1614 cachePath.getLength() == 0 ? 1 : 3 );
1615 args[ 0 ] <<= context;
1616 if (cachePath.getLength() > 0) {
1617 args[ 1 ] <<= cachePath;
1618 args[ 2 ] <<= readOnly;
1619 }
1620 return new BackendImpl( args, xComponentContext, xRootRegistry );
1621 }
1622
1623 } // namespace bundle
1624 } // namespace backend
1625 } // namespace dp_registry
1626