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