LibreOffice Module desktop (master)  1
dp_gui_extensioncmdqueue.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
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>
65 #include <vcl/svapp.hxx>
66 #include <vcl/weld.hxx>
68 
69 #include "dp_gui.h"
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"
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 
98 OUString getVersion( OUString const & sVersion )
99 {
100  return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
101 }
102 
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 
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 
122  OUString m_sTitle;
125 
126  void updateProgress();
127 
129  void update_( uno::Any const & Status );
130 
131 public:
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 
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 );
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 
172 {
174 
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 
182  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 ) {};
190  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 ) {};
195  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 
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 );
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 
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;
263 };
264 
265 
267 {
268  m_nCurrentProgress = 0;
269 
270  if ( m_pDialogHelper )
271  m_pDialogHelper->showProgress( true );
272 }
273 
274 
276 {
277  if ( m_pDialogHelper )
278  m_pDialogHelper->showProgress( false );
279 }
280 
281 
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 );
290  }
291 }
292 
293 
295 {
296  long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
297  if ( m_pDialogHelper )
298  m_pDialogHelper->updateProgress( nProgress );
299 }
300 
301 // XCommandEnvironment
302 
303 uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
304 {
305  return this;
306 }
307 
308 
309 uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
310 {
311  return this;
312 }
313 
314 
315 // XInteractionHandler
316 
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)
381  DependencyDialog aDlg(activeDialog(), deps);
382  short n = aDlg.run();
383  if (m_pDialogHelper)
385  // Distinguish between closing the dialog and programmatically
386  // canceling the dialog (headless VCL):
387  approve = n == RET_OK
389  }
390  }
391  else if (request >>= licExc)
392  {
393  SolarMutexGuard guard;
394 
395  weld::Window *pTopLevel = activeDialog();
396  if (m_pDialogHelper)
398  uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
400  m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
401  licExc.ExtensionName, licExc.Text ) );
402  sal_Int16 res = xDialog->execute();
403  if (m_pDialogHelper)
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)
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)
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)
498  std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
499  VclMessageType::Warning, VclButtonsType::Ok, sMsg));
500  xBox->run();
501  if (m_pDialogHelper)
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 
547 void ProgressCmdEnv::push( uno::Any const & rStatus )
548 {
549  update_( rStatus );
550 }
551 
552 
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)
566  std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
567  VclMessageType::Warning, VclButtonsType::Ok, text));
568  xBox->run();
569  if (m_pDialogHelper)
571  }
573  updateProgress();
574 }
575 
576 
577 void ProgressCmdEnv::update( uno::Any const & rStatus )
578 {
579  update_( rStatus );
580 }
581 
582 
584 {
585  update_( uno::Any() ); // no message
586 }
587 
588 
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 
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 
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 
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 
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 :
649  rPackage ) );
650  _insert( pEntry );
651  }
652 }
653 
654 
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.
665 {
666  osl::MutexGuard aGuard( m_mutex );
667  m_bStopped = true;
668  m_eInput = STOP;
669  m_wakeup.set();
670 }
671 
672 
674 {
675  osl::MutexGuard aGuard( m_mutex );
676  return m_bWorking;
677 }
678 
679 
681 
682 
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;
764  _checkForUpdates( pEntry->m_vExtensionList );
765  break;
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()
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 
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 
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 
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 
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 
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 
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 
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 
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 
1075  stop();
1076 }
1077 
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 
1085 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
1086 {
1087  m_thread->removeExtension( rPackage );
1088 }
1089 
1090 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
1091  const bool bEnable )
1092 {
1093  m_thread->enableExtension( rPackage, bEnable );
1094 }
1095 
1096 void ExtensionCmdQueue::checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
1097 {
1098  m_thread->checkForUpdates( vExtensionList );
1099 }
1100 
1101 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
1102 {
1103  m_thread->acceptLicense( rPackage );
1104 }
1105 
1106 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
1107 {
1108  dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1109 }
1110 
1112 {
1113  m_thread->stop();
1114 }
1115 
1117 {
1118  return m_thread->isBusy();
1119 }
1120 
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: */
The modal “Download and Installation” dialog.
virtual void updateProgress(const OUString &rText, const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel)=0
bool hasValue()
void _removeExtension(::rtl::Reference< ProgressCmdEnv > const &rCmdEnv, const uno::Reference< deployment::XPackage > &xPackage)
virtual css::uno::Reference< css::awt::XWindow > GetXWindow()=0
bool installExtensionWarn(const OUString &rExtensionURL)
Thread(DialogHelper *pDialogHelper, TheExtensionManager *pManager, const uno::Reference< uno::XComponentContext > &rContext)
void checkForUpdates(const std::vector< uno::Reference< deployment::XPackage > > &vExtensionList)
void notifyMenubar(bool bPrepareOnly, bool bRecheckOnly)
void syncRepositories(bool force, Reference< ucb::XCommandEnvironment > const &xCmdEnv)
Definition: dp_misc.cxx:532
virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC Order compareVersions(OUString const &version1, OUString const &version2)
Definition: dp_version.cxx:42
static void createNotifyJob(bool bPrepareOnly, css::uno::Sequence< css::uno::Sequence< OUString > > const &rItemList)
void _checkForUpdates(const std::vector< uno::Reference< deployment::XPackage > > &vExtensionList)
RET_CANCEL
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
virtual void SAL_CALL update(uno::Any const &Status) override
ProgressCmdEnv(const uno::Reference< uno::XComponentContext > &rContext, DialogHelper *pDialogHelper, const OUString &rTitle)
When param bAskWhenInstalling = true, then the user is asked if he agrees to install this extension...
NONE
virtual void showProgress(bool bStart)=0
void progressSection(const OUString &rText, const uno::Reference< task::XAbortChannel > &xAbortChannel)
Any SAL_CALL getCaughtException()
uno::Reference< uno::XComponentContext > m_xContext
bool getPropertyValue(ValueType &rValue, css::uno::Reference< css::beans::XPropertySet > const &xPropSet, OUString const &propName)
void removeExtension(const uno::Reference< deployment::XPackage > &rPackage)
STOP
void removeExtension(const css::uno::Reference< css::deployment::XPackage > &rPackage)
std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList
virtual void SAL_CALL push(uno::Any const &Status) override
std::shared_ptr< ExtensionCmd > TExtensionCmd
if(nullptr==pCandidateA||nullptr==pCandidateB)
static bool IsDialogCancelEnabled()
void _acceptLicense(::rtl::Reference< ProgressCmdEnv > const &rCmdEnv, const uno::Reference< deployment::XPackage > &xPackage)
void addExtension(const OUString &rExtensionURL, const OUString &rRepository, const bool bWarnUser)
virtual void SAL_CALL handle(uno::Reference< task::XInteractionRequest > const &xRequest) override
ExtensionCmd(const E_CMD_TYPE eCommand, const OUString &rExtensionURL, const OUString &rRepository, const bool bWarnUser)
void checkForUpdates(const std::vector< css::uno::Reference< css::deployment::XPackage > > &vList)
ExtensionCmd(const E_CMD_TYPE eCommand, const uno::Reference< deployment::XPackage > &rPackage)
void _enableExtension(::rtl::Reference< ProgressCmdEnv > const &rCmdEnv, const uno::Reference< deployment::XPackage > &xPackage)
OUString sName
int i
void update_(uno::Any const &Status)
The modal “Check for Updates” dialog.
void acceptLicense(const uno::Reference< deployment::XPackage > &rPackage)
rtl::Reference< Thread > m_thread
uno::Reference< task::XInteractionHandler2 > m_xHandler
void _disableExtension(::rtl::Reference< ProgressCmdEnv > const &rCmdEnv, const uno::Reference< deployment::XPackage > &xPackage)
void _insert(const TExtensionCmd &rExtCmd)
static void syncRepositories(const css::uno::Reference< css::uno::XComponentContext > &xContext)
static uno::Reference< css::uno::XComponentContext > xContext
Definition: init.cxx:1858
void handleInteractionRequest(const uno::Reference< uno::XComponentContext > &xContext, const uno::Reference< task::XInteractionRequest > &xRequest)
ExtensionCmdQueue(DialogHelper *pDialogHelper, TheExtensionManager *pManager, const css::uno::Reference< css::uno::XComponentContext > &rContext)
Create an instance.
void stop()
This call does not block.
void acceptLicense(const css::uno::Reference< css::deployment::XPackage > &rPackage)
void TRACE(OUString const &sText)
print the text to the console in a debug build.
Definition: dp_misc.cxx:527
void enableExtension(const css::uno::Reference< css::deployment::XPackage > &rPackage, const bool bEnable)
RET_OK
weld::Window * getFrameWeld() const
virtual short run()
void enableExtension(const uno::Reference< deployment::XPackage > &rPackage, const bool bEnable)
uno::Reference< deployment::XPackage > m_xPackage
void addExtension(const OUString &rExtensionURL, const OUString &rRepository, const bool bWarnUser)
virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage)
def text(shape, st)
OUString DpResId(const char *pId)
Definition: dp_shared.hxx:38
ExtensionCmd(const E_CMD_TYPE eCommand, const std::vector< uno::Reference< deployment::XPackage > > &vExtensionList)
Definition: dp_gui.h:50
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getErrorText(css::uno::Reference< css::xml::dom::XElement > const &dependency)
Obtain the (human-readable) error message of a failed dependency.
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getIdentifier(css::uno::Reference< css::deployment::XPackage > const &package)
Gets the identifier of a package.
virtual void SAL_CALL pop() override
void _addExtension(::rtl::Reference< ProgressCmdEnv > const &rCmdEnv, const OUString &rPackageURL, const OUString &rRepository, const bool bWarnUser)
uno::Reference< uno::XComponentContext > m_xContext
const uno::Reference< uno::XComponentContext > m_xContext
OUString anyToString(uno::Any const &value)
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo