/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_extensions.hxx"

#include "updatecheck.hxx"

#include <cppuhelper/implbase1.hxx>
#include <com/sun/star/beans/XFastPropertySet.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/frame/XDesktop.hpp>
#include <com/sun/star/frame/XFrame.hpp>
#include <com/sun/star/frame/DispatchResultEvent.hpp>
#include <com/sun/star/frame/DispatchResultState.hpp>
#include <com/sun/star/system/XSystemShellExecute.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <com/sun/star/task/XJob.hpp>
#include <com/sun/star/task/XJobExecutor.hpp>

// #include <comphelper/processfactory.hxx>

#include <rtl/ustrbuf.hxx>

#include <rtl/bootstrap.hxx>
#include <osl/process.h>
#include <osl/module.hxx>
#include <osl/file.hxx>

#ifdef WNT
#ifdef _MSC_VER
#pragma warning(push,1) // disable warnings within system headers
//#pragma warning(disable: 4917)
#endif
#include <objbase.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

#include "updateprotocol.hxx"
#include "updatecheckconfig.hxx"

namespace awt = com::sun::star::awt ;
namespace beans = com::sun::star::beans ;
namespace container = com::sun::star::container ;
namespace deployment = com::sun::star::deployment ;
namespace frame = com::sun::star::frame ;
namespace lang = com::sun::star::lang ;
namespace c3s = com::sun::star::system ;
namespace task = com::sun::star::task ;
namespace util = com::sun::star::util ;
namespace uno = com::sun::star::uno ;

#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s))

#define PROPERTY_TITLE          UNISTRING("BubbleHeading")
#define PROPERTY_TEXT           UNISTRING("BubbleText")
#define PROPERTY_IMAGE          UNISTRING("BubbleImageURL")
#define PROPERTY_SHOW_BUBBLE    UNISTRING("BubbleVisible")
#define PROPERTY_CLICK_HDL      UNISTRING("MenuClickHDL")
#define PROPERTY_DEFAULT_TITLE  UNISTRING("DefaultHeading")
#define PROPERTY_DEFAULT_TEXT   UNISTRING("DefaultText")
#define PROPERTY_SHOW_MENUICON  UNISTRING("MenuIconVisible")

//------------------------------------------------------------------------------

// Returns the URL of the release note for the given position
rtl::OUString getReleaseNote(const UpdateInfo& rInfo, sal_uInt8 pos, bool autoDownloadEnabled)
{
    std::vector< ReleaseNote >::const_iterator iter = rInfo.ReleaseNotes.begin();
    while( iter != rInfo.ReleaseNotes.end() )
    {
        if( pos == iter->Pos )
        {
            if( (pos > 2) || !autoDownloadEnabled || ! (iter->URL2.getLength() > 0) )
                return iter->URL;
        }
        else if( (pos == iter->Pos2) && ((1 == iter->Pos) || (2 == iter->Pos)) && autoDownloadEnabled )
            return iter->URL2;
        
        ++iter;
    }
            
    return rtl::OUString();    
}

//------------------------------------------------------------------------------

namespace 
{ 

static inline rtl::OUString getBuildId()
{
	rtl::OUString aPathVal(UNISTRING("${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("version") ":buildid}"));
	rtl::Bootstrap::expandMacros(aPathVal);
	return aPathVal;
}

//------------------------------------------------------------------------------
static inline rtl::OUString getBaseInstallation()
{
	rtl::OUString aPathVal(UNISTRING("${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap") ":BaseInstallation}"));
	rtl::Bootstrap::expandMacros(aPathVal);
	return aPathVal;
}

//------------------------------------------------------------------------------

inline bool isObsoleteUpdateInfo(const rtl::OUString& rBuildId)
{
    return sal_True != rBuildId.equals(getBuildId()) && rBuildId.getLength() > 0;
}


//------------------------------------------------------------------------------

rtl::OUString getImageFromFileName(const rtl::OUString& aFile)
{
#ifndef WNT
    rtl::OUString aUnpackPath;
    if( osl_getExecutableFile(&aUnpackPath.pData) == osl_Process_E_None )
    {
        sal_uInt32 lastIndex = aUnpackPath.lastIndexOf('/');
        if ( lastIndex > 0 )
        {
            aUnpackPath = aUnpackPath.copy( 0, lastIndex+1 );
            aUnpackPath  += UNISTRING( "unpack_update" );
        } 
        
        oslFileHandle hOut = NULL;
        oslProcess hProcess = NULL;
        
        rtl::OUString aSystemPath;
        osl::File::getSystemPathFromFileURL(aFile, aSystemPath);
        
        oslProcessError rc = osl_executeProcess_WithRedirectedIO(
            aUnpackPath.pData,                                  // [in] Image name
            &aSystemPath.pData, 1,                              // [in] Arguments
            osl_Process_WAIT || osl_Process_NORMAL,             // [in] Options
            NULL,                                               // [in] Security
            NULL,                                               // [in] Working directory
            NULL, 0,                                            // [in] Environment variables
            &hProcess,                                          // [out] Process handle
            NULL, &hOut, NULL                                   // [out] File handles for redirected I/O
        );
        
        if( osl_Process_E_None == rc )
        {
            oslProcessInfo aInfo;
            aInfo.Size = sizeof(oslProcessInfo);
            
            if( osl_Process_E_None == osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo) )
            {
                if( 0 == aInfo.Code )
                {
                    sal_Char   szBuffer[4096];
                    sal_uInt64 nBytesRead = 0;
                    const sal_uInt64 nBytesToRead = sizeof(szBuffer) - 1;
                    
                    rtl::OUString aImageName;
                    while( osl_File_E_None == osl_readFile(hOut, szBuffer, nBytesToRead, &nBytesRead) )
                    {
                        sal_Char *pc = szBuffer + nBytesRead;
                        do
                        {
                            *pc = '\0'; --pc;
                        } 
                        while( ('\n' == *pc) || ('\r' == *pc) );
                        
                        aImageName += rtl::OUString(szBuffer, pc - szBuffer + 1, osl_getThreadTextEncoding());

                        if( nBytesRead < nBytesToRead )
                            break;
                    }
                    
                    if( osl::FileBase::E_None == osl::FileBase::getFileURLFromSystemPath(aImageName, aImageName) )
                        return aImageName;
                }
            }
            
            osl_closeFile(hOut);
            osl_freeProcessHandle(hProcess);
        }
    }        
#endif
        
    return aFile;
}


//------------------------------------------------------------------------------

static uno::Reference< beans::XPropertySet > createMenuBarUI(
    const uno::Reference< uno::XComponentContext >& xContext,
    const uno::Reference< task::XJob >& xJob)
{
    if( !xContext.is() )
        throw uno::RuntimeException( 
            UNISTRING( "UpdateCheckJob: empty component context" ), uno::Reference< uno::XInterface > () );

    uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
    if( !xServiceManager.is() )
        throw uno::RuntimeException( 
            UNISTRING( "UpdateCheckJob: unable to obtain service manager from component context" ), uno::Reference< uno::XInterface > () );

    uno::Reference< beans::XPropertySet > xMenuBarUI = 
        uno::Reference< beans::XPropertySet > (
            xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.setup.UpdateCheckUI" ), xContext ),
            uno::UNO_QUERY_THROW);

    xMenuBarUI->setPropertyValue( PROPERTY_CLICK_HDL, uno::makeAny( xJob ) );

    return xMenuBarUI;
}

//------------------------------------------------------------------------------



typedef sal_Bool (* OnlineCheckFunc) ();

class UpdateCheckThread : public WorkerThread
{    

public:
    UpdateCheckThread( osl::Condition& rCondition,
        const uno::Reference<uno::XComponentContext>& xContext );

    virtual void SAL_CALL join();
    virtual void SAL_CALL terminate();
    virtual void SAL_CALL cancel();

protected:
    virtual ~UpdateCheckThread();

    virtual void SAL_CALL run();
    virtual void SAL_CALL onTerminated();

    /* Wrapper around checkForUpdates */
    bool runCheck( bool & rbExtensionsChecked );

private:

    /* Used to avoid dialup login windows (on platforms we know how to double this) */
    inline bool hasInternetConnection() const 
    {
        if(m_pHasInternetConnection != NULL )
            return (sal_True == m_pHasInternetConnection());
        return true;
    }
    
    /* Creates a new instance of UpdateInformationProvider and returns this instance */
    inline uno::Reference<deployment::XUpdateInformationProvider> createProvider()
    { 
        osl::MutexGuard aGuard(m_aMutex); 
        m_xProvider = deployment::UpdateInformationProvider::create(m_xContext);
        return m_xProvider;
    };

    /* Returns the remembered instance of UpdateInformationProvider if any */
    inline uno::Reference<deployment::XUpdateInformationProvider> getProvider()
        { osl::MutexGuard aGuard(m_aMutex); return m_xProvider; };

    /* Releases the remembered instance of UpdateInformationProvider if any */
    inline void clearProvider()
        { osl::MutexGuard aGuard(m_aMutex); m_xProvider.clear(); };
        
    osl::Mutex      m_aMutex;
    osl::Module     m_aModule;
        
protected:
    osl::Condition& m_aCondition;

private:

//    const 
    OnlineCheckFunc m_pHasInternetConnection;

    const uno::Reference<uno::XComponentContext> m_xContext;   
    uno::Reference<deployment::XUpdateInformationProvider> m_xProvider;
};


class ManualUpdateCheckThread : public UpdateCheckThread
{
public:
    ManualUpdateCheckThread( osl::Condition& rCondition, const uno::Reference<uno::XComponentContext>& xContext ) : 
        UpdateCheckThread(rCondition, xContext) {};

    virtual void SAL_CALL run();
};
    

class MenuBarButtonJob : public ::cppu::WeakImplHelper1< task::XJob >
{
public:
    MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck);
    
    // XJob
    virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) 
        throw (lang::IllegalArgumentException, uno::Exception);
    
private:
    rtl::Reference< UpdateCheck > m_aUpdateCheck;
};
    
class DownloadThread :  public WorkerThread
{
public:
    DownloadThread(
        osl::Condition& rCondition,
        const uno::Reference<uno::XComponentContext>& xContext, 
        const rtl::Reference< DownloadInteractionHandler >& rHandler,
        const rtl::OUString& rURL );

    virtual void SAL_CALL run();
    virtual void SAL_CALL cancel();
    virtual void SAL_CALL suspend();
    virtual void SAL_CALL onTerminated();

protected:
    ~DownloadThread();

private:
    osl::Condition& m_aCondition;
    const uno::Reference<uno::XComponentContext> m_xContext;
    const rtl::OUString m_aURL;
    Download m_aDownload;
};

//------------------------------------------------------------------------------
class ShutdownThread :  public osl::Thread
{
public:
    ShutdownThread( const uno::Reference<uno::XComponentContext>& xContext );

    virtual void SAL_CALL run();
    virtual void SAL_CALL onTerminated();

protected:
    ~ShutdownThread();

private:
    osl::Condition m_aCondition;
    const uno::Reference<uno::XComponentContext> m_xContext;
};

//------------------------------------------------------------------------------

UpdateCheckThread::UpdateCheckThread( osl::Condition& rCondition,
                                      const uno::Reference<uno::XComponentContext>& xContext ) :
    m_aCondition(rCondition),
    m_pHasInternetConnection(NULL),
    m_xContext(xContext)
{
    
#ifdef WNT
    rtl::OUString aPath;
    if( osl_getExecutableFile(&aPath.pData) == osl_Process_E_None )
    {
        sal_uInt32 lastIndex = aPath.lastIndexOf('/');
        if ( lastIndex > 0 )
        {
            aPath = aPath.copy( 0, lastIndex+1 );
            aPath  += UNISTRING( "onlinecheck" );
        }
        
        if ( m_aModule.load(aPath) )
        {
            m_pHasInternetConnection =
                reinterpret_cast < OnlineCheckFunc > (
                    m_aModule.getFunctionSymbol( UNISTRING("hasInternetConnection")));
        }
    }
#endif
    
    createSuspended();

    // actually run the thread
    resume();
}

//------------------------------------------------------------------------------

UpdateCheckThread::~UpdateCheckThread()
{
}

//------------------------------------------------------------------------------


void SAL_CALL 
UpdateCheckThread::terminate()
{
    // Cancel potentially hanging http request ..
    cancel();
    // .. before terminating
    osl::Thread::terminate();
}

//------------------------------------------------------------------------------

void SAL_CALL 
UpdateCheckThread::join()
{
    uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
    
    // do not join during an update check until #i73893# is fixed
    if( ! xProvider.is() )
    {
        osl::Thread::join();
    }
}

//------------------------------------------------------------------------------

void SAL_CALL 
UpdateCheckThread::cancel()
{
    uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
    
    if( xProvider.is() )
        xProvider->cancel();   
}

//------------------------------------------------------------------------------

bool 
UpdateCheckThread::runCheck( bool & rbExtensionsChecked )
{
    bool ret = false;
    UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;

    UpdateInfo aInfo;
    rtl::Reference< UpdateCheck > aController(UpdateCheck::get());

    if( checkForUpdates(aInfo, m_xContext, aController->getInteractionHandler(), createProvider()) )
    {
        aController->setUpdateInfo(aInfo);
        eUIState = aController->getUIState(aInfo);
        ret = true;
    }
    else
        aController->setCheckFailedState();

    // We will only look for extension updates, when there is no 'check for office updates' dialog open
    // and when there was no office update found
    if ( ( eUIState != UPDATESTATE_UPDATE_AVAIL ) &&
         ( eUIState != UPDATESTATE_UPDATE_NO_DOWNLOAD ) &&
         !aController->isDialogShowing() &&
         !rbExtensionsChecked )
    {
        bool bHasExtensionUpdates = checkForExtensionUpdates( m_xContext );
        aController->setHasExtensionUpdates( bHasExtensionUpdates );
        if ( bHasExtensionUpdates )
            aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL );
        rbExtensionsChecked = true;
    }

    // joining with this thread is safe again
    clearProvider();
    return ret;
}

//------------------------------------------------------------------------------

void SAL_CALL
UpdateCheckThread::onTerminated()
{
    delete this;
}

//------------------------------------------------------------------------------

void SAL_CALL
UpdateCheckThread::run()
{
    bool bExtensionsChecked = false;
    TimeValue systime;
    TimeValue nExtCheckTime;
    osl_getSystemTime( &nExtCheckTime );

    osl::Condition::Result aResult = osl::Condition::result_timeout;
    TimeValue tv = { 10, 0 };
    
    // Initial wait to avoid doing further time consuming tasks during start-up
    aResult = m_aCondition.wait(&tv);
    
    try {
        
        while( sal_True == schedule() )
        {
            /* Use cases:
             *  a) manual check requested from auto check thread - "last check" should not be checked (one time)
             *     a1) manual check was requested in the middle of a running auto check,
             *         condition is set 
             *     a2) manual check was requested while waiting for a retry,
             *         condition is set 
             *     a3) manual check was requested while waiting for time to next 
             *         scheduled check elapsing, condition is set
             *     a4) manual check was requested during initial wait, condition is set
             *  b) check interval got changed, condition may be set - same sub-cases as a), 
             *     but "last check" should be honored
             *  c) normal auto check mode, condition not set - "last check" should be honored
             */
                        
            // Accessing const members without synchronization
            rtl::Reference< UpdateCheck > aController(UpdateCheck::get());            
            rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *aController);
            
            // FIXME: remember last & offset ?
            sal_Int64 last   = rModel->getLastChecked();
            sal_Int64 offset = rModel->getCheckInterval();
            
            rModel.clear();
            
            // last == 0 means check immediately
            bool checkNow = ! (last > 0);
            
            // Reset the condition to avoid busy loops 
            if( osl::Condition::result_ok == aResult )
            {
                m_aCondition.reset();
                aResult = osl::Condition::result_timeout;
                checkNow = aController->isDialogShowing();
            }
            
            if( ! checkNow ) 
            {
                osl_getSystemTime(&systime);
                
                // Go back to sleep until time has elapsed
                sal_Int64 next = last + offset;
                if( last + offset > systime.Seconds )
                {
                    // This can not be > 32 Bit for now ..
                    tv.Seconds = static_cast< sal_Int32 > (next - systime.Seconds);
                    aResult = m_aCondition.wait(&tv);
                    continue;
                }
            }

            static sal_uInt8 n = 0;
            
            if( ! hasInternetConnection() || ! runCheck( bExtensionsChecked ) )
            {
                // the extension update check should be independent from the office update check
                // 
                osl_getSystemTime( &systime );
                if ( nExtCheckTime.Seconds + offset < systime.Seconds )
                    bExtensionsChecked = false;

                // Increase next by 15, 60, .. minutes
                static const sal_Int32 nRetryInterval[] = { 900, 3600, 14400, 86400 };
                
                if( n < sizeof(nRetryInterval) / sizeof(sal_Int32) )
                    ++n;
                
                tv.Seconds = nRetryInterval[n-1];
                aResult = m_aCondition.wait(&tv);
            }
            else // reset retry counter
            {
                n = 0;
                bExtensionsChecked = false;
            }
        }
    }
    
    catch(const uno::Exception& e) {
        // Silently catch all errors
        OSL_TRACE( "Caught exception: %s\n thread terminated.\n",
            rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr() );
    }    
}

//------------------------------------------------------------------------------

void SAL_CALL
ManualUpdateCheckThread::run()
{
    bool bExtensionsChecked = false;

    try {
        runCheck( bExtensionsChecked );
        m_aCondition.reset();
    }
    catch(const uno::Exception& e) {
        // Silently catch all errors
        OSL_TRACE( "Caught exception: %s\n thread terminated.\n",
            rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr() );
    }    
}

//------------------------------------------------------------------------------

MenuBarButtonJob::MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck) : 
    m_aUpdateCheck(rUpdateCheck) 
{
};
    
//------------------------------------------------------------------------------

uno::Any SAL_CALL 
MenuBarButtonJob::execute(const uno::Sequence<beans::NamedValue>& ) 
    throw (lang::IllegalArgumentException, uno::Exception)
{
    if ( m_aUpdateCheck->shouldShowExtUpdDlg() )
        m_aUpdateCheck->showExtensionDialog();
    else
        m_aUpdateCheck->showDialog();

    return uno::Any();
}

//------------------------------------------------------------------------------

DownloadThread::DownloadThread(osl::Condition& rCondition,
                               const uno::Reference<uno::XComponentContext>& xContext, 
                               const rtl::Reference< DownloadInteractionHandler >& rHandler, 
                               const rtl::OUString& rURL) :
    m_aCondition(rCondition),
    m_xContext(xContext),
    m_aURL(rURL),
    m_aDownload(xContext, rHandler)
{
    createSuspended();
}

//------------------------------------------------------------------------------

DownloadThread::~DownloadThread()
{
}

//------------------------------------------------------------------------------
        
void SAL_CALL 
DownloadThread::run()
{
#ifdef WNT
    CoUninitialize();
    CoInitialize( NULL );
#endif

    while( schedule() )
    {
        rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
        
        rtl::OUString aLocalFile = rModel->getLocalFileName();
        rtl::OUString aDownloadDest = rModel->getDownloadDestination();

        // release config class for now
        rModel.clear();
            
        static sal_uInt8 n = 0;
        if( ! m_aDownload.start(m_aURL, aLocalFile, aDownloadDest ) )
        {
            // retry every 15s unless the dialog is not visible
            TimeValue tv;
            tv.Seconds = 15;
            
            if( ! UpdateCheck::get()->isDialogShowing() )
            {
                // Increase next by 1, 5, 15, 60, .. minutes
                static const sal_Int16 nRetryInterval[] = { 60, 300, 900, 3600 };
                
                if( n < sizeof(nRetryInterval) / sizeof(sal_Int16) )
                    ++n;
                
                tv.Seconds = nRetryInterval[n-1];
            }
            m_aCondition.wait(&tv);
        }
		else
		{
			// reset wait period after successful download
			n=0;
		}
    }
}

//------------------------------------------------------------------------------

void SAL_CALL DownloadThread::cancel()
{
    m_aDownload.stop();
    resume();
    
    rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
    aController->cancelDownload();   
}

//------------------------------------------------------------------------------

void SAL_CALL DownloadThread::suspend()
{
    osl::Thread::suspend();
    m_aDownload.stop();
}

//------------------------------------------------------------------------------

void SAL_CALL DownloadThread::onTerminated()
{
    delete this;
}

//------------------------------------------------------------------------------
ShutdownThread::ShutdownThread( const uno::Reference<uno::XComponentContext>& xContext) :
    m_xContext( xContext )
{
    create();
}

//------------------------------------------------------------------------------
ShutdownThread::~ShutdownThread()
{
}

//------------------------------------------------------------------------------
void SAL_CALL 
ShutdownThread::run()
{
    TimeValue tv = { 0, 250 };

    m_aCondition.wait(&tv);

    // Tell QuickStarter not to veto ..
    uno::Reference< beans::XFastPropertySet > xQuickStarter(
        UpdateCheck::createService(UNISTRING("com.sun.star.office.Quickstart"), m_xContext), 
        uno::UNO_QUERY
    );

    if (xQuickStarter.is())
        xQuickStarter->setFastPropertyValue(0, uno::makeAny(false)); 

    // Shutdown the office
    uno::Reference< frame::XDesktop > xDesktop(
        UpdateCheck::createService(UNISTRING("com.sun.star.frame.Desktop"), m_xContext), 
        uno::UNO_QUERY);

    if( xDesktop.is() )
        xDesktop->terminate();
}

//------------------------------------------------------------------------------
void SAL_CALL ShutdownThread::onTerminated()
{
    delete this;
}

//------------------------------------------------------------------------------

} // anonymous namespace


//------------------------------------------------------------------------------


void 
UpdateCheck::initialize(const uno::Sequence< beans::NamedValue >& rValues,
                        const uno::Reference<uno::XComponentContext>& xContext)
{
    osl::MutexGuard aGuard(m_aMutex);
    
    if( NOT_INITIALIZED == m_eState )
    {
        NamedValueByNameAccess aNameAccess(rValues);
        UpdateCheckROModel aModel( aNameAccess );
        m_xContext = xContext;

        rtl::OUString aUpdateEntryVersion = aModel.getUpdateEntryVersion();
        
        aModel.getUpdateEntry(m_aUpdateInfo);
        
        bool obsoleteUpdateInfo = isObsoleteUpdateInfo(aUpdateEntryVersion);
        bool bContinueDownload = false;
        bool bDownloadAvailable = false;

        m_bHasExtensionUpdate = checkForPendingUpdates( xContext );
        m_bShowExtUpdDlg = false;

        rtl::OUString aLocalFileName = aModel.getLocalFileName();
        
        if( aLocalFileName.getLength() > 0 )
        {
            bContinueDownload = true;

            // Try to get the number of bytes already on disk
            osl::DirectoryItem aDirectoryItem;
            if( osl::DirectoryItem::E_None == osl::DirectoryItem::get(aLocalFileName, aDirectoryItem) )
            {
                osl::FileStatus aFileStatus(FileStatusMask_FileSize);
                if( osl::DirectoryItem::E_None == aDirectoryItem.getFileStatus(aFileStatus) )
                {
                    sal_Int64 nDownloadSize = aModel.getDownloadSize();
                    sal_Int64 nFileSize = aFileStatus.getFileSize();

                    if( nDownloadSize > 0 )
                    {
                        if ( nDownloadSize <= nFileSize ) // we have already downloaded everthing
                        {
                            bContinueDownload = false;
                            bDownloadAvailable = true;
                            m_aImageName = getImageFromFileName( aLocalFileName );
                        }
                        else // Calculate initial percent value.
                        {
                            sal_Int32 nPercent = (sal_Int32) (100 * nFileSize / nDownloadSize);
                            getUpdateHandler()->setProgress( nPercent );
                        }
                    }
                }
            }

            if ( bContinueDownload )
            {
                bool downloadPaused = aModel.isDownloadPaused();

                enableDownload(true, downloadPaused);
                setUIState(downloadPaused ? UPDATESTATE_DOWNLOAD_PAUSED : UPDATESTATE_DOWNLOADING);
            }
            
        }
        if ( !bContinueDownload )
        {
            // We do this intentionally only if no download is in progress ..
            if( obsoleteUpdateInfo ) 
            {
                // Bring-up release note for position 5 ..
                const rtl::OUString aURL(getReleaseNote(m_aUpdateInfo, 5));
                if( aURL.getLength() > 0 )
                    showReleaseNote(aURL);
                
                // Data is outdated, probably due to installed update 
                rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( xContext, *this );
                aConfig->clearUpdateFound();
                aConfig->clearLocalFileName();
    
                
                m_aUpdateInfo = UpdateInfo();
                // Remove outdated release notes
                storeReleaseNote( 1, rtl::OUString() );
                storeReleaseNote( 2, rtl::OUString() );
            }
            else
            {
                enableAutoCheck(aModel.isAutoCheckEnabled());
                if ( bDownloadAvailable )
                    setUIState( UPDATESTATE_DOWNLOAD_AVAIL );
                else
                    setUIState(getUIState(m_aUpdateInfo));
            }
        }
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::cancel()
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    WorkerThread *pThread = m_pThread;
    UpdateState eUIState = getUIState(m_aUpdateInfo);

    aGuard.clear();
    
    if( NULL != pThread )
        pThread->cancel();
    
    setUIState(eUIState);
}

//------------------------------------------------------------------------------

void 
UpdateCheck::download()
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    UpdateInfo aInfo(m_aUpdateInfo);
    State eState = m_eState;
    aGuard.clear();

    if( aInfo.Sources[0].IsDirect )
    {
        // Ignore second click of a double click
        if( DOWNLOADING != eState )
        {
            shutdownThread(true);
    
            osl::ClearableMutexGuard aGuard2(m_aMutex);
            enableDownload(true);
            aGuard2.clear();
            setUIState(UPDATESTATE_DOWNLOADING);
        }
    }
    else
    {
        showReleaseNote(aInfo.Sources[0].URL); // Display in browser
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::install()
{
    osl::MutexGuard aGuard(m_aMutex);
    
    const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
        createService( UNISTRING( "com.sun.star.system.SystemShellExecute" ), m_xContext ),
        uno::UNO_QUERY );
    
    try {
        // Construct install command ??
        
        // Store release note for position 3 and 4
        rtl::OUString aURL(getReleaseNote(m_aUpdateInfo, 3));
        storeReleaseNote(1, aURL);

        aURL = getReleaseNote(m_aUpdateInfo, 4);
        storeReleaseNote(2, aURL);
        
        if( xShellExecute.is() )
        {
            rtl::OUString aInstallImage(m_aImageName);
            osl::FileBase::getSystemPathFromFileURL(aInstallImage, aInstallImage);
           
            rtl::OUString aParameter;
            sal_Int32 nFlags = c3s::SystemShellExecuteFlags::DEFAULTS;
#if ( defined LINUX || defined SOLARIS )
            nFlags = 42;
            aParameter = getBaseInstallation();
            if( aParameter.getLength() > 0 )
                osl::FileBase::getSystemPathFromFileURL(aParameter, aParameter);
            
            aParameter += UNISTRING(" &");
#endif            

            rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
            rModel->clearLocalFileName();

            xShellExecute->execute(aInstallImage, aParameter, nFlags);
            ShutdownThread *pShutdownThread = new ShutdownThread( m_xContext );
            (void) pShutdownThread;
        }
    } catch(uno::Exception&) {
        m_aUpdateHandler->setErrorMessage( m_aUpdateHandler->getDefaultInstErrMsg() );
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::pause()
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    if( NULL != m_pThread )
        m_pThread->suspend();
    
    rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
    aGuard.clear();
    
    rModel->storeDownloadPaused(true);
    setUIState(UPDATESTATE_DOWNLOAD_PAUSED);
}

//------------------------------------------------------------------------------

void 
UpdateCheck::resume()
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    if( NULL != m_pThread )
        m_pThread->resume();
    
    rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
    aGuard.clear();
    
    rModel->storeDownloadPaused(false);
    setUIState(UPDATESTATE_DOWNLOADING);
}

//------------------------------------------------------------------------------

void 
UpdateCheck::closeAfterFailure()
{
    osl::ClearableMutexGuard aGuard(m_aMutex);

    if ( ( m_eState == DISABLED ) || ( m_eState == CHECK_SCHEDULED ) )
    {
        const UpdateState eUIState = getUIState( m_aUpdateInfo );
        aGuard.clear();
        setUIState( eUIState, true );
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::shutdownThread(bool join)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    // copy thread object pointer to stack
    osl::Thread *pThread = m_pThread;
    m_pThread = NULL;
    aGuard.clear();
    
    if( NULL != pThread )
    {
        pThread->terminate();
        if( join )
        {
            m_aCondition.set();
            pThread->join();
            m_aCondition.reset();
        }
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::enableAutoCheck(bool enable)
{
    if( enable )
        m_pThread = new UpdateCheckThread(m_aCondition, m_xContext);
    
    m_eState = enable ? CHECK_SCHEDULED : DISABLED;    
}

//------------------------------------------------------------------------------

void 
UpdateCheck::enableDownload(bool enable, bool paused)
{
    OSL_ASSERT(NULL == m_pThread);
    
    State eState = DISABLED;
    if( enable )
    {
        m_pThread = new DownloadThread(m_aCondition, m_xContext, this, m_aUpdateInfo.Sources[0].URL );
        if( !paused )
        {
            eState = DOWNLOADING;
            m_pThread->resume();
        }
        else
            eState = DOWNLOAD_PAUSED;
        
        m_eState = eState;
    }
    else {
        enableAutoCheck(UpdateCheckConfig::get(m_xContext)->isAutoCheckEnabled());
    }
    
}

//------------------------------------------------------------------------------

bool 
UpdateCheck::downloadTargetExists(const rtl::OUString& rFileName)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
    UpdateState eUIState = UPDATESTATE_DOWNLOADING;
    
    bool cont = false;
    
    if( aUpdateHandler->isVisible() )
    {
        cont = aUpdateHandler->showOverwriteWarning();
        if( cont )
        {
            if( osl_File_E_None != osl_removeFile(rFileName.pData) )
            {
                // FIXME: error message
                cont = false;
            }
        }
        else
            eUIState = getUIState(m_aUpdateInfo);
    }
    else
    {
        m_aImageName = getImageFromFileName(rFileName);
        eUIState = UPDATESTATE_DOWNLOAD_AVAIL;
    }
    
    if( !cont )
    {
        shutdownThread(false);
        enableDownload(false);
        
        aGuard.clear();
        setUIState(eUIState);
    }        
    
    return cont;
}

//------------------------------------------------------------------------------
bool UpdateCheck::checkDownloadDestination( const rtl::OUString& rFileName )
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    rtl::Reference< UpdateHandler > aUpdateHandler( getUpdateHandler() );
    
    bool bReload = false;
    
    if( aUpdateHandler->isVisible() )
    {
        bReload = aUpdateHandler->showOverwriteWarning( rFileName );
    }
    
    return bReload;
}

//------------------------------------------------------------------------------

void 
UpdateCheck::downloadStalled(const rtl::OUString& rErrorMessage)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
    aGuard.clear();
    
    aUpdateHandler->setErrorMessage(rErrorMessage);
    setUIState(UPDATESTATE_ERROR_DOWNLOADING);
}

//------------------------------------------------------------------------------

void 
UpdateCheck::downloadProgressAt(sal_Int8 nPercent)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
    aGuard.clear();
    
    aUpdateHandler->setProgress(nPercent);
    setUIState(UPDATESTATE_DOWNLOADING);
}

//------------------------------------------------------------------------------

void 
UpdateCheck::downloadStarted(const rtl::OUString& rLocalFileName, sal_Int64 nFileSize)
{
    if ( nFileSize > 0 )
    {
        osl::MutexGuard aGuard(m_aMutex);

        rtl::Reference< UpdateCheckConfig > aModel(UpdateCheckConfig::get(m_xContext));
        aModel->storeLocalFileName(rLocalFileName, nFileSize);

        // Bring-up release note for position 1 ..
        const rtl::OUString aURL(getReleaseNote(m_aUpdateInfo, 1, aModel->isAutoDownloadEnabled()));
        if( aURL.getLength() > 0 )
            showReleaseNote(aURL);
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::downloadFinished(const rtl::OUString& rLocalFileName)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    // no more retries
    m_pThread->terminate();
    
    m_aImageName = getImageFromFileName(rLocalFileName);
    UpdateInfo aUpdateInfo(m_aUpdateInfo);
    
    aGuard.clear();
    setUIState(UPDATESTATE_DOWNLOAD_AVAIL);
    
    // Bring-up release note for position 2 ..
    rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
    const rtl::OUString aURL(getReleaseNote(aUpdateInfo, 2, rModel->isAutoDownloadEnabled()));
    if( aURL.getLength() > 0 )
        showReleaseNote(aURL);
}

//------------------------------------------------------------------------------

void
UpdateCheck::cancelDownload()
{
    shutdownThread(true);
    
    osl::MutexGuard aGuard(m_aMutex);
    enableDownload(false);
    
    rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
    
    rtl::OUString aLocalFile(rModel->getLocalFileName());
    rModel->clearLocalFileName();
    rModel->storeDownloadPaused(false);

    if( isObsoleteUpdateInfo(rModel->getUpdateEntryVersion()) )
    {
        rModel->clearUpdateFound(); // This wasn't done during init yet ..
        m_aUpdateInfo = UpdateInfo();
    }

    /*oslFileError rc =*/ osl_removeFile(aLocalFile.pData);
    // FIXME: error handling ..
    
}

//------------------------------------------------------------------------------

void
UpdateCheck::showDialog(bool forceCheck)
{
    osl::ResettableMutexGuard aGuard(m_aMutex);
    
    bool update_found = m_aUpdateInfo.BuildId.getLength() > 0;
    bool bSetUIState = ! m_aUpdateHandler.is();
    
    UpdateState eDialogState = UPDATESTATES_COUNT;

    switch( m_eState )
    {
    case DISABLED:
    case CHECK_SCHEDULED:
        if( forceCheck || ! update_found ) // Run check when forced or if we did not find an update yet
        {
            eDialogState = UPDATESTATE_CHECKING;
            bSetUIState = true;
        }
        else if(m_aUpdateInfo.Sources[0].IsDirect)
            eDialogState = UPDATESTATE_UPDATE_AVAIL;
        else
            eDialogState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
        break;
            
    case DOWNLOADING:
        eDialogState = UPDATESTATE_DOWNLOADING;
        break;
        
    case DOWNLOAD_PAUSED:
        eDialogState = UPDATESTATE_DOWNLOAD_PAUSED;
        break;
        
    case NOT_INITIALIZED:
        OSL_ASSERT( false );
        break;
    }

    if( bSetUIState )
    {
        aGuard.clear();
        setUIState(eDialogState, true); // suppress bubble as Dialog will be visible soon
        aGuard.reset();
    }
 
    getUpdateHandler()->setVisible(true);
    
    // Run check in separate thread ..
    if( UPDATESTATE_CHECKING == eDialogState )
    {
        if( DISABLED == m_eState )
        {
            // destructs itself when done, not cancellable for now ..
            new ManualUpdateCheckThread(m_aCondition, m_xContext);
        }
        
        m_aCondition.set();
    }
}

//------------------------------------------------------------------------------

void 
UpdateCheck::setUpdateInfo(const UpdateInfo& aInfo)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);

    bool bSuppressBubble = (sal_True == aInfo.BuildId.equals(m_aUpdateInfo.BuildId));
    m_aUpdateInfo = aInfo;

    OSL_ASSERT(DISABLED == m_eState || CHECK_SCHEDULED == m_eState);

    // Ignore leading non direct download if we get direct ones
    std::vector< DownloadSource >::iterator iter = m_aUpdateInfo.Sources.begin();
    while( iter != m_aUpdateInfo.Sources.end() )
    {
        if( iter->IsDirect )
            break;
        
        ++iter;
    }
    
    if( (iter != m_aUpdateInfo.Sources.begin()) &&
        (iter != m_aUpdateInfo.Sources.end()) &&
        iter->IsDirect )
    {
        m_aUpdateInfo.Sources.erase(m_aUpdateInfo.Sources.begin(), --iter);
    }
       
    rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *this);
    OSL_ASSERT( rModel.is() );
    
    // Decide whether to use alternate release note pos ..
    bool autoDownloadEnabled = rModel->isAutoDownloadEnabled();
    
    std::vector< ReleaseNote >::iterator iter2 = m_aUpdateInfo.ReleaseNotes.begin();
    while( iter2 != m_aUpdateInfo.ReleaseNotes.end() )
    {
        if( ((1 == iter2->Pos) || (2 == iter2->Pos)) && autoDownloadEnabled && (iter2->URL2.getLength() > 0))
        {
            iter2->URL = iter2->URL2;
            iter2->URL2 = rtl::OUString();
            iter2->Pos = iter2->Pos2;
            iter2->Pos2 = 0;
        }
        
        ++iter2;
    }
        
    // do not move below store/clear ..
    rModel->updateLastChecked();
    
    UpdateState eUIState;
    if( m_aUpdateInfo.Sources.size() > 0 )
    {
        rModel->storeUpdateFound(aInfo, getBuildId());
        
        if( m_aUpdateInfo.Sources[0].IsDirect )
        {
            eUIState = UPDATESTATE_UPDATE_AVAIL; 
        
            if( rModel->isAutoDownloadEnabled() )
            {
                shutdownThread(false);
                eUIState = UPDATESTATE_DOWNLOADING;
                enableDownload(true);
            }
        }
        else
            eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
    }
    else
    {
        eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
        rModel->clearUpdateFound();
    }
    
    aGuard.clear();
    setUIState(eUIState, bSuppressBubble);
}

//------------------------------------------------------------------------------

void 
UpdateCheck::setCheckFailedState()
{
    setUIState(UPDATESTATE_ERROR_CHECKING);
}

//------------------------------------------------------------------------------
void UpdateCheck::handleMenuBarUI( rtl::Reference< UpdateHandler > rUpdateHandler,
                                   UpdateState& eState,
                                   bool suppressBubble )
{
    uno::Reference<beans::XPropertySet> xMenuBarUI( m_xMenuBarUI );

    if ( ( UPDATESTATE_NO_UPDATE_AVAIL == eState ) && m_bHasExtensionUpdate )
        eState = UPDATESTATE_EXT_UPD_AVAIL;

    if ( UPDATESTATE_EXT_UPD_AVAIL == eState )
        m_bShowExtUpdDlg = true;
    else
        m_bShowExtUpdDlg = false;
        
    if( xMenuBarUI.is() )
    {
        if( UPDATESTATE_NO_UPDATE_AVAIL == eState )
        {
            xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::makeAny(sal_False) );
        }
        else
        {
            xMenuBarUI->setPropertyValue( PROPERTY_TITLE, uno::makeAny(rUpdateHandler->getBubbleTitle(eState)) );
            xMenuBarUI->setPropertyValue( PROPERTY_TEXT, uno::makeAny(rUpdateHandler->getBubbleText(eState)) );
            
            if( ! suppressBubble && ( ! rUpdateHandler->isVisible() || rUpdateHandler->isMinimized() ) )
                xMenuBarUI->setPropertyValue( PROPERTY_SHOW_BUBBLE, uno::makeAny( sal_True ) );

            if( UPDATESTATE_CHECKING != eState )
                xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::makeAny(sal_True) );
        }
    }
}

//------------------------------------------------------------------------------
void UpdateCheck::setUIState(UpdateState eState, bool suppressBubble)
{
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    if( ! m_xMenuBarUI.is() && 
        (DISABLED != m_eState) && 
        ( m_bHasExtensionUpdate	|| (UPDATESTATE_NO_UPDATE_AVAIL != eState)) &&
        (UPDATESTATE_CHECKING != eState) &&
        (UPDATESTATE_ERROR_CHECKING != eState)
    )
    {
        m_xMenuBarUI = createMenuBarUI(m_xContext, new MenuBarButtonJob(this));
    }

    // Show bubble only when the status has changed
    if ( eState == m_eUpdateState )
        suppressBubble = true;
    else
        m_eUpdateState = eState;

    rtl::Reference<UpdateHandler> aUpdateHandler(getUpdateHandler());
    OSL_ASSERT( aUpdateHandler.is() );

    UpdateInfo aUpdateInfo(m_aUpdateInfo);
    rtl::OUString aImageName(m_aImageName);
    
    aGuard.clear();
    
    handleMenuBarUI( aUpdateHandler, eState, suppressBubble );
    
    if( (UPDATESTATE_UPDATE_AVAIL == eState)
     || (UPDATESTATE_DOWNLOAD_PAUSED == eState)
     || (UPDATESTATE_DOWNLOADING == eState) )
    {
        uno::Reference< uno::XComponentContext > xContext(m_xContext);

        rtl::OUString aDownloadDestination = 
            UpdateCheckConfig::get(xContext, this)->getDownloadDestination();

        osl_getSystemPathFromFileURL(aDownloadDestination.pData, &aDownloadDestination.pData);
        
        aUpdateHandler->setDownloadPath(aDownloadDestination);
    }
    else if( UPDATESTATE_DOWNLOAD_AVAIL == eState )
    {
        aUpdateHandler->setDownloadFile(aImageName);
    }
    
    aUpdateHandler->setDescription(aUpdateInfo.Description);
    aUpdateHandler->setNextVersion(aUpdateInfo.Version);
    aUpdateHandler->setState(eState);
}

//------------------------------------------------------------------------------

UpdateState 
UpdateCheck::getUIState(const UpdateInfo& rInfo)
{
    UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
    
    if( rInfo.BuildId.getLength() > 0 )
    {
        if( rInfo.Sources[0].IsDirect )
            eUIState = UPDATESTATE_UPDATE_AVAIL;
        else
            eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
    }
    
    return eUIState;
}

//------------------------------------------------------------------------------

void 
UpdateCheck::showReleaseNote(const rtl::OUString& rURL) const
{
    const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
        createService( UNISTRING( "com.sun.star.system.SystemShellExecute" ), m_xContext ),
        uno::UNO_QUERY );
    
    try {
        
        if( xShellExecute.is() )
            xShellExecute->execute(rURL, rtl::OUString(), c3s::SystemShellExecuteFlags::DEFAULTS);
    } catch(c3s::SystemShellExecuteException&) {
    }
}

//------------------------------------------------------------------------------

bool 
UpdateCheck::storeReleaseNote(sal_Int8 nNum, const rtl::OUString &rURL)
{
    osl::FileBase::RC rc;
    rtl::OUString aTargetDir( UpdateCheckConfig::getAllUsersDirectory() + UNISTRING( "/sun" ) );

    rc = osl::Directory::createPath( aTargetDir );

    rtl::OUString aFileName = UNISTRING("releasenote") +
                              rtl::OUString::valueOf( (sal_Int32) nNum ) +
                              UNISTRING(".url");

    rtl::OUString aFilePath;
    rc = osl::FileBase::getAbsoluteFileURL( aTargetDir, aFileName, aFilePath );
    if ( rc != osl::FileBase::E_None ) return false;

    rc = osl::File::remove( aFilePath );

    // don't store empty release notes, but delete old ones
    if ( rURL.getLength() == 0 )
        return true;

    osl::File aFile( aFilePath );
    rc = aFile.open( OpenFlag_Write | OpenFlag_Create );

    if ( rc != osl::FileBase::E_None ) return false;

    rtl::OString aLineBuf("[InternetShortcut]\r\n");
    sal_uInt64 nWritten = 0;

    rtl::OUString aURL( rURL );
#ifdef WNT
    rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
    if ( rc != osl::FileBase::E_None ) return false;
    aURL = UNISTRING("URL=") + rURL;
#endif
    aLineBuf = rtl::OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 );
    rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
    if ( rc != osl::FileBase::E_None ) return false;

    aFile.close();
    return true;
}

//------------------------------------------------------------------------------
void UpdateCheck::showExtensionDialog()
{
    rtl::OUString sServiceName = UNISTRING("com.sun.star.deployment.ui.PackageManagerDialog");
    rtl::OUString sArguments = UNISTRING("SHOW_UPDATE_DIALOG");
    uno::Reference< uno::XInterface > xService;

    if( ! m_xContext.is() )
        throw uno::RuntimeException( 
            UNISTRING( "UpdateCheck::showExtensionDialog(): empty component context" ), uno::Reference< uno::XInterface > () );

    uno::Reference< lang::XMultiComponentFactory > xServiceManager( m_xContext->getServiceManager() );
    if( !xServiceManager.is() )
        throw uno::RuntimeException( 
            UNISTRING( "UpdateCheck::showExtensionDialog(): unable to obtain service manager from component context" ), uno::Reference< uno::XInterface > () );

    xService = xServiceManager->createInstanceWithContext( sServiceName, m_xContext );
    uno::Reference< task::XJobExecutor > xExecuteable( xService, uno::UNO_QUERY );
    if ( xExecuteable.is() )
        xExecuteable->trigger( sArguments );
}

//------------------------------------------------------------------------------

rtl::Reference<UpdateHandler>
UpdateCheck::getUpdateHandler()
{
    osl::MutexGuard aGuard(m_aMutex);

    if( ! m_aUpdateHandler.is() )
        m_aUpdateHandler = new UpdateHandler(m_xContext, this);

    return m_aUpdateHandler;
}

//------------------------------------------------------------------------------

uno::Reference< task::XInteractionHandler > 
UpdateCheck::getInteractionHandler() const
{
    osl::MutexGuard aGuard(m_aMutex);

    uno::Reference< task::XInteractionHandler > xHandler;

    if( m_aUpdateHandler.is() && m_aUpdateHandler->isVisible() )
        xHandler = m_aUpdateHandler.get();

    return xHandler;
}

//------------------------------------------------------------------------------

uno::Reference< uno::XInterface > 
UpdateCheck::createService(const rtl::OUString& rServiceName, 
                           const uno::Reference<uno::XComponentContext>& xContext)
{
    if( !xContext.is() )
        throw uno::RuntimeException( 
            UNISTRING( "UpdateCheckConfig: empty component context" ), 
            uno::Reference< uno::XInterface >() );
        
    const uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
    
    if( !xServiceManager.is() )
        throw uno::RuntimeException( 
            UNISTRING( "UpdateCheckConfig: unable to obtain service manager from component context" ), 
            uno::Reference< uno::XInterface >() );
    
    return xServiceManager->createInstanceWithContext(rServiceName, xContext);
}

//------------------------------------------------------------------------------

bool 
UpdateCheck::isDialogShowing() const
{ 
    osl::MutexGuard aGuard(m_aMutex); 
    return sal_True == m_aUpdateHandler.is() && m_aUpdateHandler->isVisible();
};

//------------------------------------------------------------------------------

void 
UpdateCheck::autoCheckStatusChanged(bool enabled)
{ 
    osl::ClearableMutexGuard aGuard(m_aMutex);
    
    if( (CHECK_SCHEDULED == m_eState) && !enabled )
        shutdownThread(false);
        
    if( (DISABLED == m_eState) || (CHECK_SCHEDULED == m_eState) )
    {
        enableAutoCheck(enabled);
        UpdateState eState = getUIState(m_aUpdateInfo);
        aGuard.clear();
        setUIState(eState);
    }
};

//------------------------------------------------------------------------------

void 
UpdateCheck::autoCheckIntervalChanged()
{ 
    // just wake-up
    m_aCondition.set();
};

//------------------------------------------------------------------------------

oslInterlockedCount SAL_CALL 
UpdateCheck::acquire() SAL_THROW(())
{ 
    return ReferenceObject::acquire(); 
}

//------------------------------------------------------------------------------

oslInterlockedCount SAL_CALL 
UpdateCheck::release() SAL_THROW(())
{ 
    return ReferenceObject::release(); 
}
