xref: /trunk/main/desktop/source/deployment/registry/package/dp_package.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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, &params ))
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, &params ) &&
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, &params ))
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