Avoid potential negative array index access to cached text.
[LibreOffice.git] / extensions / source / update / check / updatecheckjob.cxx
blobb79c438108ee6b3d24180e2c0a312de186d591d6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include "updatecheck.hxx"
23 #include "updatecheckconfig.hxx"
24 #include "updatehdl.hxx"
25 #include "updateprotocol.hxx"
27 #include <memory>
28 #include <mutex>
29 #include <utility>
31 #include <cppuhelper/implbase.hxx>
32 #include <cppuhelper/supportsservice.hxx>
33 #include <comphelper/diagnose_ex.hxx>
35 #include <com/sun/star/frame/Desktop.hpp>
36 #include <com/sun/star/frame/XTerminateListener.hpp>
37 #include <com/sun/star/task/XJob.hpp>
39 namespace beans = com::sun::star::beans ;
40 namespace frame = com::sun::star::frame ;
41 namespace lang = com::sun::star::lang ;
42 namespace task = com::sun::star::task ;
43 namespace uno = com::sun::star::uno ;
45 namespace
48 class InitUpdateCheckJobThread : public osl::Thread
50 public:
51 InitUpdateCheckJobThread( const uno::Reference< uno::XComponentContext > &xContext,
52 const uno::Sequence< beans::NamedValue > &xParameters,
53 bool bShowDialog );
55 virtual void SAL_CALL run() override;
57 void setTerminating();
59 private:
60 osl::Condition m_aCondition;
61 uno::Reference<uno::XComponentContext> m_xContext;
62 uno::Sequence<beans::NamedValue> m_xParameters;
63 bool m_bShowDialog;
64 bool m_bTerminating;
66 std::mutex m_mutex;
67 rtl::Reference<UpdateCheck> m_controller;
70 class UpdateCheckJob :
71 public ::cppu::WeakImplHelper< task::XJob, lang::XServiceInfo, frame::XTerminateListener >
73 virtual ~UpdateCheckJob() override;
75 public:
77 UpdateCheckJob(
78 css::uno::Reference<css::uno::XComponentContext> const & context,
79 css::uno::Reference<css::frame::XDesktop2> const & desktop):
80 m_xContext(context), m_xDesktop(desktop)
83 // XJob
84 virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
86 // XServiceInfo
87 virtual OUString SAL_CALL getImplementationName() override;
88 virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
89 virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
91 // XEventListener
92 virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
94 // XTerminateListener
95 virtual void SAL_CALL queryTermination( lang::EventObject const & evt ) override;
96 virtual void SAL_CALL notifyTermination( lang::EventObject const & evt ) override;
98 private:
99 uno::Reference<uno::XComponentContext> m_xContext;
101 std::mutex m_mutex;
102 uno::Reference< frame::XDesktop2 > m_xDesktop;
103 std::unique_ptr< InitUpdateCheckJobThread > m_pInitThread;
105 void handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp );
106 void terminateAndJoinThread();
109 InitUpdateCheckJobThread::InitUpdateCheckJobThread(
110 const uno::Reference< uno::XComponentContext > &xContext,
111 const uno::Sequence< beans::NamedValue > &xParameters,
112 bool bShowDialog ) :
113 m_xContext( xContext ),
114 m_xParameters( xParameters ),
115 m_bShowDialog( bShowDialog ),
116 m_bTerminating( false )
118 create();
122 void SAL_CALL InitUpdateCheckJobThread::run()
124 osl_setThreadName("InitUpdateCheckJobThread");
126 if (!m_bShowDialog) {
127 TimeValue tv = { 25, 0 };
128 m_aCondition.wait( &tv );
129 if ( m_bTerminating )
130 return;
133 try {
134 rtl::Reference< UpdateCheck > aController( UpdateCheck::get() );
135 // At least for the automatic ("onFirstVisibleTask", i.e., !m_bShowDialog) check, wait for
136 // m_controller during setTerminating, to prevent m_controller from still having threads
137 // running during exit (ideally, we would make sure that all threads are joined before exit,
138 // but the UpdateCheck logic is rather convoluted, so play it safe for now and only address
139 // the automatic update check that is known to cause issues during `make check`, not the
140 // manually triggered update check scenario):
141 if (!m_bShowDialog) {
142 std::scoped_lock l(m_mutex);
143 m_controller = aController;
145 aController->initialize( m_xParameters, m_xContext );
147 if ( m_bShowDialog )
148 aController->showDialog( true );
149 } catch (const uno::Exception &) {
150 // fdo#64962 - don't bring the app down on some unexpected exception.
151 TOOLS_WARN_EXCEPTION("extensions.update", "Caught init update exception, thread terminated" );
153 std::scoped_lock l(m_mutex);
154 m_controller.clear();
159 void InitUpdateCheckJobThread::setTerminating() {
160 m_bTerminating = true;
161 m_aCondition.set();
162 rtl::Reference<UpdateCheck> controller;
164 std::scoped_lock l(m_mutex);
165 std::swap(controller, m_controller);
167 if (controller.is()) {
168 controller->waitForUpdateCheckFinished();
172 UpdateCheckJob::~UpdateCheckJob()
176 uno::Any
177 UpdateCheckJob::execute(const uno::Sequence<beans::NamedValue>& namedValues)
179 for ( sal_Int32 n=namedValues.getLength(); n-- > 0; )
181 if ( namedValues[ n ].Name == "DynamicData" )
183 uno::Sequence<beans::NamedValue> aListProp;
184 if ( namedValues[n].Value >>= aListProp )
186 for ( sal_Int32 i=aListProp.getLength(); i-- > 0; )
188 if ( aListProp[ i ].Name == "updateList" )
190 handleExtensionUpdates( aListProp );
191 return uno::Any();
198 uno::Sequence<beans::NamedValue> aConfig =
199 getValue< uno::Sequence<beans::NamedValue> > (namedValues, "JobConfig");
201 /* Determine the way we got invoked here -
202 * see Developers Guide Chapter "4.7.2 Jobs" to understand the magic
205 uno::Sequence<beans::NamedValue> aEnvironment =
206 getValue< uno::Sequence<beans::NamedValue> > (namedValues, "Environment");
208 OUString aEventName = getValue< OUString > (aEnvironment, "EventName");
210 auto thread = std::make_unique<InitUpdateCheckJobThread >(
211 m_xContext, aConfig,
212 aEventName != "onFirstVisibleTask");
214 std::scoped_lock l(m_mutex);
215 m_pInitThread = std::move(thread);
218 return uno::Any();
222 void UpdateCheckJob::handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp )
224 try {
225 uno::Sequence< uno::Sequence< OUString > > aList =
226 getValue< uno::Sequence< uno::Sequence< OUString > > > ( rListProp, "updateList" );
227 bool bPrepareOnly = getValue< bool > ( rListProp, "prepareOnly" );
229 // we will first store any new found updates and then check, if there are any
230 // pending updates.
231 storeExtensionUpdateInfos( m_xContext, aList );
233 if ( bPrepareOnly )
234 return;
236 bool bHasUpdates = checkForPendingUpdates( m_xContext );
238 rtl::Reference<UpdateCheck> aController( UpdateCheck::get() );
239 if ( ! aController.is() )
240 return;
242 aController->setHasExtensionUpdates( bHasUpdates );
244 if ( ! aController->hasOfficeUpdate() )
246 if ( bHasUpdates )
247 aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL, true );
248 else
249 aController->setUIState( UPDATESTATE_NO_UPDATE_AVAIL, true );
252 catch( const uno::Exception& )
254 TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated");
259 OUString SAL_CALL
260 UpdateCheckJob::getImplementationName()
262 return "vnd.sun.UpdateCheck";
266 uno::Sequence< OUString > SAL_CALL
267 UpdateCheckJob::getSupportedServiceNames()
269 return { "com.sun.star.setup.UpdateCheck" };
272 sal_Bool SAL_CALL
273 UpdateCheckJob::supportsService( OUString const & serviceName )
275 return cppu::supportsService(this, serviceName);
279 // XEventListener
280 void SAL_CALL UpdateCheckJob::disposing( lang::EventObject const & rEvt )
282 css::uno::Reference<css::frame::XDesktop2> desktop;
284 std::scoped_lock l(m_mutex);
285 if ( rEvt.Source == m_xDesktop ) {
286 std::swap(m_xDesktop, desktop);
290 if ( desktop.is() )
292 terminateAndJoinThread();
293 desktop->removeTerminateListener( this );
298 // XTerminateListener
299 void SAL_CALL UpdateCheckJob::queryTermination( lang::EventObject const & )
303 void UpdateCheckJob::terminateAndJoinThread()
305 std::unique_ptr<InitUpdateCheckJobThread> thread;
307 std::scoped_lock l(m_mutex);
308 std::swap(m_pInitThread, thread);
310 if (thread != nullptr)
312 thread->setTerminating();
313 thread->join();
317 void SAL_CALL UpdateCheckJob::notifyTermination( lang::EventObject const & )
319 terminateAndJoinThread();
322 } // anonymous namespace
324 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
325 extensions_update_UpdateCheckJob_get_implementation(
326 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
328 css::uno::Reference<css::frame::XDesktop2> desktop(
329 css::frame::Desktop::create(context));
330 rtl::Reference<UpdateCheckJob> job(new UpdateCheckJob(context, desktop));
331 desktop->addTerminateListener(job);
332 return cppu::acquire(job.get());
336 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */