1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <sal/config.h>
21
22 #include <cstddef>
23
24 #include <com/sun/star/beans/NamedValue.hpp>
25
26 #include <com/sun/star/deployment/DependencyException.hpp>
27 #include <com/sun/star/deployment/LicenseException.hpp>
28 #include <com/sun/star/deployment/VersionException.hpp>
29 #include <com/sun/star/deployment/InstallException.hpp>
30 #include <com/sun/star/deployment/PlatformException.hpp>
31
32 #include <com/sun/star/deployment/ui/LicenseDialog.hpp>
33 #include <com/sun/star/deployment/DeploymentException.hpp>
34 #include <com/sun/star/deployment/UpdateInformationProvider.hpp>
35 #include <com/sun/star/deployment/XPackage.hpp>
36
37 #include <com/sun/star/task/InteractionHandler.hpp>
38 #include <com/sun/star/task/XAbortChannel.hpp>
39 #include <com/sun/star/task/XInteractionAbort.hpp>
40 #include <com/sun/star/task/XInteractionApprove.hpp>
41
42 #include <com/sun/star/ucb/CommandAbortedException.hpp>
43 #include <com/sun/star/ucb/CommandFailedException.hpp>
44 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
45
46 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
47
48 #include <com/sun/star/uno/Reference.hxx>
49 #include <com/sun/star/uno/RuntimeException.hpp>
50 #include <com/sun/star/uno/Sequence.hxx>
51 #include <com/sun/star/uno/XInterface.hpp>
52 #include <com/sun/star/uno/TypeClass.hpp>
53 #include <o3tl/any.hxx>
54 #include <osl/diagnose.h>
55 #include <osl/mutex.hxx>
56 #include <rtl/ref.hxx>
57 #include <rtl/ustring.h>
58 #include <rtl/ustring.hxx>
59 #include <sal/types.h>
60 #include <salhelper/thread.hxx>
61 #include <ucbhelper/content.hxx>
62 #include <cppuhelper/exc_hlp.hxx>
63 #include <cppuhelper/implbase.hxx>
64 #include <comphelper/anytostring.hxx>
65 #include <vcl/svapp.hxx>
66 #include <vcl/weld.hxx>
67 #include <toolkit/helper/vclunohelper.hxx>
68
69 #include "dp_gui.h"
70 #include "dp_gui_extensioncmdqueue.hxx"
71 #include "dp_gui_dependencydialog.hxx"
72 #include "dp_gui_dialog2.hxx"
73 #include <dp_shared.hxx>
74 #include <strings.hrc>
75 #include "dp_gui_theextmgr.hxx"
76 #include "dp_gui_updatedialog.hxx"
77 #include "dp_gui_updateinstalldialog.hxx"
78 #include <dp_dependencies.hxx>
79 #include <dp_identifier.hxx>
80 #include <dp_version.hxx>
81
82 #include <queue>
83 #include <memory>
84
85 #ifdef _WIN32
86 #if !defined WIN32_LEAN_AND_MEAN
87 # define WIN32_LEAN_AND_MEAN
88 #endif
89 #include <windows.h>
90 #include <objbase.h>
91 #endif
92
93
94 using namespace ::com::sun::star;
95
96 namespace {
97
getVersion(OUString const & sVersion)98 OUString getVersion( OUString const & sVersion )
99 {
100 return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
101 }
102
getVersion(const uno::Reference<deployment::XPackage> & rPackage)103 OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
104 {
105 return getVersion( rPackage->getVersion());
106 }
107 }
108
109
110 namespace dp_gui {
111
112
113 class ProgressCmdEnv
114 : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
115 task::XInteractionHandler,
116 ucb::XProgressHandler >
117 {
118 uno::Reference< task::XInteractionHandler2> m_xHandler;
119 uno::Reference< uno::XComponentContext > m_xContext;
120
121 DialogHelper* m_pDialogHelper;
122 OUString m_sTitle;
123 bool m_bWarnUser;
124 sal_Int32 m_nCurrentProgress;
125
126 void updateProgress();
127
128 /// @throws uno::RuntimeException
129 void update_( uno::Any const & Status );
130
131 public:
132 /** When param bAskWhenInstalling = true, then the user is asked if he
133 agrees to install this extension. In case this extension is already installed
134 then the user is also notified and asked if he wants to replace that existing
135 extension. In first case an interaction request with an InstallException
136 will be handled and in the second case a VersionException will be handled.
137 */
138
ProgressCmdEnv(const uno::Reference<uno::XComponentContext> & rContext,DialogHelper * pDialogHelper,const OUString & rTitle)139 ProgressCmdEnv( const uno::Reference< uno::XComponentContext >& rContext,
140 DialogHelper* pDialogHelper,
141 const OUString& rTitle )
142 : m_xContext( rContext )
143 , m_pDialogHelper( pDialogHelper )
144 , m_sTitle( rTitle )
145 , m_bWarnUser( false )
146 , m_nCurrentProgress(0)
147 {}
148
activeDialog()149 weld::Window* activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr; }
150
151 void startProgress();
152 void stopProgress();
153 void progressSection( const OUString &rText,
154 const uno::Reference< task::XAbortChannel > &xAbortChannel );
setWarnUser(bool bNewVal)155 void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
156
157 // XCommandEnvironment
158 virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
159 virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override;
160
161 // XInteractionHandler
162 virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) override;
163
164 // XProgressHandler
165 virtual void SAL_CALL push( uno::Any const & Status ) override;
166 virtual void SAL_CALL update( uno::Any const & Status ) override;
167 virtual void SAL_CALL pop() override;
168 };
169
170
171 struct ExtensionCmd
172 {
173 enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
174
175 E_CMD_TYPE m_eCmdType;
176 bool m_bWarnUser;
177 OUString m_sExtensionURL;
178 OUString m_sRepository;
179 uno::Reference< deployment::XPackage > m_xPackage;
180 std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
181
ExtensionCmddp_gui::ExtensionCmd182 ExtensionCmd( const E_CMD_TYPE eCommand,
183 const OUString &rExtensionURL,
184 const OUString &rRepository,
185 const bool bWarnUser )
186 : m_eCmdType( eCommand ),
187 m_bWarnUser( bWarnUser ),
188 m_sExtensionURL( rExtensionURL ),
189 m_sRepository( rRepository ) {};
ExtensionCmddp_gui::ExtensionCmd190 ExtensionCmd( const E_CMD_TYPE eCommand,
191 const uno::Reference< deployment::XPackage > &rPackage )
192 : m_eCmdType( eCommand ),
193 m_bWarnUser( false ),
194 m_xPackage( rPackage ) {};
ExtensionCmddp_gui::ExtensionCmd195 ExtensionCmd( const E_CMD_TYPE eCommand,
196 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
197 : m_eCmdType( eCommand ),
198 m_bWarnUser( false ),
199 m_vExtensionList( vExtensionList ) {};
200 };
201
202 typedef std::shared_ptr< ExtensionCmd > TExtensionCmd;
203
204
205 class ExtensionCmdQueue::Thread: public salhelper::Thread
206 {
207 public:
208 Thread( DialogHelper *pDialogHelper,
209 TheExtensionManager *pManager,
210 const uno::Reference< uno::XComponentContext > & rContext );
211
212 void addExtension( const OUString &rExtensionURL,
213 const OUString &rRepository,
214 const bool bWarnUser );
215 void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
216 void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
217 const bool bEnable );
218 void checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
219 void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
220 void stop();
221 bool isBusy();
222
223 private:
224 virtual ~Thread() override;
225
226 virtual void execute() override;
227
228 void _insert(const TExtensionCmd& rExtCmd);
229
230 void _addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
231 const OUString &rPackageURL,
232 const OUString &rRepository,
233 const bool bWarnUser );
234 void _removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
235 const uno::Reference< deployment::XPackage > &xPackage );
236 void _enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
237 const uno::Reference< deployment::XPackage > &xPackage );
238 void _disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
239 const uno::Reference< deployment::XPackage > &xPackage );
240 void _checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
241 void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
242 const uno::Reference< deployment::XPackage > &xPackage );
243
244 enum Input { NONE, START, STOP };
245
246 uno::Reference< uno::XComponentContext > m_xContext;
247 std::queue< TExtensionCmd > m_queue;
248
249 DialogHelper *m_pDialogHelper;
250 TheExtensionManager *m_pManager;
251
252 const OUString m_sEnablingPackages;
253 const OUString m_sDisablingPackages;
254 const OUString m_sAddingPackages;
255 const OUString m_sRemovingPackages;
256 const OUString m_sDefaultCmd;
257 const OUString m_sAcceptLicense;
258 osl::Condition m_wakeup;
259 osl::Mutex m_mutex;
260 Input m_eInput;
261 bool m_bStopped;
262 bool m_bWorking;
263 };
264
265
startProgress()266 void ProgressCmdEnv::startProgress()
267 {
268 m_nCurrentProgress = 0;
269
270 if ( m_pDialogHelper )
271 m_pDialogHelper->showProgress( true );
272 }
273
274
stopProgress()275 void ProgressCmdEnv::stopProgress()
276 {
277 if ( m_pDialogHelper )
278 m_pDialogHelper->showProgress( false );
279 }
280
281
progressSection(const OUString & rText,const uno::Reference<task::XAbortChannel> & xAbortChannel)282 void ProgressCmdEnv::progressSection( const OUString &rText,
283 const uno::Reference< task::XAbortChannel > &xAbortChannel )
284 {
285 m_nCurrentProgress = 0;
286 if ( m_pDialogHelper )
287 {
288 m_pDialogHelper->updateProgress( rText, xAbortChannel );
289 m_pDialogHelper->updateProgress( 5 );
290 }
291 }
292
293
updateProgress()294 void ProgressCmdEnv::updateProgress()
295 {
296 long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
297 if ( m_pDialogHelper )
298 m_pDialogHelper->updateProgress( nProgress );
299 }
300
301 // XCommandEnvironment
302
getInteractionHandler()303 uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
304 {
305 return this;
306 }
307
308
getProgressHandler()309 uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
310 {
311 return this;
312 }
313
314
315 // XInteractionHandler
316
handle(uno::Reference<task::XInteractionRequest> const & xRequest)317 void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
318 {
319 uno::Any request( xRequest->getRequest() );
320 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
321 dp_misc::TRACE( "[dp_gui_cmdenv.cxx] incoming request:\n"
322 + ::comphelper::anyToString(request) + "\n");
323
324 lang::WrappedTargetException wtExc;
325 deployment::DependencyException depExc;
326 deployment::LicenseException licExc;
327 deployment::VersionException verExc;
328 deployment::InstallException instExc;
329 deployment::PlatformException platExc;
330
331 // selections:
332 bool approve = false;
333 bool abort = false;
334
335 if (request >>= wtExc) {
336 // handable deployment error signalled, e.g.
337 // bundle item registration failed, notify cause only:
338 uno::Any cause;
339 deployment::DeploymentException dpExc;
340 if (wtExc.TargetException >>= dpExc)
341 cause = dpExc.Cause;
342 else {
343 ucb::CommandFailedException cfExc;
344 if (wtExc.TargetException >>= cfExc)
345 cause = cfExc.Reason;
346 else
347 cause = wtExc.TargetException;
348 }
349 update_( cause );
350
351 // ignore intermediate errors of legacy packages, i.e.
352 // former pkgchk behaviour:
353 const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
354 OSL_ASSERT( xPackage.is() );
355 if ( xPackage.is() )
356 {
357 const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
358 OSL_ASSERT( xPackageType.is() );
359 if (xPackageType.is())
360 {
361 approve = ( xPackage->isBundle() &&
362 xPackageType->getMediaType().match(
363 "application/vnd.sun.star.legacy-package-bundle" ));
364 }
365 }
366 abort = !approve;
367 }
368 else if (request >>= depExc)
369 {
370 std::vector< OUString > deps;
371 deps.reserve(depExc.UnsatisfiedDependencies.getLength());
372 for (sal_Int32 i = 0; i < depExc.UnsatisfiedDependencies.getLength(); ++i)
373 {
374 deps.push_back(
375 dp_misc::Dependencies::getErrorText( depExc.UnsatisfiedDependencies[i]) );
376 }
377 {
378 SolarMutexGuard guard;
379 if (m_pDialogHelper)
380 m_pDialogHelper->incBusy();
381 DependencyDialog aDlg(activeDialog(), deps);
382 short n = aDlg.run();
383 if (m_pDialogHelper)
384 m_pDialogHelper->decBusy();
385 // Distinguish between closing the dialog and programmatically
386 // canceling the dialog (headless VCL):
387 approve = n == RET_OK
388 || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
389 }
390 }
391 else if (request >>= licExc)
392 {
393 SolarMutexGuard guard;
394
395 weld::Window *pTopLevel = activeDialog();
396 if (m_pDialogHelper)
397 m_pDialogHelper->incBusy();
398 uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
399 deployment::ui::LicenseDialog::create(
400 m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
401 licExc.ExtensionName, licExc.Text ) );
402 sal_Int16 res = xDialog->execute();
403 if (m_pDialogHelper)
404 m_pDialogHelper->decBusy();
405 if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
406 abort = true;
407 else if ( res == ui::dialogs::ExecutableDialogResults::OK )
408 approve = true;
409 else
410 {
411 OSL_ASSERT(false);
412 }
413 }
414 else if (request >>= verExc)
415 {
416 const char* id;
417 switch (dp_misc::compareVersions(
418 verExc.NewVersion, verExc.Deployed->getVersion() ))
419 {
420 case dp_misc::LESS:
421 id = RID_STR_WARNING_VERSION_LESS;
422 break;
423 case dp_misc::EQUAL:
424 id = RID_STR_WARNING_VERSION_EQUAL;
425 break;
426 default: // dp_misc::GREATER
427 id = RID_STR_WARNING_VERSION_GREATER;
428 break;
429 }
430 OSL_ASSERT( verExc.Deployed.is() );
431 bool bEqualNames = verExc.NewDisplayName ==
432 verExc.Deployed->getDisplayName();
433 {
434 SolarMutexGuard guard;
435
436 if (m_pDialogHelper)
437 m_pDialogHelper->incBusy();
438
439 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
440 VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(id)));
441 OUString s;
442 if (bEqualNames)
443 {
444 s = xBox->get_primary_text();
445 }
446 else if (!strcmp(id, RID_STR_WARNING_VERSION_EQUAL))
447 {
448 //hypothetical: requires two instances of an extension with the same
449 //version to have different display names. Probably the developer forgot
450 //to change the version.
451 s = DpResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES);
452 }
453 else if (!strcmp(id, RID_STR_WARNING_VERSION_LESS))
454 {
455 s = DpResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES);
456 }
457 else if (!strcmp(id, RID_STR_WARNING_VERSION_GREATER))
458 {
459 s = DpResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES);
460 }
461 s = s.replaceAll("$NAME", verExc.NewDisplayName);
462 s = s.replaceAll("$OLDNAME", verExc.Deployed->getDisplayName());
463 s = s.replaceAll("$NEW", getVersion(verExc.NewVersion));
464 s = s.replaceAll("$DEPLOYED", getVersion(verExc.Deployed));
465 xBox->set_primary_text(s);
466 approve = xBox->run() == RET_OK;
467 if (m_pDialogHelper)
468 m_pDialogHelper->decBusy();
469 abort = !approve;
470 }
471 }
472 else if (request >>= instExc)
473 {
474 if ( ! m_bWarnUser )
475 {
476 approve = true;
477 }
478 else
479 {
480 if ( m_pDialogHelper )
481 {
482 SolarMutexGuard guard;
483
484 approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
485 }
486 else
487 approve = false;
488 abort = !approve;
489 }
490 }
491 else if (request >>= platExc)
492 {
493 SolarMutexGuard guard;
494 OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
495 sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
496 if (m_pDialogHelper)
497 m_pDialogHelper->incBusy();
498 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
499 VclMessageType::Warning, VclButtonsType::Ok, sMsg));
500 xBox->run();
501 if (m_pDialogHelper)
502 m_pDialogHelper->decBusy();
503 approve = true;
504 }
505
506 if (!approve && !abort)
507 {
508 // forward to UUI handler:
509 if (! m_xHandler.is()) {
510 // late init:
511 m_xHandler = task::InteractionHandler::createWithParentAndContext(m_xContext, nullptr, m_sTitle);
512 }
513 m_xHandler->handle( xRequest );
514 }
515 else
516 {
517 // select:
518 uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
519 xRequest->getContinuations() );
520 uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
521 sal_Int32 len = conts.getLength();
522 for ( sal_Int32 pos = 0; pos < len; ++pos )
523 {
524 if (approve) {
525 uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
526 if (xInteractionApprove.is()) {
527 xInteractionApprove->select();
528 // don't query again for ongoing continuations:
529 approve = false;
530 }
531 }
532 else if (abort) {
533 uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
534 if (xInteractionAbort.is()) {
535 xInteractionAbort->select();
536 // don't query again for ongoing continuations:
537 abort = false;
538 }
539 }
540 }
541 }
542 }
543
544
545 // XProgressHandler
546
push(uno::Any const & rStatus)547 void ProgressCmdEnv::push( uno::Any const & rStatus )
548 {
549 update_( rStatus );
550 }
551
552
update_(uno::Any const & rStatus)553 void ProgressCmdEnv::update_( uno::Any const & rStatus )
554 {
555 OUString text;
556 if ( rStatus.hasValue() && !( rStatus >>= text) )
557 {
558 if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
559 text = e->Message;
560 if ( text.isEmpty() )
561 text = ::comphelper::anyToString( rStatus ); // fallback
562
563 const SolarMutexGuard aGuard;
564 if (m_pDialogHelper)
565 m_pDialogHelper->incBusy();
566 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
567 VclMessageType::Warning, VclButtonsType::Ok, text));
568 xBox->run();
569 if (m_pDialogHelper)
570 m_pDialogHelper->decBusy();
571 }
572 ++m_nCurrentProgress;
573 updateProgress();
574 }
575
576
update(uno::Any const & rStatus)577 void ProgressCmdEnv::update( uno::Any const & rStatus )
578 {
579 update_( rStatus );
580 }
581
582
pop()583 void ProgressCmdEnv::pop()
584 {
585 update_( uno::Any() ); // no message
586 }
587
588
Thread(DialogHelper * pDialogHelper,TheExtensionManager * pManager,const uno::Reference<uno::XComponentContext> & rContext)589 ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
590 TheExtensionManager *pManager,
591 const uno::Reference< uno::XComponentContext > & rContext ) :
592 salhelper::Thread( "dp_gui_extensioncmdqueue" ),
593 m_xContext( rContext ),
594 m_pDialogHelper( pDialogHelper ),
595 m_pManager( pManager ),
596 m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
597 m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
598 m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
599 m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
600 m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
601 m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
602 m_eInput( NONE ),
603 m_bStopped( false ),
604 m_bWorking( false )
605 {
606 OSL_ASSERT( pDialogHelper );
607 }
608
609
addExtension(const OUString & rExtensionURL,const OUString & rRepository,const bool bWarnUser)610 void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
611 const OUString &rRepository,
612 const bool bWarnUser )
613 {
614 if ( !rExtensionURL.isEmpty() )
615 {
616 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser ) );
617 _insert( pEntry );
618 }
619 }
620
621
removeExtension(const uno::Reference<deployment::XPackage> & rPackage)622 void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
623 {
624 if ( rPackage.is() )
625 {
626 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::REMOVE, rPackage ) );
627 _insert( pEntry );
628 }
629 }
630
631
acceptLicense(const uno::Reference<deployment::XPackage> & rPackage)632 void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
633 {
634 if ( rPackage.is() )
635 {
636 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ACCEPT_LICENSE, rPackage ) );
637 _insert( pEntry );
638 }
639 }
640
641
enableExtension(const uno::Reference<deployment::XPackage> & rPackage,const bool bEnable)642 void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
643 const bool bEnable )
644 {
645 if ( rPackage.is() )
646 {
647 TExtensionCmd pEntry( new ExtensionCmd( bEnable ? ExtensionCmd::ENABLE :
648 ExtensionCmd::DISABLE,
649 rPackage ) );
650 _insert( pEntry );
651 }
652 }
653
654
checkForUpdates(const std::vector<uno::Reference<deployment::XPackage>> & vExtensionList)655 void ExtensionCmdQueue::Thread::checkForUpdates(
656 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
657 {
658 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::CHECK_FOR_UPDATES, vExtensionList ) );
659 _insert( pEntry );
660 }
661
662
663 //Stopping this thread will not abort the installation of extensions.
stop()664 void ExtensionCmdQueue::Thread::stop()
665 {
666 osl::MutexGuard aGuard( m_mutex );
667 m_bStopped = true;
668 m_eInput = STOP;
669 m_wakeup.set();
670 }
671
672
isBusy()673 bool ExtensionCmdQueue::Thread::isBusy()
674 {
675 osl::MutexGuard aGuard( m_mutex );
676 return m_bWorking;
677 }
678
679
~Thread()680 ExtensionCmdQueue::Thread::~Thread() {}
681
682
execute()683 void ExtensionCmdQueue::Thread::execute()
684 {
685 #ifdef _WIN32
686 //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
687 //DialogHelper::openWebBrowser
688 CoUninitialize();
689 (void) CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
690 #endif
691 for (;;)
692 {
693 if ( m_wakeup.wait() != osl::Condition::result_ok )
694 {
695 dp_misc::TRACE( "dp_gui::ExtensionCmdQueue::Thread::run: ignored "
696 "osl::Condition::wait failure\n" );
697 }
698 m_wakeup.reset();
699
700 int nSize;
701 Input eInput;
702 {
703 osl::MutexGuard aGuard( m_mutex );
704 eInput = m_eInput;
705 m_eInput = NONE;
706 nSize = m_queue.size();
707 m_bWorking = false;
708 }
709
710 // If this thread has been woken up by anything else except start, stop
711 // then input is NONE and we wait again.
712 // We only install the extension which are currently in the queue.
713 // The progressbar will be set to show the progress of the current number
714 // of extensions. If we allowed to add extensions now then the progressbar may
715 // have reached the end while we still install newly added extensions.
716 if ( ( eInput == NONE ) || ( nSize == 0 ) )
717 continue;
718 if ( eInput == STOP )
719 break;
720
721 ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
722
723 // Do not lock the following part with addExtension. addExtension may be called in the main thread.
724 // If the message box "Do you want to install the extension (or similar)" is shown and then
725 // addExtension is called, which then blocks the main thread, then we deadlock.
726 bool bStartProgress = true;
727
728 while ( --nSize >= 0 )
729 {
730 {
731 osl::MutexGuard aGuard( m_mutex );
732 m_bWorking = true;
733 }
734
735 try
736 {
737 TExtensionCmd pEntry;
738 {
739 ::osl::MutexGuard queueGuard( m_mutex );
740 pEntry = m_queue.front();
741 m_queue.pop();
742 }
743
744 if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
745 {
746 currentCmdEnv->startProgress();
747 bStartProgress = false;
748 }
749
750 switch ( pEntry->m_eCmdType ) {
751 case ExtensionCmd::ADD :
752 _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
753 break;
754 case ExtensionCmd::REMOVE :
755 _removeExtension( currentCmdEnv, pEntry->m_xPackage );
756 break;
757 case ExtensionCmd::ENABLE :
758 _enableExtension( currentCmdEnv, pEntry->m_xPackage );
759 break;
760 case ExtensionCmd::DISABLE :
761 _disableExtension( currentCmdEnv, pEntry->m_xPackage );
762 break;
763 case ExtensionCmd::CHECK_FOR_UPDATES :
764 _checkForUpdates( pEntry->m_vExtensionList );
765 break;
766 case ExtensionCmd::ACCEPT_LICENSE :
767 _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
768 break;
769 }
770 }
771 catch ( const ucb::CommandAbortedException & )
772 {
773 //This exception is thrown when the user clicks cancel on the progressbar.
774 //Then we cancel the installation of all extensions and remove them from
775 //the queue.
776 {
777 ::osl::MutexGuard queueGuard2(m_mutex);
778 while ( --nSize >= 0 )
779 m_queue.pop();
780 }
781 break;
782 }
783 catch ( const ucb::CommandFailedException & )
784 {
785 //This exception is thrown when a user clicked cancel in the messagebox which was
786 //started by the interaction handler. For example the user will be asked if he/she
787 //really wants to install the extension.
788 //These interaction are run for exactly one extension at a time. Therefore we continue
789 //with installing the remaining extensions.
790 continue;
791 }
792 catch ( const uno::Exception & )
793 {
794 //Todo display the user an error
795 //see also DialogImpl::SyncPushButton::Click()
796 uno::Any exc( ::cppu::getCaughtException() );
797 OUString msg;
798 deployment::DeploymentException dpExc;
799 if (exc >>= dpExc)
800 {
801 if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
802 {
803 // notify error cause only:
804 msg = e->Message;
805 }
806 }
807 if (msg.isEmpty()) // fallback for debugging purposes
808 msg = ::comphelper::anyToString(exc);
809
810 const SolarMutexGuard guard;
811 if (m_pDialogHelper)
812 m_pDialogHelper->incBusy();
813
814 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
815 VclMessageType::Warning, VclButtonsType::Ok, msg));
816 if (m_pDialogHelper)
817 xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
818 xBox->run();
819 if (m_pDialogHelper)
820 m_pDialogHelper->decBusy();
821 //Continue with installation of the remaining extensions
822 }
823 {
824 osl::MutexGuard aGuard( m_mutex );
825 m_bWorking = false;
826 }
827 }
828
829 {
830 // when leaving the while loop with break, we should set working to false, too
831 osl::MutexGuard aGuard( m_mutex );
832 m_bWorking = false;
833 }
834
835 if ( !bStartProgress )
836 currentCmdEnv->stopProgress();
837 }
838 //end for
839 #ifdef _WIN32
840 CoUninitialize();
841 #endif
842 }
843
844
_addExtension(::rtl::Reference<ProgressCmdEnv> const & rCmdEnv,const OUString & rPackageURL,const OUString & rRepository,const bool bWarnUser)845 void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
846 const OUString &rPackageURL,
847 const OUString &rRepository,
848 const bool bWarnUser )
849 {
850 //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
851 //and anyTitle.get<OUString> throws as RuntimeException.
852 uno::Any anyTitle;
853 try
854 {
855 anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv.get(), m_xContext ).getPropertyValue( "Title" );
856 }
857 catch ( const uno::Exception & )
858 {
859 return;
860 }
861
862 OUString sName;
863 if ( ! (anyTitle >>= sName) )
864 {
865 OSL_FAIL("Could not get file name for extension.");
866 return;
867 }
868
869 rCmdEnv->setWarnUser( bWarnUser );
870 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
871 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
872 OUString sTitle(
873 m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
874 rCmdEnv->progressSection( sTitle, xAbortChannel );
875
876 try
877 {
878 xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
879 rRepository, xAbortChannel, rCmdEnv.get() );
880 }
881 catch ( const ucb::CommandFailedException & )
882 {
883 // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
884 // cancel this exception is thrown.
885 }
886 catch ( const ucb::CommandAbortedException & )
887 {
888 // User clicked the cancel button
889 // TODO: handle cancel
890 }
891 rCmdEnv->setWarnUser( false );
892 }
893
894
_removeExtension(::rtl::Reference<ProgressCmdEnv> const & rCmdEnv,const uno::Reference<deployment::XPackage> & xPackage)895 void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
896 const uno::Reference< deployment::XPackage > &xPackage )
897 {
898 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
899 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
900 OUString sTitle(
901 m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
902 xPackage->getDisplayName()));
903 rCmdEnv->progressSection( sTitle, xAbortChannel );
904
905 OUString id( dp_misc::getIdentifier( xPackage ) );
906 try
907 {
908 xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv.get() );
909 }
910 catch ( const deployment::DeploymentException & )
911 {}
912 catch ( const ucb::CommandFailedException & )
913 {}
914 catch ( const ucb::CommandAbortedException & )
915 {}
916
917 // Check, if there are still updates to be notified via menu bar icon
918 uno::Sequence< uno::Sequence< OUString > > aItemList;
919 UpdateDialog::createNotifyJob( false, aItemList );
920 }
921
922
_checkForUpdates(const std::vector<uno::Reference<deployment::XPackage>> & vExtensionList)923 void ExtensionCmdQueue::Thread::_checkForUpdates(
924 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
925 {
926 const SolarMutexGuard guard;
927
928 if (m_pDialogHelper)
929 m_pDialogHelper->incBusy();
930
931 std::vector< UpdateData > vData;
932 UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, vExtensionList, &vData);
933
934 aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
935
936 bool bOk = aUpdateDialog.run() == RET_OK;
937 if (m_pDialogHelper)
938 m_pDialogHelper->decBusy();
939
940 if (bOk && !vData.empty())
941 {
942 // If there is at least one directly downloadable extension then we
943 // open the install dialog.
944 std::vector< UpdateData > dataDownload;
945
946 for (auto const& data : vData)
947 {
948 if ( data.sWebsiteURL.isEmpty() )
949 dataDownload.push_back(data);
950 }
951
952 short nDialogResult = RET_OK;
953 if ( !dataDownload.empty() )
954 {
955 if (m_pDialogHelper)
956 m_pDialogHelper->incBusy();
957 UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
958 nDialogResult = aDlg.run();
959 if (m_pDialogHelper)
960 m_pDialogHelper->decBusy();
961 aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
962 }
963 else
964 aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
965
966 //Now start the webbrowser and navigate to the websites where we get the updates
967 if ( RET_OK == nDialogResult )
968 {
969 for (auto const& data : vData)
970 {
971 if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
972 m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
973 }
974 }
975 }
976 else
977 aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
978 }
979
980
_enableExtension(::rtl::Reference<ProgressCmdEnv> const & rCmdEnv,const uno::Reference<deployment::XPackage> & xPackage)981 void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
982 const uno::Reference< deployment::XPackage > &xPackage )
983 {
984 if ( !xPackage.is() )
985 return;
986
987 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
988 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
989 OUString sTitle(
990 m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
991 xPackage->getDisplayName()));
992 rCmdEnv->progressSection( sTitle, xAbortChannel );
993
994 try
995 {
996 xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
997 if ( m_pDialogHelper )
998 m_pDialogHelper->updatePackageInfo( xPackage );
999 }
1000 catch ( const ::ucb::CommandAbortedException & )
1001 {}
1002 }
1003
1004
_disableExtension(::rtl::Reference<ProgressCmdEnv> const & rCmdEnv,const uno::Reference<deployment::XPackage> & xPackage)1005 void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
1006 const uno::Reference< deployment::XPackage > &xPackage )
1007 {
1008 if ( !xPackage.is() )
1009 return;
1010
1011 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1012 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1013 OUString sTitle(
1014 m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
1015 xPackage->getDisplayName()));
1016 rCmdEnv->progressSection( sTitle, xAbortChannel );
1017
1018 try
1019 {
1020 xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
1021 if ( m_pDialogHelper )
1022 m_pDialogHelper->updatePackageInfo( xPackage );
1023 }
1024 catch ( const ::ucb::CommandAbortedException & )
1025 {}
1026 }
1027
1028
_acceptLicense(::rtl::Reference<ProgressCmdEnv> const & rCmdEnv,const uno::Reference<deployment::XPackage> & xPackage)1029 void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
1030 const uno::Reference< deployment::XPackage > &xPackage )
1031 {
1032 if ( !xPackage.is() )
1033 return;
1034
1035 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1036 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1037 OUString sTitle(
1038 m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
1039 xPackage->getDisplayName()));
1040 rCmdEnv->progressSection( sTitle, xAbortChannel );
1041
1042 try
1043 {
1044 xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv.get() );
1045 if ( m_pDialogHelper )
1046 m_pDialogHelper->updatePackageInfo( xPackage );
1047 }
1048 catch ( const ::ucb::CommandAbortedException & )
1049 {}
1050 }
1051
_insert(const TExtensionCmd & rExtCmd)1052 void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
1053 {
1054 ::osl::MutexGuard aGuard( m_mutex );
1055
1056 // If someone called stop then we do not process the command -> game over!
1057 if ( m_bStopped )
1058 return;
1059
1060 m_queue.push( rExtCmd );
1061 m_eInput = START;
1062 m_wakeup.set();
1063 }
1064
1065
ExtensionCmdQueue(DialogHelper * pDialogHelper,TheExtensionManager * pManager,const uno::Reference<uno::XComponentContext> & rContext)1066 ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
1067 TheExtensionManager *pManager,
1068 const uno::Reference< uno::XComponentContext > &rContext )
1069 : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
1070 {
1071 m_thread->launch();
1072 }
1073
~ExtensionCmdQueue()1074 ExtensionCmdQueue::~ExtensionCmdQueue() {
1075 stop();
1076 }
1077
addExtension(const OUString & extensionURL,const OUString & repository,const bool bWarnUser)1078 void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
1079 const OUString & repository,
1080 const bool bWarnUser )
1081 {
1082 m_thread->addExtension( extensionURL, repository, bWarnUser );
1083 }
1084
removeExtension(const uno::Reference<deployment::XPackage> & rPackage)1085 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
1086 {
1087 m_thread->removeExtension( rPackage );
1088 }
1089
enableExtension(const uno::Reference<deployment::XPackage> & rPackage,const bool bEnable)1090 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
1091 const bool bEnable )
1092 {
1093 m_thread->enableExtension( rPackage, bEnable );
1094 }
1095
checkForUpdates(const std::vector<uno::Reference<deployment::XPackage>> & vExtensionList)1096 void ExtensionCmdQueue::checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
1097 {
1098 m_thread->checkForUpdates( vExtensionList );
1099 }
1100
acceptLicense(const uno::Reference<deployment::XPackage> & rPackage)1101 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
1102 {
1103 m_thread->acceptLicense( rPackage );
1104 }
1105
syncRepositories(const uno::Reference<uno::XComponentContext> & xContext)1106 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
1107 {
1108 dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1109 }
1110
stop()1111 void ExtensionCmdQueue::stop()
1112 {
1113 m_thread->stop();
1114 }
1115
isBusy()1116 bool ExtensionCmdQueue::isBusy()
1117 {
1118 return m_thread->isBusy();
1119 }
1120
handleInteractionRequest(const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<task::XInteractionRequest> & xRequest)1121 void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
1122 const uno::Reference< task::XInteractionRequest > & xRequest )
1123 {
1124 ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1125 xCmdEnv->handle( xRequest );
1126 }
1127
1128 } //namespace dp_gui
1129
1130 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1131