1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
29 #include <osl/diagnose.h>
30 #include <osl/file.hxx>
31 #include <sal/macros.h>
32 #include <o3tl/char16_t2wchar_t.hxx>
39 namespace container
= css::container
;
40 namespace beans
= css::beans
;
41 namespace lang
= css::lang
;
42 namespace util
= css::util
;
43 namespace uno
= css::uno
;
45 #define LAST_CHECK "LastCheck"
46 #define UPDATE_VERSION "UpdateVersion"
47 #define UPDATE_BUILDID "UpdateBuildId"
48 #define UPDATE_DESCRIPTION "UpdateDescription"
49 #define DOWNLOAD_URL "DownloadURL"
50 #define IS_DIRECT_DOWNLOAD "IsDirectDownload"
51 #define OLD_VERSION "UpdateFoundFor"
52 #define AUTOCHECK_ENABLED "AutoCheckEnabled"
53 #define AUTODOWNLOAD_ENABLED "AutoDownloadEnabled"
54 #define CHECK_INTERVAL "CheckInterval"
55 #define LOCAL_FILE "LocalFile"
56 #define DOWNLOAD_SIZE "DownloadSize"
57 #define DOWNLOAD_PAUSED "DownloadPaused"
58 #define DOWNLOAD_DESTINATION "DownloadDestination"
59 #define RELEASE_NOTE "ReleaseNote"
61 #define PROPERTY_VERSION "Version"
63 const char * const aUpdateEntryProperties
[] = {
77 const sal_uInt32 nUpdateEntryProperties
= SAL_N_ELEMENTS(aUpdateEntryProperties
);
79 css::uno::Any
NamedValueByNameAccess::getValue(const char * pName
)
81 const sal_Int32 nLen
= m_rValues
.getLength();
82 for( sal_Int32 n
=0; n
< nLen
; ++n
)
84 if( m_rValues
[n
].Name
.equalsAscii( pName
) )
85 return m_rValues
[n
].Value
;
87 return css::uno::Any();
91 UpdateCheckROModel::isAutoCheckEnabled() const
93 return m_aNameAccess
.getValue(AUTOCHECK_ENABLED
).get
<bool>();
97 UpdateCheckROModel::isDownloadPaused() const
99 return m_aNameAccess
.getValue(DOWNLOAD_PAUSED
).get
<bool>();
103 UpdateCheckROModel::getStringValue(const char * pStr
) const
105 uno::Any
aAny( m_aNameAccess
.getValue(pStr
) );
113 OUString
UpdateCheckROModel::getLocalFileName() const
115 return getStringValue(LOCAL_FILE
);
118 sal_Int64
UpdateCheckROModel::getDownloadSize() const
120 uno::Any
aAny( m_aNameAccess
.getValue(DOWNLOAD_SIZE
) );
128 UpdateCheckROModel::getUpdateEntryVersion() const
130 return getStringValue(OLD_VERSION
);
134 UpdateCheckROModel::getUpdateEntry(UpdateInfo
& rInfo
) const
136 rInfo
.BuildId
= getStringValue(UPDATE_BUILDID
);
137 rInfo
.Version
= getStringValue(UPDATE_VERSION
);
138 rInfo
.Description
= getStringValue(UPDATE_DESCRIPTION
);
140 bool isDirectDownload
= false;
141 m_aNameAccess
.getValue(IS_DIRECT_DOWNLOAD
) >>= isDirectDownload
;
143 rInfo
.Sources
.emplace_back(isDirectDownload
, getStringValue(DOWNLOAD_URL
));
145 for(sal_Int32 n
=1; n
< 6; ++n
)
147 OUString aUStr
= getStringValue(
148 OString(OString::Concat(RELEASE_NOTE
) + OString::number(n
)).getStr());
149 if( !aUStr
.isEmpty() )
150 rInfo
.ReleaseNotes
.emplace_back(static_cast<sal_Int8
>(n
), aUStr
);
154 OUString
UpdateCheckConfig::getDownloadsDirectory()
161 if (SHGetKnownFolderPath(FOLDERID_Downloads
, 0, nullptr, &szPath
) == S_OK
)
163 aRet
= o3tl::toU(szPath
);
164 CoTaskMemFree(szPath
);
165 osl::FileBase::getFileURLFromSystemPath( aRet
, aRet
);
168 // This should become a desktop specific setting in some system backend ..
170 osl::Security().getHomeDir( aHomeDir
);
171 aRet
= aHomeDir
+ "/Desktop";
173 // Set path to home directory when there is no /Desktop directory
174 osl::Directory
aDocumentsDir( aRet
);
175 if( osl::FileBase::E_None
!= aDocumentsDir
.open() )
182 OUString
UpdateCheckConfig::getAllUsersDirectory()
187 PWSTR szPath
= nullptr;
188 if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_PublicDocuments
, KF_FLAG_CREATE
, nullptr, &szPath
)))
190 aRet
= o3tl::toU(szPath
);
191 osl::FileBase::getFileURLFromSystemPath( aRet
, aRet
);
193 CoTaskMemFree(szPath
);
195 osl::FileBase::getTempDirURL(aRet
);
201 UpdateCheckConfig::UpdateCheckConfig( const uno::Reference
<container::XNameContainer
>& xContainer
,
202 const uno::Reference
<container::XNameContainer
>& xAvailableUpdates
,
203 const uno::Reference
<container::XNameContainer
>& xIgnoredUpdates
,
204 const ::rtl::Reference
< UpdateCheckConfigListener
>& rListener
) :
205 m_xContainer( xContainer
),
206 m_xAvailableUpdates( xAvailableUpdates
),
207 m_xIgnoredUpdates( xIgnoredUpdates
),
208 m_rListener( rListener
)
211 UpdateCheckConfig::~UpdateCheckConfig()
214 ::rtl::Reference
< UpdateCheckConfig
>
215 UpdateCheckConfig::get(
216 const uno::Reference
<uno::XComponentContext
>& xContext
,
217 const ::rtl::Reference
< UpdateCheckConfigListener
>& rListener
)
219 uno::Reference
< lang::XMultiServiceFactory
> xConfigProvider(
220 css::configuration::theDefaultProvider::get( xContext
) );
222 beans::PropertyValue aProperty
;
223 aProperty
.Name
= "nodepath";
224 aProperty
.Value
<<= u
"org.openoffice.Office.Jobs/Jobs/UpdateCheck/Arguments"_ustr
;
226 uno::Sequence
< uno::Any
> aArgumentList
{ uno::Any(aProperty
) };
228 uno::Reference
< container::XNameContainer
> xContainer(
229 xConfigProvider
->createInstanceWithArguments(
230 u
"com.sun.star.configuration.ConfigurationUpdateAccess"_ustr
, aArgumentList
),
231 uno::UNO_QUERY_THROW
);
233 aProperty
.Value
<<= u
"/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates"_ustr
;
234 aArgumentList
= { uno::Any(aProperty
) };
235 uno::Reference
< container::XNameContainer
> xIgnoredExt( xConfigProvider
->createInstanceWithArguments( u
"com.sun.star.configuration.ConfigurationUpdateAccess"_ustr
, aArgumentList
), uno::UNO_QUERY_THROW
);
237 aProperty
.Value
<<= u
"/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/AvailableUpdates"_ustr
;
238 aArgumentList
= { uno::Any(aProperty
) };
239 uno::Reference
< container::XNameContainer
> xUpdateAvail( xConfigProvider
->createInstanceWithArguments( u
"com.sun.star.configuration.ConfigurationUpdateAccess"_ustr
, aArgumentList
), uno::UNO_QUERY_THROW
);
241 return new UpdateCheckConfig( xContainer
, xUpdateAvail
, xIgnoredExt
, rListener
);
245 UpdateCheckConfig::isAutoCheckEnabled() const
248 const_cast < UpdateCheckConfig
*> (this)->getByName( u
"" AUTOCHECK_ENABLED
""_ustr
) >>= nValue
;
253 UpdateCheckConfig::isAutoDownloadEnabled() const
256 const_cast < UpdateCheckConfig
*> (this)->getByName( u
"" AUTODOWNLOAD_ENABLED
""_ustr
) >>= nValue
;
261 UpdateCheckConfig::getUpdateEntryVersion() const
265 // getByName is defined as non const in XNameAccess
266 const_cast < UpdateCheckConfig
*> (this)->getByName( u
"" OLD_VERSION
""_ustr
) >>= aValue
;
272 UpdateCheckConfig::getLastChecked() const
274 sal_Int64 nValue
= 0;
276 // getByName is defined as non const in XNameAccess
277 const_cast < UpdateCheckConfig
*> (this)->getByName( u
"" LAST_CHECK
""_ustr
) >>= nValue
;
283 UpdateCheckConfig::getCheckInterval() const
285 sal_Int64 nValue
= 0;
287 // getByName is defined as non const in XNameAccess
288 const_cast < UpdateCheckConfig
*> (this)->getByName( u
"" CHECK_INTERVAL
""_ustr
) >>= nValue
;
294 UpdateCheckConfig::getLocalFileName() const
296 OUString aName
= u
"" LOCAL_FILE
""_ustr
;
299 if( m_xContainer
->hasByName(aName
) )
300 m_xContainer
->getByName(aName
) >>= aRet
;
306 UpdateCheckConfig::getDownloadDestination() const
310 const_cast <UpdateCheckConfig
*> (this)->getByName(u
"" DOWNLOAD_DESTINATION
""_ustr
) >>= aRet
;
316 UpdateCheckConfig::storeLocalFileName(const OUString
& rLocalFileName
, sal_Int64 nFileSize
)
318 const sal_uInt8 nItems
= 2;
319 const OUString aNameList
[nItems
] = { u
"" LOCAL_FILE
""_ustr
, u
"" DOWNLOAD_SIZE
""_ustr
};
320 const uno::Any aValueList
[nItems
] = { uno::Any(rLocalFileName
), uno::Any(nFileSize
) };
322 for( sal_uInt8 i
=0; i
< nItems
; ++i
)
324 if( m_xContainer
->hasByName(aNameList
[i
]) )
325 m_xContainer
->replaceByName(aNameList
[i
], aValueList
[i
]);
327 m_xContainer
->insertByName(aNameList
[i
], aValueList
[i
]);
334 UpdateCheckConfig::clearLocalFileName()
336 const sal_uInt8 nItems
= 2;
337 const OUString aNameList
[nItems
] = { u
"" LOCAL_FILE
""_ustr
, u
"" DOWNLOAD_SIZE
""_ustr
};
339 for(const auto & i
: aNameList
)
341 if( m_xContainer
->hasByName(i
) )
342 m_xContainer
->removeByName(i
);
349 UpdateCheckConfig::storeDownloadPaused(bool paused
)
351 replaceByName(u
"" DOWNLOAD_PAUSED
""_ustr
, uno::Any(paused
));
356 UpdateCheckConfig::updateLastChecked()
359 osl_getSystemTime(&systime
);
361 sal_Int64 lastCheck
= systime
.Seconds
;
363 replaceByName(u
"" LAST_CHECK
""_ustr
, uno::Any(lastCheck
));
367 UpdateCheckConfig::storeUpdateFound( const UpdateInfo
& rInfo
, const OUString
& aCurrentBuild
)
370 bool autoDownloadEnabled
= isAutoDownloadEnabled();
372 uno::Any aValues
[nUpdateEntryProperties
] =
374 uno::Any(rInfo
.Version
),
375 uno::Any(rInfo
.BuildId
),
376 uno::Any(rInfo
.Description
),
377 uno::Any(rInfo
.Sources
[0].URL
),
378 uno::Any(rInfo
.Sources
[0].IsDirect
),
379 uno::Any(getReleaseNote(rInfo
, 1, autoDownloadEnabled
) ),
380 uno::Any(getReleaseNote(rInfo
, 2, autoDownloadEnabled
) ),
381 uno::Any(getReleaseNote(rInfo
, 3, autoDownloadEnabled
) ),
382 uno::Any(getReleaseNote(rInfo
, 4, autoDownloadEnabled
) ),
383 uno::Any(getReleaseNote(rInfo
, 5, autoDownloadEnabled
) ),
384 uno::Any(aCurrentBuild
)
388 for( sal_uInt32 n
=0; n
< nUpdateEntryProperties
; ++n
)
390 aName
= OUString::createFromAscii(aUpdateEntryProperties
[n
]);
392 if( m_xContainer
->hasByName(aName
) )
393 m_xContainer
->replaceByName(aName
, aValues
[n
]);
395 m_xContainer
->insertByName(aName
,aValues
[n
]);
402 UpdateCheckConfig::clearUpdateFound()
406 for(const char* aUpdateEntryProperty
: aUpdateEntryProperties
)
408 aName
= OUString::createFromAscii(aUpdateEntryProperty
);
411 if( m_xContainer
->hasByName(aName
) )
412 m_xContainer
->removeByName(aName
);
413 } catch(const lang::WrappedTargetException
& ) {
414 // Can not remove value, probably in share layer
416 m_xContainer
->replaceByName(aName
, uno::Any(OUString()));
420 /* As we have removed UpdateVersionFound from the shared configuration
421 * existing entries in the user layer do not have a oor operation and
422 * thus are completely ignored (which also means they can not be removed).
429 UpdateCheckConfig::getElementType()
431 return m_xContainer
->getElementType();
435 UpdateCheckConfig::hasElements()
437 return m_xContainer
->hasElements();
441 UpdateCheckConfig::getByName( const OUString
& aName
)
443 uno::Any aValue
= m_xContainer
->getByName( aName
);
445 // Provide dynamic default value
446 if( aName
== DOWNLOAD_DESTINATION
)
452 aValue
<<= getDownloadsDirectory();
457 uno::Sequence
< OUString
> SAL_CALL
458 UpdateCheckConfig::getElementNames()
460 return m_xContainer
->getElementNames();
464 UpdateCheckConfig::hasByName( const OUString
& aName
)
466 return m_xContainer
->hasByName( aName
);
470 UpdateCheckConfig::replaceByName( const OUString
& aName
, const uno::Any
& aElement
)
472 return m_xContainer
->replaceByName( aName
, aElement
);
478 UpdateCheckConfig::commitChanges()
480 uno::Reference
< util::XChangesBatch
> xChangesBatch(m_xContainer
, uno::UNO_QUERY
);
481 if( xChangesBatch
.is() && xChangesBatch
->hasPendingChanges() )
483 util::ChangesSet aChangesSet
= xChangesBatch
->getPendingChanges();
484 xChangesBatch
->commitChanges();
486 if( m_rListener
.is() )
488 const sal_Int32 nChanges
= aChangesSet
.getLength();
491 for( sal_Int32 i
=0; i
<nChanges
; ++i
)
493 aChangesSet
[i
].Accessor
>>= aString
;
494 if( aString
.endsWith(AUTOCHECK_ENABLED
"']") )
496 bool bEnabled
= false;
497 aChangesSet
[i
].Element
>>= bEnabled
;
498 m_rListener
->autoCheckStatusChanged(bEnabled
);
500 else if( aString
.endsWith(CHECK_INTERVAL
"']") )
502 m_rListener
->autoCheckIntervalChanged();
508 xChangesBatch
.set( m_xAvailableUpdates
, uno::UNO_QUERY
);
509 if( xChangesBatch
.is() && xChangesBatch
->hasPendingChanges() )
511 xChangesBatch
->commitChanges();
513 xChangesBatch
.set( m_xIgnoredUpdates
, uno::UNO_QUERY
);
514 if( xChangesBatch
.is() && xChangesBatch
->hasPendingChanges() )
516 xChangesBatch
->commitChanges();
521 UpdateCheckConfig::hasPendingChanges( )
523 uno::Reference
< util::XChangesBatch
> xChangesBatch(m_xContainer
, uno::UNO_QUERY
);
524 if( xChangesBatch
.is() )
525 return xChangesBatch
->hasPendingChanges();
530 uno::Sequence
< util::ElementChange
> SAL_CALL
531 UpdateCheckConfig::getPendingChanges( )
533 uno::Reference
< util::XChangesBatch
> xChangesBatch(m_xContainer
, uno::UNO_QUERY
);
534 if( xChangesBatch
.is() )
535 return xChangesBatch
->getPendingChanges();
537 return uno::Sequence
< util::ElementChange
>();
540 bool UpdateCheckConfig::storeExtensionVersion( const OUString
& rExtensionName
,
541 const OUString
& rVersion
)
545 if ( m_xAvailableUpdates
->hasByName( rExtensionName
) )
546 uno::Reference
< beans::XPropertySet
>( m_xAvailableUpdates
->getByName( rExtensionName
), uno::UNO_QUERY_THROW
)->setPropertyValue( u
"" PROPERTY_VERSION
""_ustr
, uno::Any( rVersion
) );
549 uno::Reference
< beans::XPropertySet
> elem( uno::Reference
< lang::XSingleServiceFactory
>( m_xAvailableUpdates
, uno::UNO_QUERY_THROW
)->createInstance(), uno::UNO_QUERY_THROW
);
550 elem
->setPropertyValue( u
"" PROPERTY_VERSION
""_ustr
, uno::Any( rVersion
) );
551 m_xAvailableUpdates
->insertByName( rExtensionName
, uno::Any( elem
) );
554 if ( m_xIgnoredUpdates
->hasByName( rExtensionName
) )
556 OUString aIgnoredVersion
;
557 uno::Any
aValue( uno::Reference
< beans::XPropertySet
>( m_xIgnoredUpdates
->getByName( rExtensionName
), uno::UNO_QUERY_THROW
)->getPropertyValue( u
"" PROPERTY_VERSION
""_ustr
) );
558 aValue
>>= aIgnoredVersion
;
559 if ( aIgnoredVersion
.isEmpty() ) // no version means ignore all updates
561 else if ( aIgnoredVersion
== rVersion
) // the user wanted to ignore this update
570 bool UpdateCheckConfig::checkExtensionVersion( const OUString
& rExtensionName
,
571 const OUString
& rVersion
)
573 if ( m_xAvailableUpdates
->hasByName( rExtensionName
) )
575 OUString aStoredVersion
;
576 uno::Any
aValue( uno::Reference
< beans::XPropertySet
>( m_xAvailableUpdates
->getByName( rExtensionName
), uno::UNO_QUERY_THROW
)->getPropertyValue( u
"" PROPERTY_VERSION
""_ustr
) );
577 aValue
>>= aStoredVersion
;
579 if ( m_xIgnoredUpdates
->hasByName( rExtensionName
) )
581 OUString aIgnoredVersion
;
582 uno::Any
aValue2( uno::Reference
< beans::XPropertySet
>( m_xIgnoredUpdates
->getByName( rExtensionName
), uno::UNO_QUERY_THROW
)->getPropertyValue( u
"" PROPERTY_VERSION
""_ustr
) );
583 aValue2
>>= aIgnoredVersion
;
584 if ( aIgnoredVersion
.isEmpty() ) // no version means ignore all updates
586 else if ( aIgnoredVersion
== aStoredVersion
) // the user wanted to ignore this update
588 // TODO: else delete ignored entry?
590 if ( isVersionGreater( rVersion
, aStoredVersion
) )
594 m_xAvailableUpdates
->removeByName( rExtensionName
);
602 OUString
UpdateCheckConfig::getSubVersion( const OUString
& rVersion
,
605 while ( *nIndex
< rVersion
.getLength() && rVersion
[*nIndex
] == '0')
610 return rVersion
.getToken( 0, '.', *nIndex
);
613 /// checks if the second version string is greater than the first one
614 bool UpdateCheckConfig::isVersionGreater( const OUString
& rVersion1
,
615 const OUString
& rVersion2
)
617 for ( sal_Int32 i1
= 0, i2
= 0; i1
>= 0 || i2
>= 0; )
619 OUString
sSub1( getSubVersion( rVersion1
, &i1
) );
620 OUString
sSub2( getSubVersion( rVersion2
, &i2
) );
622 if ( sSub1
.getLength() < sSub2
.getLength() ) {
624 } else if ( sSub1
.getLength() > sSub2
.getLength() ) {
626 } else if ( sSub1
< sSub2
) {
628 } else if ( sSub1
> sSub2
) {
636 UpdateCheckConfig::getImplementationName()
638 return u
"vnd.sun.UpdateCheckConfig"_ustr
;
642 UpdateCheckConfig::supportsService(OUString
const & serviceName
)
644 return cppu::supportsService(this, serviceName
);
647 uno::Sequence
< OUString
> SAL_CALL
648 UpdateCheckConfig::getSupportedServiceNames()
650 return { u
"com.sun.star.setup.UpdateCheckConfig"_ustr
};
653 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
654 extensions_update_UpdateCheckConfig_get_implementation(
655 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
657 return cppu::acquire(UpdateCheckConfig::get(context
, *UpdateCheck::get()).get());
661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */