Avoid potential negative array index access to cached text.
[LibreOffice.git] / extensions / source / update / check / updatecheckconfig.cxx
blobe728d91e77f1af0a7f02c2a182cc010a28cd41be
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 "updatecheckconfig.hxx"
21 #include "updatecheck.hxx"
22 #include <com/sun/star/beans/PropertyValue.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/configuration/theDefaultProvider.hpp>
25 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
26 #include <cppuhelper/supportsservice.hxx>
27 #include <osl/security.hxx>
28 #include <osl/time.h>
29 #include <osl/file.hxx>
30 #include <sal/macros.h>
31 #include <o3tl/char16_t2wchar_t.hxx>
33 #ifdef _WIN32
34 #include <objbase.h>
35 #include <shlobj.h>
36 #endif
38 namespace container = com::sun::star::container ;
39 namespace beans = com::sun::star::beans ;
40 namespace lang = com::sun::star::lang ;
41 namespace util = com::sun::star::util ;
42 namespace uno = com::sun::star::uno ;
44 #define LAST_CHECK "LastCheck"
45 #define UPDATE_VERSION "UpdateVersion"
46 #define UPDATE_BUILDID "UpdateBuildId"
47 #define UPDATE_DESCRIPTION "UpdateDescription"
48 #define DOWNLOAD_URL "DownloadURL"
49 #define IS_DIRECT_DOWNLOAD "IsDirectDownload"
50 #define OLD_VERSION "UpdateFoundFor"
51 #define AUTOCHECK_ENABLED "AutoCheckEnabled"
52 #define AUTODOWNLOAD_ENABLED "AutoDownloadEnabled"
53 #define CHECK_INTERVAL "CheckInterval"
54 #define LOCAL_FILE "LocalFile"
55 #define DOWNLOAD_SIZE "DownloadSize"
56 #define DOWNLOAD_PAUSED "DownloadPaused"
57 #define DOWNLOAD_DESTINATION "DownloadDestination"
58 #define RELEASE_NOTE "ReleaseNote"
60 #define PROPERTY_VERSION "Version"
62 const char * const aUpdateEntryProperties[] = {
63 UPDATE_VERSION,
64 UPDATE_BUILDID,
65 UPDATE_DESCRIPTION,
66 DOWNLOAD_URL,
67 IS_DIRECT_DOWNLOAD,
68 RELEASE_NOTE"1",
69 RELEASE_NOTE"2",
70 RELEASE_NOTE"3",
71 RELEASE_NOTE"4",
72 RELEASE_NOTE"5",
73 OLD_VERSION
76 const sal_uInt32 nUpdateEntryProperties = SAL_N_ELEMENTS(aUpdateEntryProperties);
78 css::uno::Any NamedValueByNameAccess::getValue(const char * pName)
80 const sal_Int32 nLen = m_rValues.getLength();
81 for( sal_Int32 n=0; n < nLen; ++n )
83 if( m_rValues[n].Name.equalsAscii( pName ) )
84 return m_rValues[n].Value;
86 return css::uno::Any();
89 bool
90 UpdateCheckROModel::isAutoCheckEnabled() const
92 return m_aNameAccess.getValue(AUTOCHECK_ENABLED).get<bool>();
95 bool
96 UpdateCheckROModel::isDownloadPaused() const
98 return m_aNameAccess.getValue(DOWNLOAD_PAUSED).get<bool>();
101 OUString
102 UpdateCheckROModel::getStringValue(const char * pStr) const
104 uno::Any aAny( m_aNameAccess.getValue(pStr) );
105 OUString aRet;
107 aAny >>= aRet;
109 return aRet;
112 OUString UpdateCheckROModel::getLocalFileName() const
114 return getStringValue(LOCAL_FILE);
117 sal_Int64 UpdateCheckROModel::getDownloadSize() const
119 uno::Any aAny( m_aNameAccess.getValue(DOWNLOAD_SIZE) );
120 sal_Int64 nRet = -1;
122 aAny >>= nRet;
123 return nRet;
126 OUString
127 UpdateCheckROModel::getUpdateEntryVersion() const
129 return getStringValue(OLD_VERSION);
132 void
133 UpdateCheckROModel::getUpdateEntry(UpdateInfo& rInfo) const
135 rInfo.BuildId = getStringValue(UPDATE_BUILDID);
136 rInfo.Version = getStringValue(UPDATE_VERSION);
137 rInfo.Description = getStringValue(UPDATE_DESCRIPTION);
139 bool isDirectDownload = false;
140 m_aNameAccess.getValue(IS_DIRECT_DOWNLOAD) >>= isDirectDownload;
142 rInfo.Sources.push_back( DownloadSource( isDirectDownload, getStringValue(DOWNLOAD_URL) ) );
144 for(sal_Int32 n=1; n < 6; ++n )
146 OUString aUStr = getStringValue(
147 OString(OString::Concat(RELEASE_NOTE) + OString::number(n)).getStr());
148 if( !aUStr.isEmpty() )
149 rInfo.ReleaseNotes.push_back(ReleaseNote(static_cast<sal_Int8>(n), aUStr));
153 OUString UpdateCheckConfig::getDownloadsDirectory()
155 OUString aRet;
157 #ifdef _WIN32
158 PWSTR szPath;
160 if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, nullptr, &szPath) == S_OK)
162 aRet = o3tl::toU(szPath);
163 CoTaskMemFree(szPath);
164 osl::FileBase::getFileURLFromSystemPath( aRet, aRet );
166 #else
167 // This should become a desktop specific setting in some system backend ..
168 OUString aHomeDir;
169 osl::Security().getHomeDir( aHomeDir );
170 aRet = aHomeDir + "/Desktop";
172 // Set path to home directory when there is no /Desktop directory
173 osl::Directory aDocumentsDir( aRet );
174 if( osl::FileBase::E_None != aDocumentsDir.open() )
175 aRet = aHomeDir;
176 #endif
178 return aRet;
181 OUString UpdateCheckConfig::getAllUsersDirectory()
183 OUString aRet;
185 #ifdef _WIN32
186 WCHAR szPath[MAX_PATH];
188 if (TRUE == SHGetSpecialFolderPathW(nullptr, szPath, CSIDL_COMMON_DOCUMENTS, true))
190 aRet = o3tl::toU(szPath);
191 osl::FileBase::getFileURLFromSystemPath( aRet, aRet );
193 #else
194 osl::FileBase::getTempDirURL(aRet);
195 #endif
197 return aRet;
200 UpdateCheckConfig::UpdateCheckConfig( const uno::Reference<container::XNameContainer>& xContainer,
201 const uno::Reference<container::XNameContainer>& xAvailableUpdates,
202 const uno::Reference<container::XNameContainer>& xIgnoredUpdates,
203 const ::rtl::Reference< UpdateCheckConfigListener >& rListener ) :
204 m_xContainer( xContainer ),
205 m_xAvailableUpdates( xAvailableUpdates ),
206 m_xIgnoredUpdates( xIgnoredUpdates ),
207 m_rListener( rListener )
210 UpdateCheckConfig::~UpdateCheckConfig()
213 ::rtl::Reference< UpdateCheckConfig >
214 UpdateCheckConfig::get(
215 const uno::Reference<uno::XComponentContext>& xContext,
216 const ::rtl::Reference< UpdateCheckConfigListener >& rListener)
218 uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
219 css::configuration::theDefaultProvider::get( xContext ) );
221 beans::PropertyValue aProperty;
222 aProperty.Name = "nodepath";
223 aProperty.Value <<= OUString("org.openoffice.Office.Jobs/Jobs/UpdateCheck/Arguments");
225 uno::Sequence< uno::Any > aArgumentList{ uno::Any(aProperty) };
227 uno::Reference< container::XNameContainer > xContainer(
228 xConfigProvider->createInstanceWithArguments(
229 "com.sun.star.configuration.ConfigurationUpdateAccess", aArgumentList ),
230 uno::UNO_QUERY_THROW );
232 aProperty.Value <<= OUString("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates");
233 aArgumentList = { uno::Any(aProperty) };
234 uno::Reference< container::XNameContainer > xIgnoredExt( xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationUpdateAccess", aArgumentList ), uno::UNO_QUERY_THROW );
236 aProperty.Value <<= OUString("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/AvailableUpdates");
237 aArgumentList = { uno::Any(aProperty) };
238 uno::Reference< container::XNameContainer > xUpdateAvail( xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationUpdateAccess", aArgumentList ), uno::UNO_QUERY_THROW );
240 return new UpdateCheckConfig( xContainer, xUpdateAvail, xIgnoredExt, rListener );
243 bool
244 UpdateCheckConfig::isAutoCheckEnabled() const
246 bool nValue = false;
247 const_cast < UpdateCheckConfig *> (this)->getByName( AUTOCHECK_ENABLED ) >>= nValue;
248 return nValue;
251 bool
252 UpdateCheckConfig::isAutoDownloadEnabled() const
254 bool nValue = false;
255 const_cast < UpdateCheckConfig *> (this)->getByName( AUTODOWNLOAD_ENABLED ) >>= nValue;
256 return nValue;
259 OUString
260 UpdateCheckConfig::getUpdateEntryVersion() const
262 OUString aValue;
264 // getByName is defined as non const in XNameAccess
265 const_cast < UpdateCheckConfig *> (this)->getByName( OLD_VERSION ) >>= aValue;
267 return aValue;
270 sal_Int64
271 UpdateCheckConfig::getLastChecked() const
273 sal_Int64 nValue = 0;
275 // getByName is defined as non const in XNameAccess
276 const_cast < UpdateCheckConfig *> (this)->getByName( LAST_CHECK ) >>= nValue;
278 return nValue;
281 sal_Int64
282 UpdateCheckConfig::getCheckInterval() const
284 sal_Int64 nValue = 0;
286 // getByName is defined as non const in XNameAccess
287 const_cast < UpdateCheckConfig *> (this)->getByName( CHECK_INTERVAL ) >>= nValue;
289 return nValue;
292 OUString
293 UpdateCheckConfig::getLocalFileName() const
295 OUString aName = LOCAL_FILE;
296 OUString aRet;
298 if( m_xContainer->hasByName(aName) )
299 m_xContainer->getByName(aName) >>= aRet;
301 return aRet;
304 OUString
305 UpdateCheckConfig::getDownloadDestination() const
307 OUString aRet;
309 const_cast <UpdateCheckConfig *> (this)->getByName(DOWNLOAD_DESTINATION) >>= aRet;
311 return aRet;
314 void
315 UpdateCheckConfig::storeLocalFileName(const OUString& rLocalFileName, sal_Int64 nFileSize)
317 const sal_uInt8 nItems = 2;
318 const OUString aNameList[nItems] = { OUString(LOCAL_FILE), OUString(DOWNLOAD_SIZE) };
319 const uno::Any aValueList[nItems] = { uno::Any(rLocalFileName), uno::Any(nFileSize) };
321 for( sal_uInt8 i=0; i < nItems; ++i )
323 if( m_xContainer->hasByName(aNameList[i]) )
324 m_xContainer->replaceByName(aNameList[i], aValueList[i]);
325 else
326 m_xContainer->insertByName(aNameList[i], aValueList[i]);
329 commitChanges();
332 void
333 UpdateCheckConfig::clearLocalFileName()
335 const sal_uInt8 nItems = 2;
336 const OUString aNameList[nItems] = { OUString(LOCAL_FILE), OUString(DOWNLOAD_SIZE) };
338 for(const auto & i : aNameList)
340 if( m_xContainer->hasByName(i) )
341 m_xContainer->removeByName(i);
344 commitChanges();
347 void
348 UpdateCheckConfig::storeDownloadPaused(bool paused)
350 replaceByName(DOWNLOAD_PAUSED , uno::Any(paused));
351 commitChanges();
354 void
355 UpdateCheckConfig::updateLastChecked()
357 TimeValue systime;
358 osl_getSystemTime(&systime);
360 sal_Int64 lastCheck = systime.Seconds;
362 replaceByName(LAST_CHECK, uno::Any(lastCheck));
365 void
366 UpdateCheckConfig::storeUpdateFound( const UpdateInfo& rInfo, const OUString& aCurrentBuild)
369 bool autoDownloadEnabled = isAutoDownloadEnabled();
371 uno::Any aValues[nUpdateEntryProperties] =
373 uno::Any(rInfo.Version),
374 uno::Any(rInfo.BuildId),
375 uno::Any(rInfo.Description),
376 uno::Any(rInfo.Sources[0].URL),
377 uno::Any(rInfo.Sources[0].IsDirect),
378 uno::Any(getReleaseNote(rInfo, 1, autoDownloadEnabled) ),
379 uno::Any(getReleaseNote(rInfo, 2, autoDownloadEnabled) ),
380 uno::Any(getReleaseNote(rInfo, 3, autoDownloadEnabled) ),
381 uno::Any(getReleaseNote(rInfo, 4, autoDownloadEnabled) ),
382 uno::Any(getReleaseNote(rInfo, 5, autoDownloadEnabled) ),
383 uno::Any(aCurrentBuild)
386 OUString aName;
387 for( sal_uInt32 n=0; n < nUpdateEntryProperties; ++n )
389 aName = OUString::createFromAscii(aUpdateEntryProperties[n]);
391 if( m_xContainer->hasByName(aName) )
392 m_xContainer->replaceByName(aName, aValues[n]);
393 else
394 m_xContainer->insertByName(aName,aValues[n]);
397 commitChanges();
400 void
401 UpdateCheckConfig::clearUpdateFound()
403 OUString aName;
405 for(const char* aUpdateEntryProperty : aUpdateEntryProperties)
407 aName = OUString::createFromAscii(aUpdateEntryProperty);
409 try {
410 if( m_xContainer->hasByName(aName) )
411 m_xContainer->removeByName(aName);
412 } catch(const lang::WrappedTargetException& ) {
413 // Can not remove value, probably in share layer
414 OSL_ASSERT(false);
415 m_xContainer->replaceByName(aName, uno::Any(OUString()));
419 /* As we have removed UpdateVersionFound from the shared configuration
420 * existing entries in the user layer do not have a oor operation and
421 * thus are completely ignored (which also means they can not be removed).
424 commitChanges();
427 uno::Type SAL_CALL
428 UpdateCheckConfig::getElementType()
430 return m_xContainer->getElementType();
433 sal_Bool SAL_CALL
434 UpdateCheckConfig::hasElements()
436 return m_xContainer->hasElements();
439 uno::Any SAL_CALL
440 UpdateCheckConfig::getByName( const OUString& aName )
442 uno::Any aValue = m_xContainer->getByName( aName );
444 // Provide dynamic default value
445 if( aName == DOWNLOAD_DESTINATION )
447 OUString aStr;
448 aValue >>= aStr;
450 if( aStr.isEmpty() )
451 aValue <<= getDownloadsDirectory();
453 return aValue;
456 uno::Sequence< OUString > SAL_CALL
457 UpdateCheckConfig::getElementNames()
459 return m_xContainer->getElementNames();
462 sal_Bool SAL_CALL
463 UpdateCheckConfig::hasByName( const OUString& aName )
465 return m_xContainer->hasByName( aName );
468 void SAL_CALL
469 UpdateCheckConfig::replaceByName( const OUString& aName, const uno::Any& aElement )
471 return m_xContainer->replaceByName( aName, aElement );
474 // XChangesBatch
476 void SAL_CALL
477 UpdateCheckConfig::commitChanges()
479 uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
480 if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
482 util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges();
483 xChangesBatch->commitChanges();
485 if( m_rListener.is() )
487 const sal_Int32 nChanges = aChangesSet.getLength();
488 OUString aString;
490 for( sal_Int32 i=0; i<nChanges; ++i )
492 aChangesSet[i].Accessor >>= aString;
493 if( aString.endsWith(AUTOCHECK_ENABLED "']") )
495 bool bEnabled = false;
496 aChangesSet[i].Element >>= bEnabled;
497 m_rListener->autoCheckStatusChanged(bEnabled);
499 else if( aString.endsWith(CHECK_INTERVAL "']") )
501 m_rListener->autoCheckIntervalChanged();
507 xChangesBatch.set( m_xAvailableUpdates, uno::UNO_QUERY );
508 if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
510 xChangesBatch->commitChanges();
512 xChangesBatch.set( m_xIgnoredUpdates, uno::UNO_QUERY );
513 if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
515 xChangesBatch->commitChanges();
519 sal_Bool SAL_CALL
520 UpdateCheckConfig::hasPendingChanges( )
522 uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
523 if( xChangesBatch.is() )
524 return xChangesBatch->hasPendingChanges();
526 return false;
529 uno::Sequence< util::ElementChange > SAL_CALL
530 UpdateCheckConfig::getPendingChanges( )
532 uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
533 if( xChangesBatch.is() )
534 return xChangesBatch->getPendingChanges();
536 return uno::Sequence< util::ElementChange >();
539 bool UpdateCheckConfig::storeExtensionVersion( const OUString& rExtensionName,
540 const OUString& rVersion )
542 bool bNotify = true;
544 if ( m_xAvailableUpdates->hasByName( rExtensionName ) )
545 uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) );
546 else
548 uno::Reference< beans::XPropertySet > elem( uno::Reference< lang::XSingleServiceFactory >( m_xAvailableUpdates, uno::UNO_QUERY_THROW )->createInstance(), uno::UNO_QUERY_THROW );
549 elem->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) );
550 m_xAvailableUpdates->insertByName( rExtensionName, uno::Any( elem ) );
553 if ( m_xIgnoredUpdates->hasByName( rExtensionName ) )
555 OUString aIgnoredVersion;
556 uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
557 aValue >>= aIgnoredVersion;
558 if ( aIgnoredVersion.isEmpty() ) // no version means ignore all updates
559 bNotify = false;
560 else if ( aIgnoredVersion == rVersion ) // the user wanted to ignore this update
561 bNotify = false;
564 commitChanges();
566 return bNotify;
569 bool UpdateCheckConfig::checkExtensionVersion( const OUString& rExtensionName,
570 const OUString& rVersion )
572 if ( m_xAvailableUpdates->hasByName( rExtensionName ) )
574 OUString aStoredVersion;
575 uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
576 aValue >>= aStoredVersion;
578 if ( m_xIgnoredUpdates->hasByName( rExtensionName ) )
580 OUString aIgnoredVersion;
581 uno::Any aValue2( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
582 aValue2 >>= aIgnoredVersion;
583 if ( aIgnoredVersion.isEmpty() ) // no version means ignore all updates
584 return false;
585 else if ( aIgnoredVersion == aStoredVersion ) // the user wanted to ignore this update
586 return false;
587 // TODO: else delete ignored entry?
589 if ( isVersionGreater( rVersion, aStoredVersion ) )
590 return true;
591 else
593 m_xAvailableUpdates->removeByName( rExtensionName );
594 commitChanges();
598 return false;
601 OUString UpdateCheckConfig::getSubVersion( const OUString& rVersion,
602 sal_Int32 *nIndex )
604 while ( *nIndex < rVersion.getLength() && rVersion[*nIndex] == '0')
606 ++*nIndex;
609 return rVersion.getToken( 0, '.', *nIndex );
612 /// checks if the second version string is greater than the first one
613 bool UpdateCheckConfig::isVersionGreater( const OUString& rVersion1,
614 const OUString& rVersion2 )
616 for ( sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0; )
618 OUString sSub1( getSubVersion( rVersion1, &i1 ) );
619 OUString sSub2( getSubVersion( rVersion2, &i2 ) );
621 if ( sSub1.getLength() < sSub2.getLength() ) {
622 return true;
623 } else if ( sSub1.getLength() > sSub2.getLength() ) {
624 return false;
625 } else if ( sSub1 < sSub2 ) {
626 return true;
627 } else if ( sSub1 > sSub2 ) {
628 return false;
631 return false;
634 OUString SAL_CALL
635 UpdateCheckConfig::getImplementationName()
637 return "vnd.sun.UpdateCheckConfig";
640 sal_Bool SAL_CALL
641 UpdateCheckConfig::supportsService(OUString const & serviceName)
643 return cppu::supportsService(this, serviceName);
646 uno::Sequence< OUString > SAL_CALL
647 UpdateCheckConfig::getSupportedServiceNames()
649 return { "com.sun.star.setup.UpdateCheckConfig" };
652 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
653 extensions_update_UpdateCheckConfig_get_implementation(
654 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
656 return cppu::acquire(UpdateCheckConfig::get(context, *UpdateCheck::get()).get());
660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */