1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_extensions.hxx"
30 
31 
32 #include "updatecheckconfig.hxx"
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 #include <com/sun/star/beans/XPropertyState.hpp>
35 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
37 
38 #include <osl/security.hxx>
39 #include <osl/time.h>
40 #include <osl/file.hxx>
41 
42 #ifdef WNT
43 #ifdef _MSC_VER
44 #pragma warning(push,1) // disable warnings within system headers
45 #pragma warning(disable: 4917)
46 #endif
47 #include <shlobj.h>
48 #ifdef _MSC_VER
49 #pragma warning(pop)
50 #endif
51 #endif
52 
53 namespace container = com::sun::star::container ;
54 namespace beans = com::sun::star::beans ;
55 namespace lang = com::sun::star::lang ;
56 namespace util = com::sun::star::util ;
57 namespace uno = com::sun::star::uno ;
58 
59 #define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s))
60 
61 #define LAST_CHECK              "LastCheck"
62 #define VERSION_FOUND           "UpdateVersionFound"
63 #define UPDATE_VERSION          "UpdateVersion"
64 #define UPDATE_BUILDID          "UpdateBuildId"
65 #define UPDATE_DESCRIPTION      "UpdateDescription"
66 #define DOWNLOAD_URL            "DownloadURL"
67 #define IS_DIRECT_DOWNLOAD      "IsDirectDownload"
68 #define OLD_VERSION             "UpdateFoundFor"
69 #define AUTOCHECK_ENABLED       "AutoCheckEnabled"
70 #define AUTODOWNLOAD_ENABLED    "AutoDownloadEnabled"
71 #define CHECK_INTERVAL          "CheckInterval"
72 #define LOCAL_FILE              "LocalFile"
73 #define DOWNLOAD_SIZE           "DownloadSize"
74 #define DOWNLOAD_PAUSED         "DownloadPaused"
75 #define DOWNLOAD_DESTINATION    "DownloadDestination"
76 #define RELEASE_NOTE            "ReleaseNote"
77 #define EXTENSION_PREFIX        "Extension_"
78 
79 #define PROPERTY_VERSION        UNISTRING("Version")
80 
81 static const sal_Char * const aUpdateEntryProperties[] = {
82     UPDATE_VERSION,
83     UPDATE_BUILDID,
84     UPDATE_DESCRIPTION,
85     DOWNLOAD_URL,
86     IS_DIRECT_DOWNLOAD,
87     RELEASE_NOTE"1",
88     RELEASE_NOTE"2",
89     RELEASE_NOTE"3",
90     RELEASE_NOTE"4",
91     RELEASE_NOTE"5",
92     OLD_VERSION
93 };
94 
95 static const sal_uInt32 nUpdateEntryProperties = sizeof(aUpdateEntryProperties) / sizeof(sal_Char *);
96 
97 //------------------------------------------------------------------------------
98 
99 NamedValueByNameAccess::~NamedValueByNameAccess()
100 {
101 }
102 
103 //------------------------------------------------------------------------------
104 
105 ::com::sun::star::uno::Any
106 NamedValueByNameAccess::getValue(const sal_Char * pName)
107 {
108     const sal_Int32 nLen = m_rValues.getLength();
109     for( sal_Int32 n=0; n < nLen; ++n )
110     {
111         if( m_rValues[n].Name.equalsAscii( pName ) )
112             return m_rValues[n].Value;
113     }
114     return ::com::sun::star::uno::Any();
115 }
116 
117 
118 //------------------------------------------------------------------------------
119 
120 bool
121 UpdateCheckROModel::isAutoCheckEnabled() const
122 {
123     return sal_True == m_aNameAccess.getValue(AUTOCHECK_ENABLED).get< sal_Bool >();
124 }
125 
126 //------------------------------------------------------------------------------
127 
128 bool
129 UpdateCheckROModel::isDownloadPaused() const
130 {
131     return sal_True == m_aNameAccess.getValue(DOWNLOAD_PAUSED).get< sal_Bool >();
132 }
133 
134 //------------------------------------------------------------------------------
135 
136 rtl::OUString
137 UpdateCheckROModel::getStringValue(const sal_Char * pStr) const
138 {
139     uno::Any aAny( m_aNameAccess.getValue(pStr) );
140     rtl::OUString aRet;
141 
142     aAny >>= aRet;
143 
144     return aRet;
145 }
146 
147 //------------------------------------------------------------------------------
148 
149 rtl::OUString UpdateCheckROModel::getLocalFileName() const
150 {
151     return getStringValue(LOCAL_FILE);
152 };
153 
154 //------------------------------------------------------------------------------
155 
156 sal_Int64 UpdateCheckROModel::getDownloadSize() const
157 {
158     uno::Any aAny( m_aNameAccess.getValue(DOWNLOAD_SIZE) );
159     sal_Int64 nRet = -1;
160 
161     aAny >>= nRet;
162     return nRet;
163 };
164 
165 //------------------------------------------------------------------------------
166 
167 rtl::OUString
168 UpdateCheckROModel::getUpdateEntryVersion() const
169 {
170     return getStringValue(OLD_VERSION);
171 }
172 
173 //------------------------------------------------------------------------------
174 
175 void
176 UpdateCheckROModel::getUpdateEntry(UpdateInfo& rInfo) const
177 {
178     rInfo.BuildId = getStringValue(UPDATE_BUILDID);
179     rInfo.Version = getStringValue(UPDATE_VERSION);
180     rInfo.Description = getStringValue(UPDATE_DESCRIPTION);
181 
182     sal_Bool isDirectDownload = sal_False;
183     m_aNameAccess.getValue(IS_DIRECT_DOWNLOAD) >>= isDirectDownload;
184 
185     rInfo.Sources.push_back( DownloadSource( isDirectDownload, getStringValue(DOWNLOAD_URL) ) );
186 
187     rtl::OString aStr(RELEASE_NOTE);
188     for(sal_Int32 n=1; n < 6; ++n )
189     {
190         rtl::OUString aUStr = getStringValue(aStr + rtl::OString::valueOf(n));
191         if( aUStr.getLength() > 0 )
192             rInfo.ReleaseNotes.push_back(ReleaseNote((sal_Int8) n, aUStr));
193     }
194 }
195 
196 
197 //------------------------------------------------------------------------------
198 
199 rtl::OUString UpdateCheckConfig::getDesktopDirectory()
200 {
201     rtl::OUString aRet;
202 
203 #ifdef WNT
204     WCHAR szPath[MAX_PATH];
205 
206     if( ! FAILED( SHGetSpecialFolderPathW( NULL, szPath, CSIDL_DESKTOPDIRECTORY, true ) ) )
207     {
208         aRet = rtl::OUString( reinterpret_cast< sal_Unicode * >(szPath) );
209         osl::FileBase::getFileURLFromSystemPath( aRet, aRet );
210     }
211 #else
212     // This should become a desktop specific setting in some system backend ..
213     rtl::OUString aHomeDir;
214     osl::Security().getHomeDir( aHomeDir );
215     aRet = aHomeDir + rtl::OUString::createFromAscii("/Desktop");
216 
217     // Set path to home directory when there is no /Desktop directory
218     osl::Directory aDocumentsDir( aRet );
219     if( osl::FileBase::E_None != aDocumentsDir.open() )
220         aRet = aHomeDir;
221 #endif
222 
223     return aRet;
224 }
225 
226 //------------------------------------------------------------------------------
227 
228 rtl::OUString UpdateCheckConfig::getAllUsersDirectory()
229 {
230     rtl::OUString aRet;
231 
232 #ifdef WNT
233     WCHAR szPath[MAX_PATH];
234 
235     if( ! FAILED( SHGetSpecialFolderPathW( NULL, szPath, CSIDL_COMMON_DOCUMENTS, true ) ) )
236     {
237         aRet = rtl::OUString( reinterpret_cast< sal_Unicode * >(szPath) );
238         osl::FileBase::RC rc;
239         rc = osl::FileBase::getFileURLFromSystemPath( aRet, aRet );
240     }
241 #else
242     osl::FileBase::getTempDirURL(aRet);
243 #endif
244 
245     return aRet;
246 }
247 
248 //------------------------------------------------------------------------------
249 UpdateCheckConfig::UpdateCheckConfig( const uno::Reference<container::XNameContainer>& xContainer,
250                                       const uno::Reference<container::XNameContainer>& xAvailableUpdates,
251                                       const uno::Reference<container::XNameContainer>& xIgnoredUpdates,
252                                       const ::rtl::Reference< UpdateCheckConfigListener >& rListener ) :
253     m_xContainer( xContainer ),
254     m_xAvailableUpdates( xAvailableUpdates ),
255     m_xIgnoredUpdates( xIgnoredUpdates ),
256     m_rListener( rListener )
257 {}
258 
259 //------------------------------------------------------------------------------
260 UpdateCheckConfig::~UpdateCheckConfig()
261 {}
262 
263 //------------------------------------------------------------------------------
264 
265 ::rtl::Reference< UpdateCheckConfig >
266 UpdateCheckConfig::get(
267     const uno::Reference<uno::XComponentContext>& xContext,
268     const ::rtl::Reference< UpdateCheckConfigListener >& rListener)
269 {
270     if( !xContext.is() )
271         throw uno::RuntimeException(
272             UNISTRING( "UpdateCheckConfig: empty component context" ),
273             uno::Reference< uno::XInterface >() );
274 
275     uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
276 
277     if( !xServiceManager.is() )
278         throw uno::RuntimeException(
279             UNISTRING( "UpdateCheckConfig: unable to obtain service manager from component context" ),
280             uno::Reference< uno::XInterface >() );
281 
282     uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
283         xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.configuration.ConfigurationProvider" ), xContext ),
284         uno::UNO_QUERY_THROW);
285 
286     beans::PropertyValue aProperty;
287     aProperty.Name  = UNISTRING( "nodepath" );
288     aProperty.Value = uno::makeAny( UNISTRING("org.openoffice.Office.Jobs/Jobs/UpdateCheck/Arguments") );
289 
290     uno::Sequence< uno::Any > aArgumentList( 1 );
291     aArgumentList[0] = uno::makeAny( aProperty );
292 
293     uno::Reference< container::XNameContainer > xContainer(
294         xConfigProvider->createInstanceWithArguments(
295             UNISTRING("com.sun.star.configuration.ConfigurationUpdateAccess"), aArgumentList ),
296         uno::UNO_QUERY_THROW );
297 
298     aProperty.Value = uno::makeAny( UNISTRING("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates") );
299     aArgumentList[0] = uno::makeAny( aProperty );
300     uno::Reference< container::XNameContainer > xIgnoredExt( xConfigProvider->createInstanceWithArguments( UNISTRING("com.sun.star.configuration.ConfigurationUpdateAccess"), aArgumentList ), uno::UNO_QUERY_THROW );
301 
302     aProperty.Value = uno::makeAny( UNISTRING("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/AvailableUpdates") );
303     aArgumentList[0] = uno::makeAny( aProperty );
304     uno::Reference< container::XNameContainer > xUpdateAvail( xConfigProvider->createInstanceWithArguments( UNISTRING("com.sun.star.configuration.ConfigurationUpdateAccess"), aArgumentList ), uno::UNO_QUERY_THROW );
305 
306     return new UpdateCheckConfig( xContainer, xUpdateAvail, xIgnoredExt, rListener );
307 }
308 
309 //------------------------------------------------------------------------------
310 
311 bool
312 UpdateCheckConfig::isAutoCheckEnabled() const
313 {
314     sal_Bool nValue = sal_False;
315     const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( AUTOCHECK_ENABLED ) ) >>= nValue;
316     return nValue;
317 }
318 
319 //------------------------------------------------------------------------------
320 
321 bool
322 UpdateCheckConfig::isAutoDownloadEnabled() const
323 {
324     sal_Bool nValue = sal_False;
325     const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( AUTODOWNLOAD_ENABLED ) ) >>= nValue;
326     return nValue;
327 }
328 
329 //------------------------------------------------------------------------------
330 
331 rtl::OUString
332 UpdateCheckConfig::getUpdateEntryVersion() const
333 {
334     rtl::OUString aValue;
335 
336     // getByName is defined as non const in XNameAccess
337     const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( OLD_VERSION ) ) >>= aValue;
338 
339     return aValue;
340 }
341 
342 //------------------------------------------------------------------------------
343 
344 sal_Int64
345 UpdateCheckConfig::getLastChecked() const
346 {
347     sal_Int64 nValue = 0;
348 
349     // getByName is defined as non const in XNameAccess
350     const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( LAST_CHECK ) ) >>= nValue;
351 
352     return nValue;
353 }
354 
355 //------------------------------------------------------------------------------
356 
357 sal_Int64
358 UpdateCheckConfig::getCheckInterval() const
359 {
360     sal_Int64 nValue = 0;
361 
362     // getByName is defined as non const in XNameAccess
363     const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( CHECK_INTERVAL ) ) >>= nValue;
364 
365     return nValue;
366 }
367 
368 //------------------------------------------------------------------------------
369 
370 rtl::OUString
371 UpdateCheckConfig::getLocalFileName() const
372 {
373     rtl::OUString aName = UNISTRING(LOCAL_FILE);
374     rtl::OUString aRet;
375 
376     if( m_xContainer->hasByName(aName) )
377         m_xContainer->getByName(aName) >>= aRet;
378 
379     return aRet;
380 }
381 
382 //------------------------------------------------------------------------------
383 
384 rtl::OUString
385 UpdateCheckConfig::getDownloadDestination() const
386 {
387     rtl::OUString aName = UNISTRING(DOWNLOAD_DESTINATION);
388     rtl::OUString aRet;
389 
390     const_cast <UpdateCheckConfig *> (this)->getByName(aName) >>= aRet;
391 
392     return aRet;
393 }
394 
395 //------------------------------------------------------------------------------
396 
397 void
398 UpdateCheckConfig::storeLocalFileName(const rtl::OUString& rLocalFileName, sal_Int64 nFileSize)
399 {
400     const sal_uInt8 nItems = 2;
401     const rtl::OUString aNameList[nItems] = { UNISTRING(LOCAL_FILE), UNISTRING(DOWNLOAD_SIZE) };
402     const uno::Any aValueList[nItems] = { uno::makeAny(rLocalFileName), uno::makeAny(nFileSize) };
403 
404     for( sal_uInt8 i=0; i < nItems; ++i )
405     {
406         if( m_xContainer->hasByName(aNameList[i]) )
407             m_xContainer->replaceByName(aNameList[i], aValueList[i]);
408         else
409             m_xContainer->insertByName(aNameList[i], aValueList[i]);
410     }
411 
412     commitChanges();
413 }
414 
415 //------------------------------------------------------------------------------
416 
417 void
418 UpdateCheckConfig::clearLocalFileName()
419 {
420     const sal_uInt8 nItems = 2;
421     const rtl::OUString aNameList[nItems] = { UNISTRING(LOCAL_FILE), UNISTRING(DOWNLOAD_SIZE) };
422 
423     for( sal_uInt8 i=0; i < nItems; ++i )
424     {
425         if( m_xContainer->hasByName(aNameList[i]) )
426             m_xContainer->removeByName(aNameList[i]);
427     }
428 
429     commitChanges();
430 }
431 
432 //------------------------------------------------------------------------------
433 
434 void
435 UpdateCheckConfig::storeDownloadPaused(bool paused)
436 {
437     replaceByName(UNISTRING(DOWNLOAD_PAUSED) , uno::makeAny(paused));
438     commitChanges();
439 }
440 
441 //------------------------------------------------------------------------------
442 
443 void
444 UpdateCheckConfig::updateLastChecked()
445 {
446     TimeValue systime;
447     osl_getSystemTime(&systime);
448 
449     sal_Int64 lastCheck = systime.Seconds;
450 
451     replaceByName(UNISTRING(LAST_CHECK), uno::makeAny(lastCheck));
452 }
453 
454 //------------------------------------------------------------------------------
455 
456 void
457 UpdateCheckConfig::storeUpdateFound( const UpdateInfo& rInfo, const rtl::OUString& aCurrentBuild)
458 
459 {
460     bool autoDownloadEnabled = isAutoDownloadEnabled();
461 
462     uno::Any aValues[nUpdateEntryProperties] =
463     {
464         uno::makeAny(rInfo.Version),
465         uno::makeAny(rInfo.BuildId),
466         uno::makeAny(rInfo.Description),
467         uno::makeAny(rInfo.Sources[0].URL),
468         uno::makeAny(rInfo.Sources[0].IsDirect ? sal_True : sal_False),
469         uno::makeAny(getReleaseNote(rInfo, 1, autoDownloadEnabled) ),
470         uno::makeAny(getReleaseNote(rInfo, 2, autoDownloadEnabled) ),
471         uno::makeAny(getReleaseNote(rInfo, 3, autoDownloadEnabled) ),
472         uno::makeAny(getReleaseNote(rInfo, 4, autoDownloadEnabled) ),
473         uno::makeAny(getReleaseNote(rInfo, 5, autoDownloadEnabled) ),
474         uno::makeAny(aCurrentBuild)
475     };
476 
477     rtl::OUString aName;
478     for( sal_uInt32 n=0; n < nUpdateEntryProperties; ++n )
479     {
480         aName = rtl::OUString::createFromAscii(aUpdateEntryProperties[n]);
481 
482         if( m_xContainer->hasByName(aName) )
483             m_xContainer->replaceByName(aName, aValues[n]);
484         else
485             m_xContainer->insertByName(aName,aValues[n]);
486     }
487 
488     commitChanges();
489 }
490 
491 //------------------------------------------------------------------------------
492 
493 void
494 UpdateCheckConfig::clearUpdateFound()
495 {
496     rtl::OUString aName;
497 
498     for( sal_uInt32 n=0; n < nUpdateEntryProperties; ++n )
499     {
500         aName = rtl::OUString::createFromAscii(aUpdateEntryProperties[n]);
501 
502         try {
503             if( m_xContainer->hasByName(aName) )
504                 m_xContainer->removeByName(aName);
505         } catch(const lang::WrappedTargetException& ) {
506             // Can not remove value, probably in share layer
507             OSL_ASSERT(false);
508             m_xContainer->replaceByName(aName, uno::makeAny(rtl::OUString()));
509         }
510     }
511 
512     /* As we have removed UpdateVersionFound from the shared configuration
513      * existing entries in the user layer do not have a oor operation and
514      * thus are completly ignored (which also means they can not be removed).
515      */
516 
517     commitChanges();
518 }
519 
520 //------------------------------------------------------------------------------
521 
522 uno::Sequence< rtl::OUString >
523 UpdateCheckConfig::getServiceNames()
524 {
525     uno::Sequence< rtl::OUString > aServiceList(1);
526     aServiceList[0] = UNISTRING( "com.sun.star.setup.UpdateCheckConfig");
527     return aServiceList;
528 }
529 
530 //------------------------------------------------------------------------------
531 
532 rtl::OUString
533 UpdateCheckConfig::getImplName()
534 {
535     return UNISTRING( "vnd.sun.UpdateCheckConfig");
536 }
537 
538 //------------------------------------------------------------------------------
539 
540 uno::Type SAL_CALL
541 UpdateCheckConfig::getElementType() throw (uno::RuntimeException)
542 {
543     return m_xContainer->getElementType();
544 }
545 
546 //------------------------------------------------------------------------------
547 
548 sal_Bool SAL_CALL
549 UpdateCheckConfig::hasElements() throw (uno::RuntimeException)
550 {
551     return m_xContainer->hasElements();
552 }
553 
554 //------------------------------------------------------------------------------
555 
556 uno::Any SAL_CALL
557 UpdateCheckConfig::getByName( const ::rtl::OUString& aName )
558     throw (container::NoSuchElementException, lang::WrappedTargetException,  uno::RuntimeException)
559 {
560     uno::Any aValue = m_xContainer->getByName( aName );
561 
562     // Provide dynamic default value
563     if( aName.equalsAscii(DOWNLOAD_DESTINATION) )
564     {
565         rtl::OUString aStr;
566         aValue >>= aStr;
567 
568         if( aStr.getLength() == 0 )
569             aValue = uno::makeAny(getDesktopDirectory());
570     }
571 
572     return aValue;
573 }
574 
575 //------------------------------------------------------------------------------
576 
577 uno::Sequence< ::rtl::OUString > SAL_CALL
578 UpdateCheckConfig::getElementNames(  ) throw (uno::RuntimeException)
579 {
580     return m_xContainer->getElementNames();
581 }
582 
583 //------------------------------------------------------------------------------
584 
585 sal_Bool SAL_CALL
586 UpdateCheckConfig::hasByName( const ::rtl::OUString& aName ) throw (uno::RuntimeException)
587 {
588     return m_xContainer->hasByName( aName );
589 }
590 
591 //------------------------------------------------------------------------------
592 
593 void SAL_CALL
594 UpdateCheckConfig::replaceByName( const ::rtl::OUString& aName, const uno::Any& aElement )
595     throw (lang::IllegalArgumentException, container::NoSuchElementException,
596            lang::WrappedTargetException, uno::RuntimeException)
597 {
598     return m_xContainer->replaceByName( aName, aElement );
599 }
600 
601 //------------------------------------------------------------------------------
602 // XChangesBatch
603 
604 void SAL_CALL
605 UpdateCheckConfig::commitChanges()
606     throw (lang::WrappedTargetException, uno::RuntimeException)
607 {
608     uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
609     if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
610     {
611         util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges();
612         xChangesBatch->commitChanges();
613 
614         if( m_rListener.is() )
615         {
616             const sal_Int32 nChanges = aChangesSet.getLength();
617             rtl::OUString aString;
618 
619             for( sal_Int32 i=0; i<nChanges; ++i )
620             {
621                 aChangesSet[i].Accessor >>= aString;
622 
623                 // FIXME: use non IgnoreAsciiCase version as soon as it becomes available
624                 if( aString.endsWithIgnoreAsciiCaseAsciiL(AUTOCHECK_ENABLED "']", sizeof(AUTOCHECK_ENABLED)+1) )
625                 {
626                     sal_Bool bEnabled = sal_False;
627                     aChangesSet[i].Element >>= bEnabled;
628                     m_rListener->autoCheckStatusChanged(sal_True == bEnabled);
629                 }
630                 // FIXME: use non IgnoreAsciiCase version as soon as it becomes available
631                 else if( aString.endsWithIgnoreAsciiCaseAsciiL(CHECK_INTERVAL "']", sizeof(CHECK_INTERVAL)+1) )
632                 {
633                     m_rListener->autoCheckIntervalChanged();
634                 }
635             }
636         }
637     }
638 
639     xChangesBatch = uno::Reference< util::XChangesBatch > ( m_xAvailableUpdates, uno::UNO_QUERY );
640     if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
641     {
642         util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges();
643         xChangesBatch->commitChanges();
644     }
645     xChangesBatch = uno::Reference< util::XChangesBatch > ( m_xIgnoredUpdates, uno::UNO_QUERY );
646     if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
647     {
648         util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges();
649         xChangesBatch->commitChanges();
650     }
651 }
652 
653 //------------------------------------------------------------------------------
654 
655 sal_Bool SAL_CALL
656 UpdateCheckConfig::hasPendingChanges(  ) throw (uno::RuntimeException)
657 {
658     uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
659     if( xChangesBatch.is() )
660         return xChangesBatch->hasPendingChanges();
661 
662     return sal_False;
663 }
664 
665 //------------------------------------------------------------------------------
666 
667 uno::Sequence< util::ElementChange > SAL_CALL
668 UpdateCheckConfig::getPendingChanges(  ) throw (uno::RuntimeException)
669 {
670     uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
671     if( xChangesBatch.is() )
672         return xChangesBatch->getPendingChanges();
673 
674     return uno::Sequence< util::ElementChange >();
675 }
676 
677 //------------------------------------------------------------------------------
678 bool UpdateCheckConfig::storeExtensionVersion( const rtl::OUString& rExtensionName,
679                                                const rtl::OUString& rVersion )
680 {
681     bool bNotify = true;
682 
683     if ( m_xAvailableUpdates->hasByName( rExtensionName ) )
684         uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) );
685     else
686     {
687         uno::Reference< beans::XPropertySet > elem( uno::Reference< lang::XSingleServiceFactory >( m_xAvailableUpdates, uno::UNO_QUERY_THROW )->createInstance(), uno::UNO_QUERY_THROW );
688         elem->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) );
689         m_xAvailableUpdates->insertByName( rExtensionName, uno::Any( elem ) );
690     }
691 
692     if ( m_xIgnoredUpdates->hasByName( rExtensionName ) )
693     {
694         ::rtl::OUString aIgnoredVersion;
695         uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
696         aValue >>= aIgnoredVersion;
697         if ( aIgnoredVersion.getLength() == 0 ) // no version means ignore all updates
698             bNotify = false;
699         else if ( aIgnoredVersion == rVersion ) // the user wanted to ignore this update
700             bNotify = false;
701     }
702 
703     commitChanges();
704 
705     return bNotify;
706 }
707 
708 //------------------------------------------------------------------------------
709 bool UpdateCheckConfig::checkExtensionVersion( const rtl::OUString& rExtensionName,
710                                                const rtl::OUString& rVersion )
711 {
712     if ( m_xAvailableUpdates->hasByName( rExtensionName ) )
713     {
714         ::rtl::OUString aStoredVersion;
715         uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
716         aValue >>= aStoredVersion;
717 
718         if ( m_xIgnoredUpdates->hasByName( rExtensionName ) )
719         {
720             ::rtl::OUString aIgnoredVersion;
721             uno::Any aValue2( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
722             aValue2 >>= aIgnoredVersion;
723             if ( aIgnoredVersion.getLength() == 0 ) // no version means ignore all updates
724                 return false;
725             else if ( aIgnoredVersion == aStoredVersion ) // the user wanted to ignore this update
726                 return false;
727             // TODO: else delete ignored entry?
728         }
729         if ( isVersionGreater( rVersion, aStoredVersion ) )
730             return true;
731         else
732         {
733             m_xAvailableUpdates->removeByName( rExtensionName );
734             commitChanges();
735         }
736     }
737 
738     return false;
739 }
740 
741 //------------------------------------------------------------------------------
742 rtl::OUString UpdateCheckConfig::getSubVersion( const rtl::OUString& rVersion,
743                                                 sal_Int32 *nIndex )
744 {
745     while ( *nIndex < rVersion.getLength() && rVersion[*nIndex] == '0')
746     {
747         ++*nIndex;
748     }
749 
750     return rVersion.getToken( 0, '.', *nIndex );
751 }
752 
753 //------------------------------------------------------------------------------
754 // checks if the second version string is greater than the first one
755 
756 bool UpdateCheckConfig::isVersionGreater( const rtl::OUString& rVersion1,
757                                           const rtl::OUString& rVersion2 )
758 {
759     for ( sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0; )
760     {
761         ::rtl::OUString sSub1( getSubVersion( rVersion1, &i1 ) );
762         ::rtl::OUString sSub2( getSubVersion( rVersion2, &i2 ) );
763 
764         if ( sSub1.getLength() < sSub2.getLength() ) {
765             return true;
766         } else if ( sSub1.getLength() > sSub2.getLength() ) {
767             return false;
768         } else if ( sSub1 < sSub2 ) {
769             return true;
770         } else if ( sSub1 > sSub2 ) {
771             return false;
772         }
773     }
774     return false;
775 }
776 
777 //------------------------------------------------------------------------------
778 //------------------------------------------------------------------------------
779 //------------------------------------------------------------------------------
780 
781 rtl::OUString SAL_CALL
782 UpdateCheckConfig::getImplementationName()  throw (uno::RuntimeException)
783 {
784     return getImplName();
785 }
786 
787 //------------------------------------------------------------------------------
788 
789 sal_Bool SAL_CALL
790 UpdateCheckConfig::supportsService(rtl::OUString const & serviceName)
791     throw (uno::RuntimeException)
792 {
793     uno::Sequence< rtl::OUString > aServiceNameList = getServiceNames();
794 
795     for( sal_Int32 n=0; n < aServiceNameList.getLength(); n++ )
796         if( aServiceNameList[n].equals(serviceName) )
797             return sal_True;
798 
799     return sal_False;
800 }
801 
802 //------------------------------------------------------------------------------
803 
804 uno::Sequence< rtl::OUString > SAL_CALL
805 UpdateCheckConfig::getSupportedServiceNames()  throw (uno::RuntimeException)
806 {
807     return getServiceNames();
808 }
809 
810 
811