diff options
Diffstat (limited to 'extensions/source/update/check')
23 files changed, 6884 insertions, 0 deletions
diff --git a/extensions/source/update/check/Addons.xcu b/extensions/source/update/check/Addons.xcu new file mode 100644 index 000000000000..8794ce858073 --- /dev/null +++ b/extensions/source/update/check/Addons.xcu @@ -0,0 +1,26 @@ +<?xml version='1.0' encoding='UTF-8'?> + +<oor:component-data oor:name="Addons" oor:package="org.openoffice.Office" xmlns:install="http://openoffice.org/2004/installation" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <node oor:name="AddonUI"> + <node oor:name="OfficeHelp"> + <node oor:name="UpdateCheckJob" oor:op="replace"> + <prop oor:name="URL" oor:type="xs:string"> + <value>vnd.sun.star.job:alias=UpdateCheck</value> + </prop> + <prop oor:name="ImageIdentifier" oor:type="xs:string"> + <value/> + </prop> + <prop oor:name="Title" oor:type="xs:string"> + <value xml:lang="en-US">Check for ~Updates...</value> + </prop> + <prop oor:name="Target" oor:type="xs:string"> + <value>_self</value> + </prop> + <prop oor:name="Context" oor:type="xs:string"> + <value/> + </prop> + </node> + </node> + </node> +</oor:component-data> + diff --git a/extensions/source/update/check/Jobs.xcu b/extensions/source/update/check/Jobs.xcu new file mode 100644 index 000000000000..ed324650c66f --- /dev/null +++ b/extensions/source/update/check/Jobs.xcu @@ -0,0 +1,42 @@ +<?xml version='1.0' encoding='UTF-8'?> + +<oor:component-data oor:name="Jobs" oor:package="org.openoffice.Office" xmlns:install="http://openoffice.org/2004/installation" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <node oor:name="Jobs"> + <node oor:name="UpdateCheck" oor:op="replace"> + <prop oor:name="Service"> + <value>com.sun.star.setup.UpdateCheck</value> + </prop> + <node oor:name="Arguments"> + <prop oor:name="AutoCheckEnabled" oor:type="xs:boolean" oor:op="replace"> + <value>false</value> + </prop> + <prop oor:name="LastCheck" oor:type="xs:long" oor:op="replace"> + <value>0</value> + </prop> + <prop oor:name="CheckInterval" oor:type="xs:long" oor:op="replace"> + <value>604800</value> + </prop> + <prop oor:name="DownloadDestination" oor:type="xs:string" oor:op="replace"> + <value></value> + </prop> + <prop oor:name="AutoDownloadEnabled" oor:type="xs:boolean" oor:op="replace"> + <value>false</value> + </prop> + <prop oor:name="DownloadSupported" oor:type="xs:boolean" oor:op="replace"> + <value>true</value> + </prop> + <prop oor:name="DownloadPaused" oor:type="xs:boolean" oor:op="replace"> + <value>false</value> + </prop> + </node> + </node> + </node> + <node oor:name="Events"> + <node oor:name="onFirstVisibleTask" oor:op="fuse"> + <node oor:name="JobList"> + <node oor:name="UpdateCheck" oor:op="replace"/> + </node> + </node> + </node> +</oor:component-data> + diff --git a/extensions/source/update/check/actionlistener.hxx b/extensions/source/update/check/actionlistener.hxx new file mode 100644 index 000000000000..6d90357aef85 --- /dev/null +++ b/extensions/source/update/check/actionlistener.hxx @@ -0,0 +1,46 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef INCLUDED_ACTION_LISTENER_HXX +#define INCLUDED_ACTION_LISTENER_HXX + +#include <rtl/ref.hxx> + +class IActionListener : public rtl::IReference +{ + public: + + virtual void cancel() = 0; + virtual void download() = 0; + virtual void install() = 0; + virtual void pause() = 0; + virtual void resume() = 0; + virtual void closeAfterFailure() = 0; +}; + +#endif //INCLUDED_ACTION_LISTENER_HXX + diff --git a/extensions/source/update/check/download.cxx b/extensions/source/update/check/download.cxx new file mode 100644 index 000000000000..09d90f1eaa83 --- /dev/null +++ b/extensions/source/update/check/download.cxx @@ -0,0 +1,460 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" + +#if defined WNT +#ifdef _MSC_VER +#pragma warning(push, 1) /* disable warnings within system headers */ +#endif +#include <curl/curl.h> +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#else +#include <curl/curl.h> +#endif +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include "download.hxx" + +namespace beans = com::sun::star::beans ; +namespace container = com::sun::star::container ; +namespace lang = com::sun::star::lang ; +namespace uno = com::sun::star::uno ; + +#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s)) + + +struct OutData +{ + rtl::Reference< DownloadInteractionHandler >Handler; + rtl::OUString File; + rtl::OUString DestinationDir; + oslFileHandle FileHandle; + sal_uInt64 Offset; + osl::Condition& StopCondition; + CURL *curl; + + OutData(osl::Condition& rCondition) : FileHandle(NULL), Offset(0), StopCondition(rCondition), curl(NULL) {}; +}; + +//------------------------------------------------------------------------------ + +static void openFile( OutData& out ) +{ + char * effective_url; + curl_easy_getinfo(out.curl, CURLINFO_EFFECTIVE_URL, &effective_url); + + double fDownloadSize; + curl_easy_getinfo(out.curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fDownloadSize); + + rtl::OString aURL(effective_url); + + // ensure no trailing '/' + sal_Int32 nLen = aURL.getLength(); + while( (nLen > 0) && ('/' == aURL[nLen-1]) ) + aURL = aURL.copy(0, --nLen); + + // extract file name last '/' + sal_Int32 nIndex = aURL.lastIndexOf('/'); + if( nIndex > 0 ) + { + out.File = out.DestinationDir + rtl::OStringToOUString(aURL.copy(nIndex), RTL_TEXTENCODING_UTF8); + + oslFileError rc; + + // Give the user an overwrite warning if the target file exists + const sal_Int32 openFlags = osl_File_OpenFlag_Write | osl_File_OpenFlag_Create; + do + { + rc = osl_openFile(out.File.pData, &out.FileHandle, openFlags); + + if( osl_File_E_EXIST == rc && ! out.Handler->downloadTargetExists(out.File) ) + { + out.StopCondition.set(); + break; + } + + } while( osl_File_E_EXIST == rc ); + + if( osl_File_E_None == rc ) + out.Handler->downloadStarted(out.File, (sal_Int64) fDownloadSize); + } +} + +//------------------------------------------------------------------------------ + +static inline rtl::OString +getStringValue(const uno::Reference< container::XNameAccess >& xNameAccess, const rtl::OUString& aName) +{ + rtl::OString aRet; + + OSL_ASSERT(xNameAccess->hasByName(aName)); + uno::Any aValue = xNameAccess->getByName(aName); + + return rtl::OUStringToOString(aValue.get<rtl::OUString>(), RTL_TEXTENCODING_UTF8); +} + +//------------------------------------------------------------------------------ + +static inline sal_Int32 +getInt32Value(const uno::Reference< container::XNameAccess >& xNameAccess, + const rtl::OUString& aName, sal_Int32 nDefault=-1) +{ + OSL_ASSERT(xNameAccess->hasByName(aName)); + uno::Any aValue = xNameAccess->getByName(aName); + + sal_Int32 n=nDefault; + aValue >>= n; + return n; +} + +//------------------------------------------------------------------------------ + +static size_t +write_function( void *ptr, size_t size, size_t nmemb, void *stream ) +{ + OutData *out = reinterpret_cast < OutData * > (stream); + + if( NULL == out->FileHandle ) + openFile(*out); + + sal_uInt64 nBytesWritten = 0; + + if( NULL != out->FileHandle ) + osl_writeFile(out->FileHandle, ptr, size * nmemb, &nBytesWritten); + + return (size_t) nBytesWritten; +} + +//------------------------------------------------------------------------------ + +static int +progress_callback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow ) +{ + (void) ultotal; + (void) ulnow; + + OutData *out = reinterpret_cast < OutData * > (clientp); + + OSL_ASSERT( out ); + + if( ! out->StopCondition.check() ) + { + double fPercent = 0; + if ( dltotal + out->Offset ) + fPercent = (dlnow + out->Offset) * 100 / (dltotal + out->Offset); + if( fPercent < 0 ) + fPercent = 0; + + // Do not report progress for redirection replies + long nCode; + curl_easy_getinfo(out->curl, CURLINFO_RESPONSE_CODE, &nCode); + if( (nCode != 302) && (nCode != 303) && (dltotal > 0) ) + out->Handler->downloadProgressAt((sal_Int8)fPercent); + + return 0; + } + + // If stop condition is set, return non 0 value to abort + return -1; +} + +//------------------------------------------------------------------------------ + +void +Download::getProxyForURL(const rtl::OUString& rURL, rtl::OString& rHost, sal_Int32& rPort) const +{ + if( !m_xContext.is() ) + throw uno::RuntimeException( + UNISTRING( "Download: empty component context" ), + uno::Reference< uno::XInterface >() ); + + uno::Reference< lang::XMultiComponentFactory > xServiceManager(m_xContext->getServiceManager()); + + if( !xServiceManager.is() ) + throw uno::RuntimeException( + UNISTRING( "Download: unable to obtain service manager from component context" ), + uno::Reference< uno::XInterface >() ); + + uno::Reference< lang::XMultiServiceFactory > xConfigProvider( + xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.configuration.ConfigurationProvider" ), m_xContext ), + uno::UNO_QUERY_THROW); + + beans::PropertyValue aProperty; + aProperty.Name = UNISTRING( "nodepath" ); + aProperty.Value = uno::makeAny( UNISTRING("org.openoffice.Inet/Settings") ); + + uno::Sequence< uno::Any > aArgumentList( 1 ); + aArgumentList[0] = uno::makeAny( aProperty ); + + uno::Reference< container::XNameAccess > xNameAccess( + xConfigProvider->createInstanceWithArguments( + UNISTRING("com.sun.star.configuration.ConfigurationAccess"), aArgumentList ), + uno::UNO_QUERY_THROW ); + + OSL_ASSERT(xNameAccess->hasByName(UNISTRING("ooInetProxyType"))); + uno::Any aValue = xNameAccess->getByName(UNISTRING("ooInetProxyType")); + + sal_Int32 nProxyType = aValue.get< sal_Int32 >(); + if( 0 != nProxyType ) // type 0 means "direct connection to the internet + { + if( rURL.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("http:")) ) + { + rHost = getStringValue(xNameAccess, UNISTRING("ooInetHTTPProxyName")); + rPort = getInt32Value(xNameAccess, UNISTRING("ooInetHTTPProxyPort")); + } + else if( rURL.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("https:")) ) + { + rHost = getStringValue(xNameAccess, UNISTRING("ooInetHTTPSProxyName")); + rPort = getInt32Value(xNameAccess, UNISTRING("ooInetHTTPSProxyPort")); + } + else if( rURL.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("ftp:")) ) + { + rHost = getStringValue(xNameAccess, UNISTRING("ooInetFTPProxyName")); + rPort = getInt32Value(xNameAccess, UNISTRING("ooInetFTPProxyPort")); + } + } +} + +//------------------------------------------------------------------------------ + +bool curl_run(const rtl::OUString& rURL, OutData& out, const rtl::OString& aProxyHost, sal_Int32 nProxyPort) +{ + /* Need to investigate further whether it is necessary to call + * curl_global_init or not - leave it for now (as the ftp UCB content + * provider does as well). + */ + + CURL * pCURL = curl_easy_init(); + bool ret = false; + + if( NULL != pCURL ) + { + out.curl = pCURL; + + rtl::OString aURL(rtl::OUStringToOString(rURL, RTL_TEXTENCODING_UTF8)); + curl_easy_setopt(pCURL, CURLOPT_URL, aURL.getStr()); + + // abort on http errors + curl_easy_setopt(pCURL, CURLOPT_FAILONERROR, 1); + + // enable redirection + curl_easy_setopt(pCURL, CURLOPT_FOLLOWLOCATION, 1); + + // write function + curl_easy_setopt(pCURL, CURLOPT_WRITEDATA, &out); + curl_easy_setopt(pCURL, CURLOPT_WRITEFUNCTION, &write_function); + + // progress handler - Condition::check unfortunatly is not defined const + curl_easy_setopt(pCURL, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(pCURL, CURLOPT_PROGRESSFUNCTION, &progress_callback); + curl_easy_setopt(pCURL, CURLOPT_PROGRESSDATA, &out); + + // proxy + curl_easy_setopt(pCURL, CURLOPT_PROXY, aProxyHost.getStr()); + curl_easy_setopt(pCURL, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + if( -1 != nProxyPort ) + curl_easy_setopt(pCURL, CURLOPT_PROXYPORT, nProxyPort); + + if( out.Offset > 0 ) + { + // curl_off_t offset = nOffset; libcurl seems to be compiled with large + // file support (and we not) .. + sal_Int64 offset = (sal_Int64) out.Offset; + curl_easy_setopt(pCURL, CURLOPT_RESUME_FROM_LARGE, offset); + } + + CURLcode cc = curl_easy_perform(pCURL); + + // treat zero byte downloads as errors + if( NULL == out.FileHandle ) + openFile(out); + + if( CURLE_OK == cc ) + { + out.Handler->downloadFinished(out.File); + ret = true; + } + + if ( CURLE_PARTIAL_FILE == cc ) + { + // this sometimes happens, when a user throws away his user data, but has already + // completed the download of an update. + double fDownloadSize; + curl_easy_getinfo( pCURL, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fDownloadSize ); + if ( -1 == fDownloadSize ) + { + out.Handler->downloadFinished(out.File); + ret = true; + } + } + + // Avoid target file being removed + else if( (CURLE_ABORTED_BY_CALLBACK == cc) || out.StopCondition.check() ) + ret = true; + + // Only report errors when not stopped + else + { + rtl::OString aMessage(RTL_CONSTASCII_STRINGPARAM("Unknown error")); + + const char * error_message = curl_easy_strerror(cc); + if( NULL != error_message ) + aMessage = error_message; + + if ( CURLE_HTTP_RETURNED_ERROR == cc ) + { + long nError; + curl_easy_getinfo( pCURL, CURLINFO_RESPONSE_CODE, &nError ); + + if ( 403 == nError ) + aMessage += rtl::OString( RTL_CONSTASCII_STRINGPARAM( " 403: Access denied!" ) ); + else if ( 404 == nError ) + aMessage += rtl::OString( RTL_CONSTASCII_STRINGPARAM( " 404: File not found!" ) ); + else if ( 416 == nError ) + { + // we got this error probably, because we already downloaded the file + out.Handler->downloadFinished(out.File); + ret = true; + } + else + { + aMessage += rtl::OString( RTL_CONSTASCII_STRINGPARAM( ":error code = " ) ); + aMessage += aMessage.valueOf( nError ); + aMessage += rtl::OString( RTL_CONSTASCII_STRINGPARAM( " !" ) ); + } + } + if ( !ret ) + out.Handler->downloadStalled( rtl::OStringToOUString(aMessage, RTL_TEXTENCODING_UTF8) ); + } + + curl_easy_cleanup(pCURL); + } + + return ret; +} + +//------------------------------------------------------------------------------ + +bool +Download::start(const rtl::OUString& rURL, const rtl::OUString& rFile, const rtl::OUString& rDestinationDir) +{ + OSL_ASSERT( m_aHandler.is() ); + + OutData out(m_aCondition); + rtl::OUString aFile( rFile ); + + // when rFile is empty, there is no remembered file name. If there is already a file with the + // same name ask the user if she wants to resume a download or restart the download + if ( !aFile.getLength() ) + { + // GetFileName() + rtl::OUString aURL( rURL ); + // ensure no trailing '/' + sal_Int32 nLen = aURL.getLength(); + while( (nLen > 0) && ('/' == aURL[ nLen-1 ]) ) + aURL = aURL.copy( 0, --nLen ); + + // extract file name last '/' + sal_Int32 nIndex = aURL.lastIndexOf('/'); + aFile = rDestinationDir + aURL.copy( nIndex ); + + // check for existing file + oslFileError rc = osl_openFile( aFile.pData, &out.FileHandle, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); + osl_closeFile(out.FileHandle); + out.FileHandle = NULL; + + if( osl_File_E_EXIST == rc ) + { + if ( m_aHandler->checkDownloadDestination( aURL.copy( nIndex+1 ) ) ) + { + osl_removeFile( aFile.pData ); + aFile = rtl::OUString(); + } + else + m_aHandler->downloadStarted( aFile, 0 ); + } + else + { + osl_removeFile( aFile.pData ); + aFile = rtl::OUString(); + } + } + + out.File = aFile; + out.DestinationDir = rDestinationDir; + out.Handler = m_aHandler; + + if( aFile.getLength() > 0 ) + { + oslFileError rc = osl_openFile(aFile.pData, &out.FileHandle, osl_File_OpenFlag_Write); + + if( osl_File_E_None == rc ) + { + // Set file pointer to the end of the file on resume + if( osl_File_E_None == osl_setFilePos(out.FileHandle, osl_Pos_End, 0) ) + { + osl_getFilePos(out.FileHandle, &out.Offset); + } + } + else if( osl_File_E_NOENT == rc ) // file has been deleted meanwhile .. + out.File = rtl::OUString(); + } + + rtl::OString aProxyHost; + sal_Int32 nProxyPort = -1; + getProxyForURL(rURL, aProxyHost, nProxyPort); + + bool ret = curl_run(rURL, out, aProxyHost, nProxyPort); + + if( NULL != out.FileHandle ) + { + osl_syncFile(out.FileHandle); + osl_closeFile(out.FileHandle); + +// #i90930# Don't remove already downloaded bits, when curl_run reports an error +// because later calls might be successful +// if( ! ret ) +// osl_removeFile(out.File.pData); + } + + m_aCondition.reset(); + return ret; +} + +//------------------------------------------------------------------------------ + +void +Download::stop() +{ + m_aCondition.set(); +} diff --git a/extensions/source/update/check/download.hxx b/extensions/source/update/check/download.hxx new file mode 100644 index 000000000000..ff16ef52f7fb --- /dev/null +++ b/extensions/source/update/check/download.hxx @@ -0,0 +1,84 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <osl/conditn.hxx> +#include <osl/file.h> + +struct DownloadInteractionHandler : public rtl::IReference +{ + virtual bool checkDownloadDestination(const rtl::OUString& rFileName) = 0; + + // called if the destination file already exists, but resume is false + virtual bool downloadTargetExists(const rtl::OUString& rFileName) = 0; + + // called when curl reports an error + virtual void downloadStalled(const rtl::OUString& rErrorMessage) = 0; + + // progress handler + virtual void downloadProgressAt(sal_Int8 nPercent) = 0; + + // called on first progress notification + virtual void downloadStarted(const rtl::OUString& rFileName, sal_Int64 nFileSize) = 0; + + // called when download has been finished + virtual void downloadFinished(const rtl::OUString& rFileName) = 0; +}; + + +class Download +{ +public: + Download(const com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext >& xContext, + const rtl::Reference< DownloadInteractionHandler >& rHandler) : m_xContext(xContext), m_aHandler(rHandler) {}; + + // returns true when the content of rURL was successfully written to rLocalFile + bool start(const rtl::OUString& rURL, const rtl::OUString& rFile, const rtl::OUString& rDestinationDir); + + // stops the download after the next write operation + void stop(); + + // returns true if the stop condition is set + bool isStopped() const + { return sal_True == const_cast <Download *> (this)->m_aCondition.check(); }; + +protected: + + // Determines the appropriate proxy settings for the given URL. Returns true if a proxy should be used + void getProxyForURL(const rtl::OUString& rURL, rtl::OString& rHost, sal_Int32& rPort) const; + +private: + osl::Condition m_aCondition; + const com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext >& m_xContext; + const rtl::Reference< DownloadInteractionHandler > m_aHandler; +}; + + diff --git a/extensions/source/update/check/makefile.mk b/extensions/source/update/check/makefile.mk new file mode 100644 index 000000000000..47da869ea11e --- /dev/null +++ b/extensions/source/update/check/makefile.mk @@ -0,0 +1,142 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ=..$/..$/.. + +PRJNAME=extensions +TARGET=updchk +PACKAGE=org.openoffice.Office + +LIBTARGET=NO +ENABLE_EXCEPTIONS=TRUE + +ABSXCSROOT=$(SOLARXMLDIR) +XCSROOT=.. +DTDDIR=$(ABSXCSROOT) +XSLDIR=$(ABSXCSROOT)$/processing +PROCESSOUT=$(MISC)$/$(TARGET) +PROCESSORDIR=$(SOLARBINDIR) + +# no validation by inspector class +NO_INSPECTION=TRUE + +# --- Settings --- + +.INCLUDE : settings.mk + +# no "lib" prefix +DLLPRE = + +# --- Files --- + +SRS2NAME=$(TARGET) +SRC2FILES=\ + updatehdl.src + +RESLIB2NAME=upd +RESLIB2SRSFILES= $(SRS)$/updchk.srs +RESLIB2DEPN= updatehdl.src updatehdl.hrc + +SLOFILES=\ + $(SLO)$/download.obj \ + $(SLO)$/updatecheck.obj \ + $(SLO)$/updatecheckjob.obj \ + $(SLO)$/updatecheckconfig.obj \ + $(SLO)$/updateprotocol.obj \ + $(SLO)$/updatehdl.obj + +SHL1NOCHECK=TRUE +SHL1TARGET=$(TARGET).uno +SHL1OBJS=$(SLOFILES) +SHL1DEF=$(MISC)$/$(SHL1TARGET).def + +SHL1IMPLIB=i$(SHL1TARGET) +SHL1STDLIBS= \ + $(CPPUHELPERLIB) \ + $(CPPULIB) \ + $(CURLLIB) \ + $(SALLIB) \ + $(SHELL32LIB) \ + $(OLE32LIB) + +SHL1VERSIONMAP=$(SOLARENV)/src/component.map +SHL1DEF=$(MISC)$/$(SHL1TARGET).def +DEF1NAME=$(SHL1TARGET) + +PACKAGEDIR=$(subst,.,$/ $(PACKAGE)) +SPOOLDIR=$(MISC)$/registry$/spool + +XCUFILES= \ + Addons.xcu \ + Jobs.xcu + +MYXCUFILES= \ + $(SPOOLDIR)$/$(PACKAGEDIR)$/Addons$/Addons-onlineupdate.xcu \ + $(SPOOLDIR)$/$(PACKAGEDIR)$/Jobs$/Jobs-onlineupdate.xcu + +LOCALIZEDFILES= \ + Addons.xcu \ + Jobs.xcu + +.IF "$(test)" != "" +APP1TARGET=updateprotocoltest +APP1STDLIBS= $(SHL1STDLIBS) +APP1OBJS= \ + $(SLO)$/updateprotocol.obj \ + $(SLO)$/updateprotocoltest.obj + + +.ENDIF # "$(test)" != "" + +# --- Targets --- + +.INCLUDE : target.mk + +ALLTAR : $(MYXCUFILES) + +.IF "$(WITH_LANG)"!="" +XCU_SOURCEDIR:=$(PROCESSOUT)$/merge$/$(PACKAGEDIR) +.ELSE # "$(WITH_LANG)"!="" +XCU_SOURCEDIR:=. +.ENDIF # "$(WITH_LANG)"!="" + +$(SPOOLDIR)$/$(PACKAGEDIR)$/Addons$/Addons-onlineupdate.xcu : $(XCU_SOURCEDIR)$/Addons.xcu + @-$(MKDIRHIER) $(@:d) + @$(COPY) $< $@ + +$(SPOOLDIR)$/$(PACKAGEDIR)$/Jobs$/Jobs-onlineupdate.xcu : $(XCU_SOURCEDIR)$/Jobs.xcu + @-$(MKDIRHIER) $(@:d) + @$(COPY) $< $@ +# @$(PERL) transform.pl < $< > $@ + + +ALLTAR : $(MISC)/updchk.uno.component + +$(MISC)/updchk.uno.component .ERRREMOVE : $(SOLARENV)/bin/createcomponent.xslt \ + updchk.uno.component + $(XSLTPROC) --nonet --stringparam uri \ + '$(COMPONENTPREFIX_BASIS_NATIVE)$(SHL1TARGETN:f)' -o $@ \ + $(SOLARENV)/bin/createcomponent.xslt updchk.uno.component diff --git a/extensions/source/update/check/transform.pl b/extensions/source/update/check/transform.pl new file mode 100644 index 000000000000..fb452ae0482c --- /dev/null +++ b/extensions/source/update/check/transform.pl @@ -0,0 +1,61 @@ +: +eval 'exec perl -wS $0 ${1+"$@"}' + if 0; + +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +while (<>) { + + if( /<prop/ ) { + $prop = $_; + next if( /Text/ || /Bubble/ ); + } + + if( /<\/prop>/ ) { + next if( $prop =~ /Text/ || $prop =~ /Bubble/ ); + } + + if( /xml:lang/ ) { + $lang = $_; + $lang =~ s/.*xml:lang=\"//; + $lang =~ s/\".*//; + chomp $lang; + $line = $prop; + $line =~ s/\" oor:type/_$lang\" oor:type/; + print $line; + $line = $_; + $line =~ s/ xml:lang=\".*\"//; + print $line; + $line = $prop; + $line =~ s/<prop.*/<\/prop>/; + print $line; + next; + } + + print; +}
\ No newline at end of file diff --git a/extensions/source/update/check/updatecheck.cxx b/extensions/source/update/check/updatecheck.cxx new file mode 100644 index 000000000000..8419569bd949 --- /dev/null +++ b/extensions/source/update/check/updatecheck.cxx @@ -0,0 +1,1690 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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(); +} diff --git a/extensions/source/update/check/updatecheck.hxx b/extensions/source/update/check/updatecheck.hxx new file mode 100644 index 000000000000..7283288769e0 --- /dev/null +++ b/extensions/source/update/check/updatecheck.hxx @@ -0,0 +1,194 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <osl/conditn.hxx> +#include <osl/thread.hxx> +#include <rtl/instance.hxx> +#include <salhelper/refobj.hxx> + +#include "updateinfo.hxx" +#include "updatecheckconfiglistener.hxx" +#include "actionlistener.hxx" +#include "updatehdl.hxx" +#include "download.hxx" + + +class UpdateCheck; +class UpdateCheckConfig; + +class UpdateCheckInitData { + +public: + inline rtl::Reference< UpdateCheck > SAL_CALL operator() () const; +}; + +class WorkerThread : public osl::Thread +{ +public: + virtual void SAL_CALL cancel() = 0; +}; + +class UpdateCheck : + public UpdateCheckConfigListener, + public IActionListener, + public DownloadInteractionHandler, + public salhelper::ReferenceObject, + public rtl::StaticWithInit< rtl::Reference< UpdateCheck >, UpdateCheckInitData > +{ + UpdateCheck() : m_eState(NOT_INITIALIZED), m_eUpdateState(UPDATESTATES_COUNT), m_pThread(NULL) {}; + +public: + inline SAL_CALL operator rtl::Reference< UpdateCheckConfigListener > () + { return static_cast< UpdateCheckConfigListener * > (this); } + + void initialize(const com::sun::star::uno::Sequence<com::sun::star::beans::NamedValue>& rValues, + const com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext>& xContext); + + /* Returns an instance of the specified service obtained from the specified + * component context + */ + + static com::sun::star::uno::Reference< com::sun::star::uno::XInterface > createService( + const rtl::OUString& aServiceName, + const com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext>& xContext); + + // Update internal update info member + void setUpdateInfo(const UpdateInfo& aInfo); + + /* This method turns on the menubar icon, triggers the bubble window or + * updates the dialog text when appropriate + */ + void setUIState(UpdateState eState, bool suppressBubble = false); + + // Returns the UI state that matches rInfo best + static UpdateState getUIState(const UpdateInfo& rInfo); + + // Check for updates failed + void setCheckFailedState(); + + // Executes the update check dialog for manual checks and downloads interaction + void showDialog(bool forceCheck = false); + + // Returns true if the update dialog is currently showing + bool isDialogShowing() const; + bool shouldShowExtUpdDlg() const { return ( m_bShowExtUpdDlg && m_bHasExtensionUpdate ); } + void showExtensionDialog(); + void setHasExtensionUpdates( bool bHasUpdates ) { m_bHasExtensionUpdate = bHasUpdates; } + bool hasOfficeUpdate() const { return (m_aUpdateInfo.BuildId.getLength() > 0); } + + // DownloadInteractionHandler + virtual bool downloadTargetExists(const rtl::OUString& rFileName); + virtual void downloadStalled(const rtl::OUString& rErrorMessage); + virtual void downloadProgressAt(sal_Int8 nProcent); + virtual void downloadStarted(const rtl::OUString& rLocalFileName, sal_Int64 nFileSize); + virtual void downloadFinished(const rtl::OUString& rLocalFileName); + // checks if the download target already exists and asks user what to do next + virtual bool checkDownloadDestination( const rtl::OUString& rFile ); + + // Cancels the download action (and resumes checking if enabled) + void cancelDownload(); + + // Returns the XInteractionHandler of the UpdateHandler instance if present (and visible) + com::sun::star::uno::Reference< com::sun::star::task::XInteractionHandler > getInteractionHandler() const; + + // UpdateCheckConfigListener + virtual void autoCheckStatusChanged(bool enabled); + virtual void autoCheckIntervalChanged(); + + // IActionListener + void cancel(); + void download(); + void install(); + void pause(); + void resume(); + void closeAfterFailure(); + + // rtl::IReference + virtual oslInterlockedCount SAL_CALL acquire() SAL_THROW(()); + virtual oslInterlockedCount SAL_CALL release() SAL_THROW(()); + +private: + + // Schedules or cancels next automatic check for updates + void enableAutoCheck(bool enable); + + // Starts/resumes or stops a download + void enableDownload(bool enable, bool paused=false); + + // Shuts down the currently running thread + void shutdownThread(bool join); + + // Returns the update handler instance + rtl::Reference<UpdateHandler> getUpdateHandler(); + + // Open the given URL in a browser + void showReleaseNote(const rtl::OUString& rURL) const; + + // stores the release note url on disk to be used by setup app + static bool storeReleaseNote(sal_Int8 nNum, const rtl::OUString &rURL); + + /* This method turns on the menubar icon and triggers the bubble window + */ + void handleMenuBarUI( rtl::Reference< UpdateHandler > rUpdateHandler, + UpdateState& eState, bool suppressBubble ); + enum State { + NOT_INITIALIZED, + DISABLED, + CHECK_SCHEDULED, + DOWNLOADING, + DOWNLOAD_PAUSED + }; + + State m_eState; + UpdateState m_eUpdateState; + + mutable osl::Mutex m_aMutex; + WorkerThread *m_pThread; + osl::Condition m_aCondition; + + UpdateInfo m_aUpdateInfo; + rtl::OUString m_aImageName; + bool m_bHasExtensionUpdate; + bool m_bShowExtUpdDlg; + + rtl::Reference<UpdateHandler> m_aUpdateHandler; + com::sun::star::uno::Reference<com::sun::star::beans::XPropertySet> m_xMenuBarUI; + com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> m_xContext; + + friend class UpdateCheckInitData; +}; + +inline rtl::Reference< UpdateCheck > SAL_CALL +UpdateCheckInitData::operator() () const +{ + return rtl::Reference< UpdateCheck > (new UpdateCheck()); +} diff --git a/extensions/source/update/check/updatecheckconfig.cxx b/extensions/source/update/check/updatecheckconfig.cxx new file mode 100755 index 000000000000..cca69db6ff33 --- /dev/null +++ b/extensions/source/update/check/updatecheckconfig.cxx @@ -0,0 +1,810 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" + + +#include "updatecheckconfig.hxx" +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include <osl/security.hxx> +#include <osl/time.h> +#include <osl/file.hxx> + +#ifdef WNT +#ifdef _MSC_VER +#pragma warning(push,1) // disable warnings within system headers +#pragma warning(disable: 4917) +#endif +#include <shlobj.h> +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif + +namespace container = com::sun::star::container ; +namespace beans = com::sun::star::beans ; +namespace lang = com::sun::star::lang ; +namespace util = com::sun::star::util ; +namespace uno = com::sun::star::uno ; + +#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s)) + +#define LAST_CHECK "LastCheck" +#define VERSION_FOUND "UpdateVersionFound" +#define UPDATE_VERSION "UpdateVersion" +#define UPDATE_BUILDID "UpdateBuildId" +#define UPDATE_DESCRIPTION "UpdateDescription" +#define DOWNLOAD_URL "DownloadURL" +#define IS_DIRECT_DOWNLOAD "IsDirectDownload" +#define OLD_VERSION "UpdateFoundFor" +#define AUTOCHECK_ENABLED "AutoCheckEnabled" +#define AUTODOWNLOAD_ENABLED "AutoDownloadEnabled" +#define CHECK_INTERVAL "CheckInterval" +#define LOCAL_FILE "LocalFile" +#define DOWNLOAD_SIZE "DownloadSize" +#define DOWNLOAD_PAUSED "DownloadPaused" +#define DOWNLOAD_DESTINATION "DownloadDestination" +#define RELEASE_NOTE "ReleaseNote" +#define EXTENSION_PREFIX "Extension_" + +#define PROPERTY_VERSION UNISTRING("Version") + +static const sal_Char * const aUpdateEntryProperties[] = { + UPDATE_VERSION, + UPDATE_BUILDID, + UPDATE_DESCRIPTION, + DOWNLOAD_URL, + IS_DIRECT_DOWNLOAD, + RELEASE_NOTE"1", + RELEASE_NOTE"2", + RELEASE_NOTE"3", + RELEASE_NOTE"4", + RELEASE_NOTE"5", + OLD_VERSION +}; + +static const sal_uInt32 nUpdateEntryProperties = sizeof(aUpdateEntryProperties) / sizeof(sal_Char *); + +//------------------------------------------------------------------------------ + +NamedValueByNameAccess::~NamedValueByNameAccess() +{ +} + +//------------------------------------------------------------------------------ + +::com::sun::star::uno::Any +NamedValueByNameAccess::getValue(const sal_Char * pName) +{ + const sal_Int32 nLen = m_rValues.getLength(); + for( sal_Int32 n=0; n < nLen; ++n ) + { + if( m_rValues[n].Name.equalsAscii( pName ) ) + return m_rValues[n].Value; + } + return ::com::sun::star::uno::Any(); +} + + +//------------------------------------------------------------------------------ + +bool +UpdateCheckROModel::isAutoCheckEnabled() const +{ + return sal_True == m_aNameAccess.getValue(AUTOCHECK_ENABLED).get< sal_Bool >(); +} + +//------------------------------------------------------------------------------ + +bool +UpdateCheckROModel::isDownloadPaused() const +{ + return sal_True == m_aNameAccess.getValue(DOWNLOAD_PAUSED).get< sal_Bool >(); +} + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckROModel::getStringValue(const sal_Char * pStr) const +{ + uno::Any aAny( m_aNameAccess.getValue(pStr) ); + rtl::OUString aRet; + + aAny >>= aRet; + + return aRet; +} + +//------------------------------------------------------------------------------ + +rtl::OUString UpdateCheckROModel::getLocalFileName() const +{ + return getStringValue(LOCAL_FILE); +}; + +//------------------------------------------------------------------------------ + +sal_Int64 UpdateCheckROModel::getDownloadSize() const +{ + uno::Any aAny( m_aNameAccess.getValue(DOWNLOAD_SIZE) ); + sal_Int64 nRet = -1; + + aAny >>= nRet; + return nRet; +}; + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckROModel::getUpdateEntryVersion() const +{ + return getStringValue(OLD_VERSION); +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckROModel::getUpdateEntry(UpdateInfo& rInfo) const +{ + rInfo.BuildId = getStringValue(UPDATE_BUILDID); + rInfo.Version = getStringValue(UPDATE_VERSION); + rInfo.Description = getStringValue(UPDATE_DESCRIPTION); + + sal_Bool isDirectDownload = sal_False; + m_aNameAccess.getValue(IS_DIRECT_DOWNLOAD) >>= isDirectDownload; + + rInfo.Sources.push_back( DownloadSource( isDirectDownload, getStringValue(DOWNLOAD_URL) ) ); + + rtl::OString aStr(RELEASE_NOTE); + for(sal_Int32 n=1; n < 6; ++n ) + { + rtl::OUString aUStr = getStringValue(aStr + rtl::OString::valueOf(n)); + if( aUStr.getLength() > 0 ) + rInfo.ReleaseNotes.push_back(ReleaseNote((sal_Int8) n, aUStr)); + } +} + + +//------------------------------------------------------------------------------ + +rtl::OUString UpdateCheckConfig::getDesktopDirectory() +{ + rtl::OUString aRet; + +#ifdef WNT + WCHAR szPath[MAX_PATH]; + + if( ! FAILED( SHGetSpecialFolderPathW( NULL, szPath, CSIDL_DESKTOPDIRECTORY, true ) ) ) + { + aRet = rtl::OUString( reinterpret_cast< sal_Unicode * >(szPath) ); + osl::FileBase::getFileURLFromSystemPath( aRet, aRet ); + } +#else + // This should become a desktop specific setting in some system backend .. + rtl::OUString aHomeDir; + osl::Security().getHomeDir( aHomeDir ); + aRet = aHomeDir + rtl::OUString::createFromAscii("/Desktop"); + + // Set path to home directory when there is no /Desktop directory + osl::Directory aDocumentsDir( aRet ); + if( osl::FileBase::E_None != aDocumentsDir.open() ) + aRet = aHomeDir; +#endif + + return aRet; +} + +//------------------------------------------------------------------------------ + +rtl::OUString UpdateCheckConfig::getAllUsersDirectory() +{ + rtl::OUString aRet; + +#ifdef WNT + WCHAR szPath[MAX_PATH]; + + if( ! FAILED( SHGetSpecialFolderPathW( NULL, szPath, CSIDL_COMMON_DOCUMENTS, true ) ) ) + { + aRet = rtl::OUString( reinterpret_cast< sal_Unicode * >(szPath) ); + osl::FileBase::RC rc; + rc = osl::FileBase::getFileURLFromSystemPath( aRet, aRet ); + } +#else + osl::FileBase::getTempDirURL(aRet); +#endif + + return aRet; +} + +//------------------------------------------------------------------------------ +UpdateCheckConfig::UpdateCheckConfig( const uno::Reference<container::XNameContainer>& xContainer, + const uno::Reference<container::XNameContainer>& xAvailableUpdates, + const uno::Reference<container::XNameContainer>& xIgnoredUpdates, + const ::rtl::Reference< UpdateCheckConfigListener >& rListener ) : + m_xContainer( xContainer ), + m_xAvailableUpdates( xAvailableUpdates ), + m_xIgnoredUpdates( xIgnoredUpdates ), + m_rListener( rListener ) +{} + +//------------------------------------------------------------------------------ +UpdateCheckConfig::~UpdateCheckConfig() +{} + +//------------------------------------------------------------------------------ + +::rtl::Reference< UpdateCheckConfig > +UpdateCheckConfig::get( + const uno::Reference<uno::XComponentContext>& xContext, + const ::rtl::Reference< UpdateCheckConfigListener >& rListener) +{ + if( !xContext.is() ) + throw uno::RuntimeException( + UNISTRING( "UpdateCheckConfig: empty component context" ), + uno::Reference< uno::XInterface >() ); + + 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 >() ); + + uno::Reference< lang::XMultiServiceFactory > xConfigProvider( + xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.configuration.ConfigurationProvider" ), xContext ), + uno::UNO_QUERY_THROW); + + beans::PropertyValue aProperty; + aProperty.Name = UNISTRING( "nodepath" ); + aProperty.Value = uno::makeAny( UNISTRING("org.openoffice.Office.Jobs/Jobs/UpdateCheck/Arguments") ); + + uno::Sequence< uno::Any > aArgumentList( 1 ); + aArgumentList[0] = uno::makeAny( aProperty ); + + uno::Reference< container::XNameContainer > xContainer( + xConfigProvider->createInstanceWithArguments( + UNISTRING("com.sun.star.configuration.ConfigurationUpdateAccess"), aArgumentList ), + uno::UNO_QUERY_THROW ); + + aProperty.Value = uno::makeAny( UNISTRING("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates") ); + aArgumentList[0] = uno::makeAny( aProperty ); + uno::Reference< container::XNameContainer > xIgnoredExt( xConfigProvider->createInstanceWithArguments( UNISTRING("com.sun.star.configuration.ConfigurationUpdateAccess"), aArgumentList ), uno::UNO_QUERY_THROW ); + + aProperty.Value = uno::makeAny( UNISTRING("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/AvailableUpdates") ); + aArgumentList[0] = uno::makeAny( aProperty ); + uno::Reference< container::XNameContainer > xUpdateAvail( xConfigProvider->createInstanceWithArguments( UNISTRING("com.sun.star.configuration.ConfigurationUpdateAccess"), aArgumentList ), uno::UNO_QUERY_THROW ); + + return new UpdateCheckConfig( xContainer, xUpdateAvail, xIgnoredExt, rListener ); +} + +//------------------------------------------------------------------------------ + +bool +UpdateCheckConfig::isAutoCheckEnabled() const +{ + sal_Bool nValue = sal_False; + const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( AUTOCHECK_ENABLED ) ) >>= nValue; + return nValue; +} + +//------------------------------------------------------------------------------ + +bool +UpdateCheckConfig::isAutoDownloadEnabled() const +{ + sal_Bool nValue = sal_False; + const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( AUTODOWNLOAD_ENABLED ) ) >>= nValue; + return nValue; +} + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckConfig::getUpdateEntryVersion() const +{ + rtl::OUString aValue; + + // getByName is defined as non const in XNameAccess + const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( OLD_VERSION ) ) >>= aValue; + + return aValue; +} + +//------------------------------------------------------------------------------ + +sal_Int64 +UpdateCheckConfig::getLastChecked() const +{ + sal_Int64 nValue = 0; + + // getByName is defined as non const in XNameAccess + const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( LAST_CHECK ) ) >>= nValue; + + return nValue; +} + +//------------------------------------------------------------------------------ + +sal_Int64 +UpdateCheckConfig::getCheckInterval() const +{ + sal_Int64 nValue = 0; + + // getByName is defined as non const in XNameAccess + const_cast < UpdateCheckConfig *> (this)->getByName( UNISTRING( CHECK_INTERVAL ) ) >>= nValue; + + return nValue; +} + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckConfig::getLocalFileName() const +{ + rtl::OUString aName = UNISTRING(LOCAL_FILE); + rtl::OUString aRet; + + if( m_xContainer->hasByName(aName) ) + m_xContainer->getByName(aName) >>= aRet; + + return aRet; +} + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckConfig::getDownloadDestination() const +{ + rtl::OUString aName = UNISTRING(DOWNLOAD_DESTINATION); + rtl::OUString aRet; + + const_cast <UpdateCheckConfig *> (this)->getByName(aName) >>= aRet; + + return aRet; +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckConfig::storeLocalFileName(const rtl::OUString& rLocalFileName, sal_Int64 nFileSize) +{ + const sal_uInt8 nItems = 2; + const rtl::OUString aNameList[nItems] = { UNISTRING(LOCAL_FILE), UNISTRING(DOWNLOAD_SIZE) }; + const uno::Any aValueList[nItems] = { uno::makeAny(rLocalFileName), uno::makeAny(nFileSize) }; + + for( sal_uInt8 i=0; i < nItems; ++i ) + { + if( m_xContainer->hasByName(aNameList[i]) ) + m_xContainer->replaceByName(aNameList[i], aValueList[i]); + else + m_xContainer->insertByName(aNameList[i], aValueList[i]); + } + + commitChanges(); +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckConfig::clearLocalFileName() +{ + const sal_uInt8 nItems = 2; + const rtl::OUString aNameList[nItems] = { UNISTRING(LOCAL_FILE), UNISTRING(DOWNLOAD_SIZE) }; + + for( sal_uInt8 i=0; i < nItems; ++i ) + { + if( m_xContainer->hasByName(aNameList[i]) ) + m_xContainer->removeByName(aNameList[i]); + } + + commitChanges(); +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckConfig::storeDownloadPaused(bool paused) +{ + replaceByName(UNISTRING(DOWNLOAD_PAUSED) , uno::makeAny(paused)); + commitChanges(); +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckConfig::updateLastChecked() +{ + TimeValue systime; + osl_getSystemTime(&systime); + + sal_Int64 lastCheck = systime.Seconds; + + replaceByName(UNISTRING(LAST_CHECK), uno::makeAny(lastCheck)); +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckConfig::storeUpdateFound( const UpdateInfo& rInfo, const rtl::OUString& aCurrentBuild) + +{ + bool autoDownloadEnabled = isAutoDownloadEnabled(); + + uno::Any aValues[nUpdateEntryProperties] = + { + uno::makeAny(rInfo.Version), + uno::makeAny(rInfo.BuildId), + uno::makeAny(rInfo.Description), + uno::makeAny(rInfo.Sources[0].URL), + uno::makeAny(rInfo.Sources[0].IsDirect ? sal_True : sal_False), + uno::makeAny(getReleaseNote(rInfo, 1, autoDownloadEnabled) ), + uno::makeAny(getReleaseNote(rInfo, 2, autoDownloadEnabled) ), + uno::makeAny(getReleaseNote(rInfo, 3, autoDownloadEnabled) ), + uno::makeAny(getReleaseNote(rInfo, 4, autoDownloadEnabled) ), + uno::makeAny(getReleaseNote(rInfo, 5, autoDownloadEnabled) ), + uno::makeAny(aCurrentBuild) + }; + + rtl::OUString aName; + for( sal_uInt32 n=0; n < nUpdateEntryProperties; ++n ) + { + aName = rtl::OUString::createFromAscii(aUpdateEntryProperties[n]); + + if( m_xContainer->hasByName(aName) ) + m_xContainer->replaceByName(aName, aValues[n]); + else + m_xContainer->insertByName(aName,aValues[n]); + } + + commitChanges(); +} + +//------------------------------------------------------------------------------ + +void +UpdateCheckConfig::clearUpdateFound() +{ + rtl::OUString aName; + + for( sal_uInt32 n=0; n < nUpdateEntryProperties; ++n ) + { + aName = rtl::OUString::createFromAscii(aUpdateEntryProperties[n]); + + try { + if( m_xContainer->hasByName(aName) ) + m_xContainer->removeByName(aName); + } catch(const lang::WrappedTargetException& ) { + // Can not remove value, probably in share layer + OSL_ASSERT(false); + m_xContainer->replaceByName(aName, uno::makeAny(rtl::OUString())); + } + } + + /* As we have removed UpdateVersionFound from the shared configuration + * existing entries in the user layer do not have a oor operation and + * thus are completly ignored (which also means they can not be removed). + */ + + commitChanges(); +} + +//------------------------------------------------------------------------------ + +uno::Sequence< rtl::OUString > +UpdateCheckConfig::getServiceNames() +{ + uno::Sequence< rtl::OUString > aServiceList(1); + aServiceList[0] = UNISTRING( "com.sun.star.setup.UpdateCheckConfig"); + return aServiceList; +} + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckConfig::getImplName() +{ + return UNISTRING( "vnd.sun.UpdateCheckConfig"); +} + +//------------------------------------------------------------------------------ + +uno::Type SAL_CALL +UpdateCheckConfig::getElementType() throw (uno::RuntimeException) +{ + return m_xContainer->getElementType(); +} + +//------------------------------------------------------------------------------ + +sal_Bool SAL_CALL +UpdateCheckConfig::hasElements() throw (uno::RuntimeException) +{ + return m_xContainer->hasElements(); +} + +//------------------------------------------------------------------------------ + +uno::Any SAL_CALL +UpdateCheckConfig::getByName( const ::rtl::OUString& aName ) + throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException) +{ + uno::Any aValue = m_xContainer->getByName( aName ); + + // Provide dynamic default value + if( aName.equalsAscii(DOWNLOAD_DESTINATION) ) + { + rtl::OUString aStr; + aValue >>= aStr; + + if( aStr.getLength() == 0 ) + aValue = uno::makeAny(getDesktopDirectory()); + } + + return aValue; +} + +//------------------------------------------------------------------------------ + +uno::Sequence< ::rtl::OUString > SAL_CALL +UpdateCheckConfig::getElementNames( ) throw (uno::RuntimeException) +{ + return m_xContainer->getElementNames(); +} + +//------------------------------------------------------------------------------ + +sal_Bool SAL_CALL +UpdateCheckConfig::hasByName( const ::rtl::OUString& aName ) throw (uno::RuntimeException) +{ + return m_xContainer->hasByName( aName ); +} + +//------------------------------------------------------------------------------ + +void SAL_CALL +UpdateCheckConfig::replaceByName( const ::rtl::OUString& aName, const uno::Any& aElement ) + throw (lang::IllegalArgumentException, container::NoSuchElementException, + lang::WrappedTargetException, uno::RuntimeException) +{ + return m_xContainer->replaceByName( aName, aElement ); +} + +//------------------------------------------------------------------------------ +// XChangesBatch + +void SAL_CALL +UpdateCheckConfig::commitChanges() + throw (lang::WrappedTargetException, uno::RuntimeException) +{ + uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY); + if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() ) + { + util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges(); + xChangesBatch->commitChanges(); + + if( m_rListener.is() ) + { + const sal_Int32 nChanges = aChangesSet.getLength(); + rtl::OUString aString; + + for( sal_Int32 i=0; i<nChanges; ++i ) + { + aChangesSet[i].Accessor >>= aString; + + // FIXME: use non IgnoreAsciiCase version as soon as it becomes available + if( aString.endsWithIgnoreAsciiCaseAsciiL(AUTOCHECK_ENABLED "']", sizeof(AUTOCHECK_ENABLED)+1) ) + { + sal_Bool bEnabled = sal_False; + aChangesSet[i].Element >>= bEnabled; + m_rListener->autoCheckStatusChanged(sal_True == bEnabled); + } + // FIXME: use non IgnoreAsciiCase version as soon as it becomes available + else if( aString.endsWithIgnoreAsciiCaseAsciiL(CHECK_INTERVAL "']", sizeof(CHECK_INTERVAL)+1) ) + { + m_rListener->autoCheckIntervalChanged(); + } + } + } + } + + xChangesBatch = uno::Reference< util::XChangesBatch > ( m_xAvailableUpdates, uno::UNO_QUERY ); + if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() ) + { + util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges(); + xChangesBatch->commitChanges(); + } + xChangesBatch = uno::Reference< util::XChangesBatch > ( m_xIgnoredUpdates, uno::UNO_QUERY ); + if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() ) + { + util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges(); + xChangesBatch->commitChanges(); + } +} + +//------------------------------------------------------------------------------ + +sal_Bool SAL_CALL +UpdateCheckConfig::hasPendingChanges( ) throw (uno::RuntimeException) +{ + uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY); + if( xChangesBatch.is() ) + return xChangesBatch->hasPendingChanges(); + + return sal_False; +} + +//------------------------------------------------------------------------------ + +uno::Sequence< util::ElementChange > SAL_CALL +UpdateCheckConfig::getPendingChanges( ) throw (uno::RuntimeException) +{ + uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY); + if( xChangesBatch.is() ) + return xChangesBatch->getPendingChanges(); + + return uno::Sequence< util::ElementChange >(); +} + +//------------------------------------------------------------------------------ +bool UpdateCheckConfig::storeExtensionVersion( const rtl::OUString& rExtensionName, + const rtl::OUString& rVersion ) +{ + bool bNotify = true; + + if ( m_xAvailableUpdates->hasByName( rExtensionName ) ) + uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) ); + else + { + uno::Reference< beans::XPropertySet > elem( uno::Reference< lang::XSingleServiceFactory >( m_xAvailableUpdates, uno::UNO_QUERY_THROW )->createInstance(), uno::UNO_QUERY_THROW ); + elem->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) ); + m_xAvailableUpdates->insertByName( rExtensionName, uno::Any( elem ) ); + } + + if ( m_xIgnoredUpdates->hasByName( rExtensionName ) ) + { + ::rtl::OUString aIgnoredVersion; + uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) ); + aValue >>= aIgnoredVersion; + if ( aIgnoredVersion.getLength() == 0 ) // no version means ignore all updates + bNotify = false; + else if ( aIgnoredVersion == rVersion ) // the user wanted to ignore this update + bNotify = false; + } + + commitChanges(); + + return bNotify; +} + +//------------------------------------------------------------------------------ +bool UpdateCheckConfig::checkExtensionVersion( const rtl::OUString& rExtensionName, + const rtl::OUString& rVersion ) +{ + if ( m_xAvailableUpdates->hasByName( rExtensionName ) ) + { + ::rtl::OUString aStoredVersion; + uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) ); + aValue >>= aStoredVersion; + + if ( m_xIgnoredUpdates->hasByName( rExtensionName ) ) + { + ::rtl::OUString aIgnoredVersion; + uno::Any aValue2( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) ); + aValue2 >>= aIgnoredVersion; + if ( aIgnoredVersion.getLength() == 0 ) // no version means ignore all updates + return false; + else if ( aIgnoredVersion == aStoredVersion ) // the user wanted to ignore this update + return false; + // TODO: else delete ignored entry? + } + if ( isVersionGreater( rVersion, aStoredVersion ) ) + return true; + else + { + m_xAvailableUpdates->removeByName( rExtensionName ); + commitChanges(); + } + } + + return false; +} + +//------------------------------------------------------------------------------ +rtl::OUString UpdateCheckConfig::getSubVersion( const rtl::OUString& rVersion, + sal_Int32 *nIndex ) +{ + while ( *nIndex < rVersion.getLength() && rVersion[*nIndex] == '0') + { + ++*nIndex; + } + + return rVersion.getToken( 0, '.', *nIndex ); +} + +//------------------------------------------------------------------------------ +// checks if the second version string is greater than the first one + +bool UpdateCheckConfig::isVersionGreater( const rtl::OUString& rVersion1, + const rtl::OUString& rVersion2 ) +{ + for ( sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0; ) + { + ::rtl::OUString sSub1( getSubVersion( rVersion1, &i1 ) ); + ::rtl::OUString sSub2( getSubVersion( rVersion2, &i2 ) ); + + if ( sSub1.getLength() < sSub2.getLength() ) { + return true; + } else if ( sSub1.getLength() > sSub2.getLength() ) { + return false; + } else if ( sSub1 < sSub2 ) { + return true; + } else if ( sSub1 > sSub2 ) { + return false; + } + } + return false; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +rtl::OUString SAL_CALL +UpdateCheckConfig::getImplementationName() throw (uno::RuntimeException) +{ + return getImplName(); +} + +//------------------------------------------------------------------------------ + +sal_Bool SAL_CALL +UpdateCheckConfig::supportsService(rtl::OUString const & serviceName) + throw (uno::RuntimeException) +{ + uno::Sequence< rtl::OUString > aServiceNameList = getServiceNames(); + + for( sal_Int32 n=0; n < aServiceNameList.getLength(); n++ ) + if( aServiceNameList[n].equals(serviceName) ) + return sal_True; + + return sal_False; +} + +//------------------------------------------------------------------------------ + +uno::Sequence< rtl::OUString > SAL_CALL +UpdateCheckConfig::getSupportedServiceNames() throw (uno::RuntimeException) +{ + return getServiceNames(); +} + + diff --git a/extensions/source/update/check/updatecheckconfig.hxx b/extensions/source/update/check/updatecheckconfig.hxx new file mode 100755 index 000000000000..cf55a0787488 --- /dev/null +++ b/extensions/source/update/check/updatecheckconfig.hxx @@ -0,0 +1,242 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <cppuhelper/implbase3.hxx> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <rtl/ref.hxx> + +#include "updatecheckconfiglistener.hxx" +#include "updateinfo.hxx" + +/* Interface to acess configuration data read-only */ +struct IByNameAccess +{ + virtual ::com::sun::star::uno::Any getValue(const sal_Char * pName) = 0; +}; + +/* This helper class provides by name access to a sequence of named values */ +class NamedValueByNameAccess : public IByNameAccess +{ + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& m_rValues; + +public: + NamedValueByNameAccess( + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& rValues) : + m_rValues(rValues) {} ; + + virtual ~NamedValueByNameAccess(); + + virtual ::com::sun::star::uno::Any getValue(const sal_Char * pName); +}; + + +/* This class encapsulates the configuration item actually used for storing the state + * the update check is actually in. + */ +class UpdateCheckROModel +{ +public: + UpdateCheckROModel(IByNameAccess& aNameAccess) : m_aNameAccess(aNameAccess) {}; + + bool isAutoCheckEnabled() const; + bool isDownloadPaused() const; + rtl::OUString getLocalFileName() const; + sal_Int64 getDownloadSize() const; + + rtl::OUString getUpdateEntryVersion() const; + void getUpdateEntry(UpdateInfo& rInfo) const; + +private: + + rtl::OUString getStringValue(const sal_Char *) const; + + IByNameAccess& m_aNameAccess; +}; + + + +/* This class implements the non published UNO service com.sun.star.setup.UpdateCheckConfig, + * which primary use is to be able to track changes done in the Toos -> Options page of this + * component, as this is not supported by the OOo configuration for extendable groups. + */ + +class UpdateCheckConfig : public ::cppu::WeakImplHelper3< + ::com::sun::star::container::XNameReplace, + ::com::sun::star::util::XChangesBatch, + ::com::sun::star::lang::XServiceInfo > +{ + UpdateCheckConfig( const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer >& xContainer, + const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer >& xAvailableUpdates, + const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer >& xIgnoredUpdates, + const ::rtl::Reference< UpdateCheckConfigListener >& rListener ); + + virtual ~UpdateCheckConfig(); + +public: + + static ::com::sun::star::uno::Sequence< rtl::OUString > getServiceNames(); + static rtl::OUString getImplName(); + + static ::rtl::Reference< UpdateCheckConfig > get( + const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& xContext, + const ::rtl::Reference< UpdateCheckConfigListener >& rListener = ::rtl::Reference< UpdateCheckConfigListener >()); + + // Should really implement ROModel .. + bool isAutoCheckEnabled() const; + bool isAutoDownloadEnabled() const; + rtl::OUString getUpdateEntryVersion() const; + + /* Updates the timestamp of last check, but does not commit the change + * as either clearUpdateFound() or setUpdateFound() are expected to get + * called next. + */ + void updateLastChecked(); + + /* Returns the date of the last successful check in seconds since 1970 */ + sal_Int64 getLastChecked() const; + + /* Returns configured check interval in seconds */ + sal_Int64 getCheckInterval() const; + + /* Reset values of previously remembered update + */ + void clearUpdateFound(); + + /* Stores the specified data of an available update + */ + void storeUpdateFound(const UpdateInfo& rInfo, const rtl::OUString& aCurrentBuild); + + // Returns the local file name of a started download + rtl::OUString getLocalFileName() const; + + // Returns the local file name of a started download + rtl::OUString getDownloadDestination() const; + + // stores the local file name of a just started download + void storeLocalFileName(const rtl::OUString& rFileName, sal_Int64 nFileSize); + + // Removes the local file name of a download + void clearLocalFileName(); + + // Stores the bool value for manually paused downloads + void storeDownloadPaused(bool paused); + + // Returns the directory that acts as the user's desktop + static rtl::OUString getDesktopDirectory(); + + // Returns a directory accessible for all users + static rtl::OUString getAllUsersDirectory(); + + // store and retrieve information about extensions + bool storeExtensionVersion( const rtl::OUString& rExtensionName, + const rtl::OUString& rVersion ); + bool checkExtensionVersion( const rtl::OUString& rExtensionName, + const rtl::OUString& rVersion ); + + // XElementAccess + virtual ::com::sun::star::uno::Type SAL_CALL getElementType( ) + throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL hasElements( ) + throw (::com::sun::star::uno::RuntimeException); + + // XNameAccess + virtual ::com::sun::star::uno::Any SAL_CALL getByName( const ::rtl::OUString& aName ) + throw (::com::sun::star::container::NoSuchElementException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getElementNames( ) + throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL hasByName( const ::rtl::OUString& aName ) + throw (::com::sun::star::uno::RuntimeException); + + // XNameReplace + virtual void SAL_CALL replaceByName( const ::rtl::OUString& aName, const ::com::sun::star::uno::Any& aElement ) + throw (::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::container::NoSuchElementException, + ::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + + // XChangesBatch + virtual void SAL_CALL commitChanges( ) + throw (::com::sun::star::lang::WrappedTargetException, + ::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL hasPendingChanges( ) + throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::util::ElementChange > SAL_CALL getPendingChanges( ) + throw (::com::sun::star::uno::RuntimeException); + + // XServiceInfo + virtual rtl::OUString SAL_CALL getImplementationName() + throw (::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & serviceName) + throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames() + throw (::com::sun::star::uno::RuntimeException); + +private: + + static rtl::OUString getSubVersion( const rtl::OUString& rVersion, sal_Int32 *nIndex ); + static bool isVersionGreater( const rtl::OUString& rVersion1, const rtl::OUString& rVersion2 ); + + const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer > m_xContainer; + const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer > m_xAvailableUpdates; + const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameContainer > m_xIgnoredUpdates; + const ::rtl::Reference< UpdateCheckConfigListener > m_rListener; +}; + +//------------------------------------------------------------------------------ + + +template <typename T> +T getValue( const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& rNamedValues, const sal_Char * pszName ) + throw (::com::sun::star::uno::RuntimeException) +{ + for( sal_Int32 n=0; n < rNamedValues.getLength(); n++ ) + { + // Unfortunatly gcc-3.3 does not like Any.get<T>(); + if( rNamedValues[n].Name.equalsAscii( pszName ) ) + { + T value = T(); + if( ! (rNamedValues[n].Value >>= value) ) + throw ::com::sun::star::uno::RuntimeException( + ::rtl::OUString( + cppu_Any_extraction_failure_msg( + &rNamedValues[n].Value, + ::cppu::getTypeFavourUnsigned(&value).getTypeLibType() ), + SAL_NO_ACQUIRE ), + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >() ); + + return value; + } + } + + return T(); +} diff --git a/extensions/source/update/check/updatecheckconfiglistener.hxx b/extensions/source/update/check/updatecheckconfiglistener.hxx new file mode 100644 index 000000000000..9d2c406b455a --- /dev/null +++ b/extensions/source/update/check/updatecheckconfiglistener.hxx @@ -0,0 +1,43 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <rtl/ref.hxx> + +#ifndef _UPDATECHECKCONFIGLISTENER_HXX_ +#define _UPDATECHECKCONFIGLISTENER_HXX_ + +/* This interface should be implemented by classes acting + * as controller (as in the MVC pattern). + */ + +struct UpdateCheckConfigListener : public rtl::IReference +{ + virtual void autoCheckStatusChanged(bool enabled) = 0; + virtual void autoCheckIntervalChanged() = 0; +}; + +#endif diff --git a/extensions/source/update/check/updatecheckjob.cxx b/extensions/source/update/check/updatecheckjob.cxx new file mode 100755 index 000000000000..9a87779e0999 --- /dev/null +++ b/extensions/source/update/check/updatecheckjob.cxx @@ -0,0 +1,399 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" + +#include <memory> + +#include "updatecheck.hxx" +#include "updatecheckconfig.hxx" +#include "updatehdl.hxx" +#include "updateprotocol.hxx" + +#include <cppuhelper/implbase3.hxx> +#include <cppuhelper/implementationentry.hxx> + +#include "com/sun/star/frame/XDesktop.hpp" +#include "com/sun/star/frame/XTerminateListener.hpp" +#include <com/sun/star/task/XJob.hpp> + +namespace beans = com::sun::star::beans ; +namespace frame = com::sun::star::frame ; +namespace lang = com::sun::star::lang ; +namespace task = com::sun::star::task ; +namespace uno = com::sun::star::uno ; + +#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s)) + +namespace +{ + +class InitUpdateCheckJobThread : public osl::Thread +{ +public: + InitUpdateCheckJobThread( const uno::Reference< uno::XComponentContext > &xContext, + const uno::Sequence< beans::NamedValue > &xParameters, + bool bShowDialog ); + + virtual void SAL_CALL run(); + + void setTerminating(); + +private: + osl::Condition m_aCondition; + uno::Reference<uno::XComponentContext> m_xContext; + uno::Sequence<beans::NamedValue> m_xParameters; + bool m_bShowDialog; + bool m_bTerminating; +}; + +class UpdateCheckJob : + public ::cppu::WeakImplHelper3< task::XJob, lang::XServiceInfo, frame::XTerminateListener > +{ + virtual ~UpdateCheckJob(); + +public: + + UpdateCheckJob(const uno::Reference<uno::XComponentContext>& xContext); + + static uno::Sequence< rtl::OUString > getServiceNames(); + static rtl::OUString getImplName(); + + // Allows runtime exceptions to be thrown by const methods + inline SAL_CALL operator uno::Reference< uno::XInterface > () const + { return const_cast< cppu::OWeakObject * > (static_cast< cppu::OWeakObject const * > (this)); }; + + // XJob + virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) + throw (lang::IllegalArgumentException, uno::Exception); + + // XServiceInfo + virtual rtl::OUString SAL_CALL getImplementationName() + throw (uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & serviceName) + throw (uno::RuntimeException); + virtual uno::Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames() + throw (uno::RuntimeException); + + // XEventListener + virtual void SAL_CALL disposing( ::com::sun::star::lang::EventObject const & evt ) + throw (::com::sun::star::uno::RuntimeException); + + // XTerminateListener + virtual void SAL_CALL queryTermination( lang::EventObject const & evt ) + throw ( frame::TerminationVetoException, uno::RuntimeException ); + virtual void SAL_CALL notifyTermination( lang::EventObject const & evt ) + throw ( uno::RuntimeException ); + +private: + uno::Reference<uno::XComponentContext> m_xContext; + uno::Reference< frame::XDesktop > m_xDesktop; + std::auto_ptr< InitUpdateCheckJobThread > m_pInitThread; + + void handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp ); +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +InitUpdateCheckJobThread::InitUpdateCheckJobThread( + const uno::Reference< uno::XComponentContext > &xContext, + const uno::Sequence< beans::NamedValue > &xParameters, + bool bShowDialog ) : + m_xContext( xContext ), + m_xParameters( xParameters ), + m_bShowDialog( bShowDialog ), + m_bTerminating( false ) +{ + create(); +} + +//------------------------------------------------------------------------------ +void SAL_CALL InitUpdateCheckJobThread::run() +{ + if (!m_bShowDialog) { + TimeValue tv = { 25, 0 }; + m_aCondition.wait( &tv ); + if ( m_bTerminating ) + return; + } + + rtl::Reference< UpdateCheck > aController( UpdateCheck::get() ); + aController->initialize( m_xParameters, m_xContext ); + + if ( m_bShowDialog ) + aController->showDialog( true ); +} + +void InitUpdateCheckJobThread::setTerminating() { + m_bTerminating = true; + m_aCondition.set(); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +UpdateCheckJob::UpdateCheckJob( const uno::Reference<uno::XComponentContext>& xContext ) : + m_xContext(xContext) +{ + m_xDesktop.set( xContext->getServiceManager()->createInstanceWithContext( UNISTRING("com.sun.star.frame.Desktop"), xContext ), uno::UNO_QUERY ); + if ( m_xDesktop.is() ) + m_xDesktop->addTerminateListener( this ); +} + +//------------------------------------------------------------------------------ + +UpdateCheckJob::~UpdateCheckJob() +{ +} + +//------------------------------------------------------------------------------ + +uno::Sequence< rtl::OUString > +UpdateCheckJob::getServiceNames() +{ + uno::Sequence< rtl::OUString > aServiceList(1); + aServiceList[0] = UNISTRING( "com.sun.star.setup.UpdateCheck"); + return aServiceList; +}; + +//------------------------------------------------------------------------------ + +rtl::OUString +UpdateCheckJob::getImplName() +{ + return UNISTRING( "vnd.sun.UpdateCheck"); +} + + +//------------------------------------------------------------------------------ + +uno::Any +UpdateCheckJob::execute(const uno::Sequence<beans::NamedValue>& namedValues) + throw (lang::IllegalArgumentException, uno::Exception) +{ + for ( sal_Int32 n=namedValues.getLength(); n-- > 0; ) + { + if ( namedValues[ n ].Name.equalsAscii( "DynamicData" ) ) + { + uno::Sequence<beans::NamedValue> aListProp; + if ( namedValues[n].Value >>= aListProp ) + { + for ( sal_Int32 i=aListProp.getLength(); i-- > 0; ) + { + if ( aListProp[ i ].Name.equalsAscii( "updateList" ) ) + { + handleExtensionUpdates( aListProp ); + return uno::Any(); + } + } + } + } + } + + uno::Sequence<beans::NamedValue> aConfig = + getValue< uno::Sequence<beans::NamedValue> > (namedValues, "JobConfig"); + + /* Determine the way we got invoked here - + * see Developers Guide Chapter "4.7.2 Jobs" to understand the magic + */ + + uno::Sequence<beans::NamedValue> aEnvironment = + getValue< uno::Sequence<beans::NamedValue> > (namedValues, "Environment"); + + rtl::OUString aEventName = getValue< rtl::OUString > (aEnvironment, "EventName"); + + m_pInitThread.reset( + new InitUpdateCheckJobThread( + m_xContext, aConfig, + !aEventName.equalsAscii("onFirstVisibleTask"))); + + return uno::Any(); +} + +//------------------------------------------------------------------------------ +void UpdateCheckJob::handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp ) +{ + try { + uno::Sequence< uno::Sequence< rtl::OUString > > aList = + getValue< uno::Sequence< uno::Sequence< rtl::OUString > > > ( rListProp, "updateList" ); + bool bPrepareOnly = getValue< bool > ( rListProp, "prepareOnly" ); + + // we will first store any new found updates and then check, if there are any + // pending updates. + storeExtensionUpdateInfos( m_xContext, aList ); + + if ( bPrepareOnly ) + return; + + bool bHasUpdates = checkForPendingUpdates( m_xContext ); + + rtl::Reference<UpdateCheck> aController( UpdateCheck::get() ); + if ( ! aController.is() ) + return; + + aController->setHasExtensionUpdates( bHasUpdates ); + + if ( ! aController->hasOfficeUpdate() ) + { + if ( bHasUpdates ) + aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL, true ); + else + aController->setUIState( UPDATESTATE_NO_UPDATE_AVAIL, true ); + } + } + catch( const uno::Exception& e ) + { + OSL_TRACE( "Caught exception: %s\n thread terminated.\n", + rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr()); + } +} + +//------------------------------------------------------------------------------ + +rtl::OUString SAL_CALL +UpdateCheckJob::getImplementationName() throw (uno::RuntimeException) +{ + return getImplName(); +} + +//------------------------------------------------------------------------------ + +uno::Sequence< rtl::OUString > SAL_CALL +UpdateCheckJob::getSupportedServiceNames() throw (uno::RuntimeException) +{ + return getServiceNames(); +} + +//------------------------------------------------------------------------------ + +sal_Bool SAL_CALL +UpdateCheckJob::supportsService( rtl::OUString const & serviceName ) throw (uno::RuntimeException) +{ + uno::Sequence< rtl::OUString > aServiceNameList = getServiceNames(); + + for( sal_Int32 n=0; n < aServiceNameList.getLength(); n++ ) + if( aServiceNameList[n].equals(serviceName) ) + return sal_True; + + return sal_False; +} + +//------------------------------------------------------------------------------ +// XEventListener +void SAL_CALL UpdateCheckJob::disposing( lang::EventObject const & rEvt ) + throw ( uno::RuntimeException ) +{ + bool shutDown = ( rEvt.Source == m_xDesktop ); + + if ( shutDown && m_xDesktop.is() ) + { + m_xDesktop->removeTerminateListener( this ); + m_xDesktop.clear(); + } +} + +//------------------------------------------------------------------------------ +// XTerminateListener +void SAL_CALL UpdateCheckJob::queryTermination( lang::EventObject const & ) + throw ( frame::TerminationVetoException, uno::RuntimeException ) +{ +} + +//------------------------------------------------------------------------------ +void SAL_CALL UpdateCheckJob::notifyTermination( lang::EventObject const & ) + throw ( uno::RuntimeException ) +{ + if ( m_pInitThread.get() != 0 ) + { + m_pInitThread->setTerminating(); + m_pInitThread->join(); + } +} + +} // anonymous namespace + +//------------------------------------------------------------------------------ + +static uno::Reference<uno::XInterface> SAL_CALL +createJobInstance(const uno::Reference<uno::XComponentContext>& xContext) +{ + return *new UpdateCheckJob(xContext); +} + +//------------------------------------------------------------------------------ + +static uno::Reference<uno::XInterface> SAL_CALL +createConfigInstance(const uno::Reference<uno::XComponentContext>& xContext) +{ + return *UpdateCheckConfig::get(xContext, *UpdateCheck::get()); +} + +//------------------------------------------------------------------------------ + +static const cppu::ImplementationEntry kImplementations_entries[] = +{ + { + createJobInstance, + UpdateCheckJob::getImplName, + UpdateCheckJob::getServiceNames, + cppu::createSingleComponentFactory, + NULL, + 0 + }, + { + createConfigInstance, + UpdateCheckConfig::getImplName, + UpdateCheckConfig::getServiceNames, + cppu::createSingleComponentFactory, + NULL, + 0 + }, + { NULL, NULL, NULL, NULL, NULL, 0 } +} ; + +//------------------------------------------------------------------------------ + +extern "C" void SAL_CALL +component_getImplementationEnvironment( const sal_Char **aEnvTypeName, uno_Environment **) +{ + *aEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME ; +} + +//------------------------------------------------------------------------------ + +extern "C" void * +component_getFactory(const sal_Char *pszImplementationName, void *pServiceManager, void *pRegistryKey) +{ + return cppu::component_getFactoryHelper( + pszImplementationName, + pServiceManager, + pRegistryKey, + kImplementations_entries) ; +} diff --git a/extensions/source/update/check/updatehdl.cxx b/extensions/source/update/check/updatehdl.cxx new file mode 100644 index 000000000000..ab6260bd804b --- /dev/null +++ b/extensions/source/update/check/updatehdl.cxx @@ -0,0 +1,1420 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" + +#include "updatehdl.hxx" +#include "update.hrc" + +#include "osl/diagnose.h" +#include "osl/thread.hxx" +#include "osl/file.hxx" +#include "rtl/ustring.hxx" +#include "rtl/bootstrap.hxx" + +#include "com/sun/star/uno/Sequence.h" + +#include <com/sun/star/style/VerticalAlignment.hpp> + +#include "com/sun/star/awt/ActionEvent.hpp" +#include "com/sun/star/awt/PushButtonType.hpp" +#include "com/sun/star/awt/VclWindowPeerAttribute.hpp" +#include "com/sun/star/awt/WindowAttribute.hpp" +#include "com/sun/star/awt/XButton.hpp" +#include "com/sun/star/awt/XControl.hpp" +#include "com/sun/star/awt/XControlContainer.hpp" +#include "com/sun/star/awt/XMessageBox.hpp" +#include "com/sun/star/awt/XAnimation.hpp" +#include "com/sun/star/awt/XTopWindow.hpp" +#include "com/sun/star/awt/XVclWindowPeer.hpp" +#include "com/sun/star/awt/XVclContainer.hpp" +#include "com/sun/star/awt/XWindow.hpp" +#include "com/sun/star/awt/XWindow2.hpp" + +#include <com/sun/star/beans/PropertyValue.hpp> +#include "com/sun/star/beans/XPropertySet.hpp" + +#include "com/sun/star/container/XNameContainer.hpp" + +#include "com/sun/star/frame/XDesktop.hpp" + +#include "com/sun/star/lang/XMultiServiceFactory.hpp" +#include "com/sun/star/task/InteractionRequestStringResolver.hpp" + +#include <com/sun/star/resource/XResourceBundleLoader.hpp> + +#include "updatehdl.hrc" +#include <tools/urlobj.hxx> + +#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s)) + +#define COMMAND_CLOSE UNISTRING("close") + +#define CTRL_THROBBER UNISTRING("throbber") +#define CTRL_PROGRESS UNISTRING("progress") + +#define TEXT_STATUS UNISTRING("text_status") +#define TEXT_PERCENT UNISTRING("text_percent") +#define TEXT_DESCRIPTION UNISTRING("text_description") + +#define FIXED_LINE_MODEL UNISTRING("com.sun.star.awt.UnoControlFixedLineModel") +#define FIXED_TEXT_MODEL UNISTRING("com.sun.star.awt.UnoControlFixedTextModel") +#define EDIT_FIELD_MODEL UNISTRING("com.sun.star.awt.UnoControlEditModel") +#define BUTTON_MODEL UNISTRING("com.sun.star.awt.UnoControlButtonModel") +#define GROUP_BOX_MODEL UNISTRING("com.sun.star.awt.UnoControlGroupBoxModel") + +using namespace com::sun::star; + +//-------------------------------------------------------------------- +UpdateHandler::UpdateHandler( const uno::Reference< uno::XComponentContext > & rxContext, + const rtl::Reference< IActionListener > & rxActionListener ) : + mxContext( rxContext ), + mxActionListener( rxActionListener ), + meCurState( UPDATESTATES_COUNT ), + meLastState( UPDATESTATES_COUNT ), + mnPercent( 0 ), + mnLastCtrlState( -1 ), + mbDownloadBtnHasDots( false ), + mbVisible( false ), + mbStringsLoaded( false ), + mbMinimized( false ), + mbListenerAdded(false), + mbShowsMessageBox(false) +{ +} + +//-------------------------------------------------------------------- +UpdateHandler::~UpdateHandler() +{ + mxContext = NULL; + mxUpdDlg = NULL; + mxInteractionHdl = NULL; + mxActionListener = NULL; +} + +//-------------------------------------------------------------------- +void UpdateHandler::enableControls( short nCtrlState ) +{ + osl::MutexGuard aGuard( maMutex ); + + if ( nCtrlState == mnLastCtrlState ) + return; + + bool bEnableControl; + + short nCurStateVal = nCtrlState; + short nOldStateVal = mnLastCtrlState; + + // the help button should always be the last button in the + // enum list und must never be disabled + for ( int i=0; i<HELP_BUTTON; i++ ) + { + nCurStateVal = (short)(nCtrlState >> i); + nOldStateVal = (short)(mnLastCtrlState >> i); + if ( ( nCurStateVal & 0x01 ) != ( nOldStateVal & 0x01 ) ) + { + bEnableControl = ( ( nCurStateVal & 0x01 ) == 0x01 ); + setControlProperty( msButtonIDs[i], UNISTRING("Enabled"), uno::Any( bEnableControl ) ); + } + } + + mnLastCtrlState = nCtrlState; +} + +//-------------------------------------------------------------------- +void UpdateHandler::setDownloadBtnLabel( bool bAppendDots ) +{ + osl::MutexGuard aGuard( maMutex ); + + if ( mbDownloadBtnHasDots != bAppendDots ) + { + rtl::OUString aLabel( msDownload ); + + if ( bAppendDots ) + aLabel += UNISTRING( "..." ); + + setControlProperty( msButtonIDs[DOWNLOAD_BUTTON], UNISTRING("Label"), uno::Any( aLabel ) ); + setControlProperty( msButtonIDs[DOWNLOAD_BUTTON], UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_DOWNLOAD2 ) ) ); + + mbDownloadBtnHasDots = bAppendDots; + } +} + +//-------------------------------------------------------------------- +void UpdateHandler::setState( UpdateState eState ) +{ + osl::MutexGuard aGuard( maMutex ); + + meCurState = eState; + + if ( mxUpdDlg.is() && mbVisible ) + updateState( meCurState ); +} + +//-------------------------------------------------------------------- +bool UpdateHandler::isVisible() const +{ + if ( !mxUpdDlg.is() ) return false; + + uno::Reference< awt::XWindow2 > xWindow( mxUpdDlg, uno::UNO_QUERY ); + + if ( xWindow.is() ) + return xWindow->isVisible(); + else + return false; +} + +//-------------------------------------------------------------------- +void UpdateHandler::setVisible( bool bVisible ) +{ + osl::MutexGuard aGuard( maMutex ); + + mbVisible = bVisible; + + if ( bVisible ) + { + if ( !mxUpdDlg.is() ) + createDialog(); + + // this should never happen, but if it happens we better return here + if ( !mxUpdDlg.is() ) + return; + + updateState( meCurState ); + + uno::Reference< awt::XWindow > xWindow( mxUpdDlg, uno::UNO_QUERY ); + + if ( xWindow.is() ) + xWindow->setVisible( bVisible ); + + uno::Reference< awt::XTopWindow > xTopWindow( mxUpdDlg, uno::UNO_QUERY ); + if ( xTopWindow.is() ) + { + xTopWindow->toFront(); + if ( !mbListenerAdded ) + { + xTopWindow->addTopWindowListener( this ); + mbListenerAdded = true; + } + } + } + else if ( mxUpdDlg.is() ) + { + uno::Reference< awt::XWindow > xWindow( mxUpdDlg, uno::UNO_QUERY ); + + if ( xWindow.is() ) + xWindow->setVisible( bVisible ); + } +} + +//-------------------------------------------------------------------- +void UpdateHandler::setProgress( sal_Int32 nPercent ) +{ + if ( nPercent > 100 ) + nPercent = 100; + else if ( nPercent < 0 ) + nPercent = 0; + + if ( nPercent != mnPercent ) + { + osl::MutexGuard aGuard( maMutex ); + + mnPercent = nPercent; + setControlProperty( CTRL_PROGRESS, UNISTRING("ProgressValue"), uno::Any( nPercent ) ); + setControlProperty( TEXT_PERCENT, UNISTRING("Text"), uno::Any( substVariables(msPercent) ) ); + } +} + +//-------------------------------------------------------------------- +void UpdateHandler::setErrorMessage( const rtl::OUString& rErrorMsg ) +{ + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( rErrorMsg ) ); +} + +//-------------------------------------------------------------------- +void UpdateHandler::setDownloadFile( const rtl::OUString& rFilePath ) +{ + sal_Int32 nLast = rFilePath.lastIndexOf( '/' ); + if ( nLast != -1 ) + { + msDownloadFile = rFilePath.copy( nLast+1 ); + const rtl::OUString aDownloadURL = rFilePath.copy( 0, nLast ); + osl::FileBase::getSystemPathFromFileURL( aDownloadURL, msDownloadPath ); + } +} + +//-------------------------------------------------------------------- +rtl::OUString UpdateHandler::getBubbleText( UpdateState eState ) +{ + osl::MutexGuard aGuard( maMutex ); + + rtl::OUString sText; + sal_Int32 nIndex = (sal_Int32) eState; + + loadStrings(); + + if ( ( UPDATESTATE_UPDATE_AVAIL <= nIndex ) && ( nIndex < UPDATESTATES_COUNT ) ) + sText = substVariables( msBubbleTexts[ nIndex - UPDATESTATE_UPDATE_AVAIL ] ); + + return sText; +} + +//-------------------------------------------------------------------- +rtl::OUString UpdateHandler::getBubbleTitle( UpdateState eState ) +{ + osl::MutexGuard aGuard( maMutex ); + + rtl::OUString sText; + sal_Int32 nIndex = (sal_Int32) eState; + + loadStrings(); + + if ( ( UPDATESTATE_UPDATE_AVAIL <= nIndex ) && ( nIndex < UPDATESTATES_COUNT ) ) + sText = substVariables( msBubbleTitles[ nIndex - UPDATESTATE_UPDATE_AVAIL] ); + + return sText; +} + +//-------------------------------------------------------------------- +rtl::OUString UpdateHandler::getDefaultInstErrMsg() +{ + osl::MutexGuard aGuard( maMutex ); + + loadStrings(); + + return substVariables( msInstallError ); +} + +// XActionListener +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::disposing( const lang::EventObject& rEvt ) + throw( uno::RuntimeException ) +{ + if ( rEvt.Source == mxUpdDlg ) + mxUpdDlg.clear(); +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::actionPerformed( awt::ActionEvent const & rEvent ) + throw( uno::RuntimeException ) +{ + DialogControls eButton = BUTTON_COUNT; + for ( int i = 0; i < BUTTON_COUNT; i++ ) + { + if ( rEvent.ActionCommand.equals( msButtonIDs[i] ) ) + { + eButton = (DialogControls) i; + break; + } + } + + if ( rEvent.ActionCommand.equals( COMMAND_CLOSE ) ) + { + if ( ( mnLastCtrlState & ( 1 << CLOSE_BUTTON ) ) == ( 1 << CLOSE_BUTTON ) ) + eButton = CLOSE_BUTTON; + else + eButton = CANCEL_BUTTON; + } + + switch ( eButton ) { + case CANCEL_BUTTON: + { + bool bCancel = true; + + if ( ( meCurState == UPDATESTATE_DOWNLOADING ) || + ( meCurState == UPDATESTATE_DOWNLOAD_PAUSED ) || + ( meCurState == UPDATESTATE_ERROR_DOWNLOADING ) ) + bCancel = showWarning( msCancelMessage ); + + if ( bCancel ) + { + mxActionListener->cancel(); + setVisible( false ); + } + break; + } + case CLOSE_BUTTON: + setVisible( false ); + if ( meCurState == UPDATESTATE_ERROR_CHECKING ) + mxActionListener->closeAfterFailure(); + break; + case DOWNLOAD_BUTTON: + mxActionListener->download(); + break; + case INSTALL_BUTTON: + if ( showWarning( msInstallMessage ) ) + mxActionListener->install(); + break; + case PAUSE_BUTTON: + mxActionListener->pause(); + break; + case RESUME_BUTTON: + mxActionListener->resume(); + break; + case HELP_BUTTON: + break; + default: + OSL_ENSURE( false, "UpdateHandler::actionPerformed: unknown command!" ); + } +} + +// XTopWindowListener +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowOpened( const lang::EventObject& ) + throw( uno::RuntimeException ) +{ +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowClosing( const lang::EventObject& e ) + throw( uno::RuntimeException ) +{ + awt::ActionEvent aActionEvt; + aActionEvt.ActionCommand = COMMAND_CLOSE; + aActionEvt.Source = e.Source; + + actionPerformed( aActionEvt ); +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowClosed( const lang::EventObject& ) + throw( uno::RuntimeException ) +{ +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowMinimized( const lang::EventObject& ) + throw( uno::RuntimeException ) +{ + mbMinimized = true; +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowNormalized( const lang::EventObject& ) + throw( uno::RuntimeException ) +{ + mbMinimized = false; +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowActivated( const lang::EventObject& ) + throw( uno::RuntimeException ) +{ +} + +//-------------------------------------------------------------------- +void SAL_CALL UpdateHandler::windowDeactivated( const lang::EventObject& ) + throw( uno::RuntimeException ) +{ +} + +// XInteractionHandler +//------------------------------------------------------------------------------ +void SAL_CALL UpdateHandler::handle( uno::Reference< task::XInteractionRequest > const & rRequest) + throw (uno::RuntimeException) +{ + if ( !mxInteractionHdl.is() ) + { + if( !mxContext.is() ) + throw uno::RuntimeException( UNISTRING( "UpdateHandler:: empty component context" ), *this ); + + uno::Reference< lang::XMultiComponentFactory > xServiceManager(mxContext->getServiceManager()); + + if( !xServiceManager.is() ) + throw uno::RuntimeException( UNISTRING( "UpdateHandler: unable to obtain service manager from component context" ), *this ); + + mxInteractionHdl = uno::Reference<task::XInteractionHandler> ( + xServiceManager->createInstanceWithContext( + UNISTRING( "com.sun.star.task.InteractionHandler" ), + mxContext), + uno::UNO_QUERY_THROW); + if( !mxInteractionHdl.is() ) + throw uno::RuntimeException( UNISTRING( "UpdateHandler:: could not get default interaction handler" ), *this ); + } + uno::Reference< task::XInteractionRequestStringResolver > xStrResolver = + task::InteractionRequestStringResolver::create( mxContext ); + beans::Optional< ::rtl::OUString > aErrorText = xStrResolver->getStringFromInformationalRequest( rRequest ); + if ( aErrorText.IsPresent ) + { + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( aErrorText.Value ) ); + + uno::Sequence< uno::Reference< task::XInteractionContinuation > > xContinuations = rRequest->getContinuations(); + if ( xContinuations.getLength() == 1 ) + { + if ( meCurState == UPDATESTATE_CHECKING ) + setState( UPDATESTATE_ERROR_CHECKING ); + else if ( meCurState == UPDATESTATE_DOWNLOADING ) + setState( UPDATESTATE_ERROR_DOWNLOADING ); + + xContinuations[0]->select(); + } + else + mxInteractionHdl->handle( rRequest ); + } + else + mxInteractionHdl->handle( rRequest ); +} + +//------------------------------------------------------------------------------ +// XTerminateListener +//------------------------------------------------------------------------------ +void SAL_CALL UpdateHandler::queryTermination( const lang::EventObject& ) + throw ( frame::TerminationVetoException, uno::RuntimeException ) +{ + if ( mbShowsMessageBox ) + { + uno::Reference< awt::XTopWindow > xTopWindow( mxUpdDlg, uno::UNO_QUERY ); + if ( xTopWindow.is() ) + xTopWindow->toFront(); + + throw frame::TerminationVetoException( + UNISTRING("The office cannot be closed while displaying a warning!"), + uno::Reference<XInterface>(static_cast<frame::XTerminateListener*>(this), uno::UNO_QUERY)); + } + else + setVisible( false ); +} + +//------------------------------------------------------------------------------ +void SAL_CALL UpdateHandler::notifyTermination( const lang::EventObject& ) + throw ( uno::RuntimeException ) +{ + osl::MutexGuard aGuard( maMutex ); + + if ( mxUpdDlg.is() ) + { + uno::Reference< awt::XTopWindow > xTopWindow( mxUpdDlg, uno::UNO_QUERY ); + if ( xTopWindow.is() ) + xTopWindow->removeTopWindowListener( this ); + + uno::Reference< lang::XComponent > xComponent( mxUpdDlg, uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + + mxUpdDlg.clear(); + } +} + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +void UpdateHandler::updateState( UpdateState eState ) +{ + if ( meLastState == eState ) + return; + + if ( isVisible() ) + {} // ToTop(); + + rtl::OUString sText; + + switch ( eState ) + { + case UPDATESTATE_CHECKING: + showControls( (1<<CANCEL_BUTTON) + (1<<THROBBER_CTRL) ); + enableControls( 1<<CANCEL_BUTTON ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msChecking) ) ); + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( rtl::OUString() ) ); + focusControl( CANCEL_BUTTON ); + break; + case UPDATESTATE_ERROR_CHECKING: + showControls( 0 ); + enableControls( 1 << CLOSE_BUTTON ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msCheckingError) ) ); + focusControl( CLOSE_BUTTON ); + break; + case UPDATESTATE_UPDATE_AVAIL: + showControls( 0 ); + enableControls( ( 1 << CLOSE_BUTTON ) + ( 1 << DOWNLOAD_BUTTON ) ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msUpdFound) ) ); + + sText = substVariables(msDownloadWarning); + if ( msDescriptionMsg.getLength() ) + sText += UNISTRING("\n\n") + msDescriptionMsg; + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( sText ) ); + + setDownloadBtnLabel( false ); + focusControl( DOWNLOAD_BUTTON ); + break; + case UPDATESTATE_UPDATE_NO_DOWNLOAD: + showControls( 0 ); + enableControls( ( 1 << CLOSE_BUTTON ) + ( 1 << DOWNLOAD_BUTTON ) ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msUpdFound) ) ); + + sText = substVariables(msDownloadNotAvail); + if ( msDescriptionMsg.getLength() ) + sText += UNISTRING("\n\n") + msDescriptionMsg; + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( sText ) ); + + setDownloadBtnLabel( true ); + focusControl( DOWNLOAD_BUTTON ); + break; + case UPDATESTATE_NO_UPDATE_AVAIL: + case UPDATESTATE_EXT_UPD_AVAIL: // will only be set, when there are no office updates avail + showControls( 0 ); + enableControls( 1 << CLOSE_BUTTON ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msNoUpdFound) ) ); + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( rtl::OUString() ) ); + focusControl( CLOSE_BUTTON ); + break; + case UPDATESTATE_DOWNLOADING: + showControls( (1<<PROGRESS_CTRL) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) ); + enableControls( (1<<CLOSE_BUTTON) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msDownloading) ) ); + setControlProperty( TEXT_PERCENT, UNISTRING("Text"), uno::Any( substVariables(msPercent) ) ); + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( substVariables(msDownloadWarning) ) ); + setControlProperty( CTRL_PROGRESS, UNISTRING("ProgressValue"), uno::Any( mnPercent ) ); + focusControl( CLOSE_BUTTON ); + break; + case UPDATESTATE_DOWNLOAD_PAUSED: + showControls( (1<<PROGRESS_CTRL) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) ); + enableControls( (1<<CLOSE_BUTTON) + (1<<CANCEL_BUTTON) + (1<<RESUME_BUTTON) ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msDownloadPause) ) ); + setControlProperty( TEXT_PERCENT, UNISTRING("Text"), uno::Any( substVariables(msPercent) ) ); + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( substVariables(msDownloadWarning) ) ); + setControlProperty( CTRL_PROGRESS, UNISTRING("ProgressValue"), uno::Any( mnPercent ) ); + focusControl( CLOSE_BUTTON ); + break; + case UPDATESTATE_ERROR_DOWNLOADING: + showControls( (1<<PROGRESS_CTRL) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) ); + enableControls( (1<<CLOSE_BUTTON) + (1<<CANCEL_BUTTON) ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msDownloadError) ) ); + focusControl( CLOSE_BUTTON ); + break; + case UPDATESTATE_DOWNLOAD_AVAIL: + showControls( 0 ); + enableControls( (1<<CLOSE_BUTTON) + (1<<INSTALL_BUTTON) ); + setControlProperty( TEXT_STATUS, UNISTRING("Text"), uno::Any( substVariables(msReady2Install) ) ); + setControlProperty( TEXT_DESCRIPTION, UNISTRING("Text"), uno::Any( substVariables(msDownloadDescr) ) ); + focusControl( INSTALL_BUTTON ); + break; + case UPDATESTATE_AUTO_START: + case UPDATESTATES_COUNT: + //do nothing, only count! + break; + } + + meLastState = eState; +} + +//-------------------------------------------------------------------- +void UpdateHandler::searchAndReplaceAll( rtl::OUString &rText, + const rtl::OUString &rWhat, + const rtl::OUString &rWith ) const +{ + sal_Int32 nIndex = rText.indexOf( rWhat ); + + while ( nIndex != -1 ) + { + rText = rText.replaceAt( nIndex, rWhat.getLength(), rWith ); + nIndex = rText.indexOf( rWhat, nIndex ); + } +} + +//-------------------------------------------------------------------- +rtl::OUString UpdateHandler::loadString( const uno::Reference< resource::XResourceBundle > xBundle, + sal_Int32 nResourceId ) const +{ + rtl::OUString sString; + rtl::OUString sKey = UNISTRING( "string:" ) + rtl::OUString::valueOf( nResourceId ); + + try + { + OSL_VERIFY( xBundle->getByName( sKey ) >>= sString ); + } + catch( const uno::Exception& ) + { + OSL_ENSURE( false, "UpdateHandler::loadString: caught an exception!" ); + sString = UNISTRING("Missing ") + sKey; + } + + return sString; +} + +rtl::OUString UpdateHandler::substVariables( const rtl::OUString &rSource ) const +{ + rtl::OUString sString( rSource ); + + searchAndReplaceAll( sString, UNISTRING( "%NEXTVERSION" ), msNextVersion ); + searchAndReplaceAll( sString, UNISTRING( "%DOWNLOAD_PATH" ), msDownloadPath ); + searchAndReplaceAll( sString, UNISTRING( "%FILE_NAME" ), msDownloadFile ); + searchAndReplaceAll( sString, UNISTRING( "%PERCENT" ), rtl::OUString::valueOf( mnPercent ) ); + + return sString; +} + +//-------------------------------------------------------------------- +void UpdateHandler::loadStrings() +{ + if ( mbStringsLoaded ) + return; + else + mbStringsLoaded = true; + + uno::Reference< resource::XResourceBundleLoader > xLoader; + try + { + uno::Any aValue( mxContext->getValueByName( + UNISTRING( "/singletons/com.sun.star.resource.OfficeResourceLoader" ) ) ); + OSL_VERIFY( aValue >>= xLoader ); + } + catch( const uno::Exception& ) + { + OSL_ENSURE( false, "UpdateHandler::loadStrings: could not create the resource loader!" ); + } + + if ( !xLoader.is() ) return; + + uno::Reference< resource::XResourceBundle > xBundle; + + try + { + xBundle = xLoader->loadBundle_Default( UNISTRING( "upd" ) ); + } + catch( const resource::MissingResourceException& ) + { + OSL_ENSURE( false, "UpdateHandler::loadStrings: missing the resource bundle!" ); + } + + if ( !xBundle.is() ) return; + + msChecking = loadString( xBundle, RID_UPDATE_STR_CHECKING ); + msCheckingError = loadString( xBundle, RID_UPDATE_STR_CHECKING_ERR ); + msNoUpdFound = loadString( xBundle, RID_UPDATE_STR_NO_UPD_FOUND ); + + msUpdFound = loadString( xBundle, RID_UPDATE_STR_UPD_FOUND ); + setFullVersion( msUpdFound ); + + msDlgTitle = loadString( xBundle, RID_UPDATE_STR_DLG_TITLE ); + msDownloadPause = loadString( xBundle, RID_UPDATE_STR_DOWNLOAD_PAUSE ); + msDownloadError = loadString( xBundle, RID_UPDATE_STR_DOWNLOAD_ERR ); + msDownloadWarning = loadString( xBundle, RID_UPDATE_STR_DOWNLOAD_WARN ); + msDownloadDescr = loadString( xBundle, RID_UPDATE_STR_DOWNLOAD_DESCR ); + msDownloadNotAvail = loadString( xBundle, RID_UPDATE_STR_DOWNLOAD_UNAVAIL ); + msDownloading = loadString( xBundle, RID_UPDATE_STR_DOWNLOADING ); + msReady2Install = loadString( xBundle, RID_UPDATE_STR_READY_INSTALL ); + msCancelTitle = loadString( xBundle, RID_UPDATE_STR_CANCEL_TITLE ); + msCancelMessage = loadString( xBundle, RID_UPDATE_STR_CANCEL_DOWNLOAD ); + msInstallMessage = loadString( xBundle, RID_UPDATE_STR_BEGIN_INSTALL ); + msInstallNow = loadString( xBundle, RID_UPDATE_STR_INSTALL_NOW ); + msInstallLater = loadString( xBundle, RID_UPDATE_STR_INSTALL_LATER ); + msInstallError = loadString( xBundle, RID_UPDATE_STR_INSTALL_ERROR ); + msOverwriteWarning = loadString( xBundle, RID_UPDATE_STR_OVERWRITE_WARNING ); + msPercent = loadString( xBundle, RID_UPDATE_STR_PERCENT ); + msReloadWarning = loadString( xBundle, RID_UPDATE_STR_RELOAD_WARNING ); + msReloadReload = loadString( xBundle, RID_UPDATE_STR_RELOAD_RELOAD ); + msReloadContinue = loadString( xBundle, RID_UPDATE_STR_RELOAD_CONTINUE ); + + msStatusFL = loadString( xBundle, RID_UPDATE_FT_STATUS ); + msDescription = loadString( xBundle, RID_UPDATE_FT_DESCRIPTION ); + + msClose = loadString( xBundle, RID_UPDATE_BTN_CLOSE ); + msDownload = loadString( xBundle, RID_UPDATE_BTN_DOWNLOAD ); + msInstall = loadString( xBundle, RID_UPDATE_BTN_INSTALL ); + msPauseBtn = loadString( xBundle, RID_UPDATE_BTN_PAUSE ); + msResumeBtn = loadString( xBundle, RID_UPDATE_BTN_RESUME ); + msCancelBtn = loadString( xBundle, RID_UPDATE_BTN_CANCEL ); + + // all update states before UPDATESTATE_UPDATE_AVAIL don't have a bubble + // so we can ignore them + for ( int i=0; i < (int)(UPDATESTATES_COUNT - UPDATESTATE_UPDATE_AVAIL); i++ ) + { + msBubbleTexts[ i ] = loadString( xBundle, RID_UPDATE_BUBBLE_TEXT_START + i ); + msBubbleTitles[ i ] = loadString( xBundle, RID_UPDATE_BUBBLE_T_TEXT_START + i ); + } + + for ( int i=0; i < BUTTON_COUNT; i++ ) + { + msButtonIDs[ i ] = UNISTRING("BUTTON_") + rtl::OUString::valueOf( (sal_Int32) i ); + } +} + + +//-------------------------------------------------------------------- +void UpdateHandler::startThrobber( bool bStart ) +{ + uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY ); + uno::Reference< awt::XAnimation > xThrobber( xContainer->getControl( CTRL_THROBBER ), uno::UNO_QUERY ); + + if ( xThrobber.is() ) + { + if ( bStart ) + xThrobber->startAnimation(); + else + xThrobber->stopAnimation(); + } + + uno::Reference< awt::XWindow > xWindow( xContainer->getControl( CTRL_THROBBER ), uno::UNO_QUERY ); + if (xWindow.is() ) + xWindow->setVisible( bStart ); +} + +//-------------------------------------------------------------------- +void UpdateHandler::setControlProperty( const rtl::OUString &rCtrlName, + const rtl::OUString &rPropName, + const uno::Any &rPropValue ) +{ + if ( !mxUpdDlg.is() ) return; + + uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY ); + uno::Reference< awt::XControl > xControl( xContainer->getControl( rCtrlName ), uno::UNO_QUERY_THROW ); + uno::Reference< awt::XControlModel > xControlModel( xControl->getModel(), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW ); + + try { + xPropSet->setPropertyValue( rPropName, rPropValue ); + } + catch( const beans::UnknownPropertyException& ) + { + OSL_ENSURE( false, "UpdateHandler::setControlProperty: caught an exception!" ); + } +} + +//-------------------------------------------------------------------- +void UpdateHandler::showControl( const rtl::OUString &rCtrlName, bool bShow ) +{ + uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY ); + + if ( !xContainer.is() ) + { + OSL_ENSURE( false, "UpdateHandler::showControl: could not get control container!" ); + return; + } + + uno::Reference< awt::XWindow > xWindow( xContainer->getControl( rCtrlName ), uno::UNO_QUERY ); + if ( xWindow.is() ) + xWindow->setVisible( bShow ); +} + +//-------------------------------------------------------------------- +void UpdateHandler::focusControl( DialogControls eID ) +{ + uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY ); + + if ( !xContainer.is() ) + { + OSL_ENSURE( false, "UpdateHandler::focusControl: could not get control container!" ); + return; + } + + OSL_ENSURE( (eID < BUTTON_COUNT), "UpdateHandler::focusControl: id to big!" ); + + uno::Reference< awt::XWindow > xWindow( xContainer->getControl( msButtonIDs[(short)eID] ), uno::UNO_QUERY ); + if ( xWindow.is() ) + xWindow->setFocus(); +} + +//-------------------------------------------------------------------- +void UpdateHandler::insertControlModel( uno::Reference< awt::XControlModel > & rxDialogModel, + rtl::OUString const & rServiceName, + rtl::OUString const & rControlName, + awt::Rectangle const & rPosSize, + uno::Sequence< beans::NamedValue > const & rProps ) +{ + uno::Reference< lang::XMultiServiceFactory > xFactory (rxDialogModel, uno::UNO_QUERY_THROW); + uno::Reference< awt::XControlModel > xModel (xFactory->createInstance (rServiceName), uno::UNO_QUERY_THROW); + uno::Reference< beans::XPropertySet > xPropSet (xModel, uno::UNO_QUERY_THROW); + + for (sal_Int32 i = 0, n = rProps.getLength(); i < n; i++) + { + xPropSet->setPropertyValue (rProps[i].Name, rProps[i].Value); + } + + // @see awt/UnoControlDialogElement.idl + xPropSet->setPropertyValue( UNISTRING("Name"), uno::Any (rControlName) ); + xPropSet->setPropertyValue( UNISTRING("PositionX"), uno::Any (rPosSize.X) ); + xPropSet->setPropertyValue( UNISTRING("PositionY"), uno::Any (rPosSize.Y) ); + xPropSet->setPropertyValue( UNISTRING("Height"), uno::Any (rPosSize.Height) ); + xPropSet->setPropertyValue( UNISTRING("Width"), uno::Any (rPosSize.Width) ); + + // insert by Name into DialogModel container + uno::Reference< container::XNameContainer > xContainer (rxDialogModel, uno::UNO_QUERY_THROW); + xContainer->insertByName( rControlName, uno::Any (uno::Reference< uno::XInterface >(xModel, uno::UNO_QUERY))); +} + +//-------------------------------------------------------------------- +void UpdateHandler::setFullVersion( rtl::OUString& rString ) +{ + if( !mxContext.is() ) + throw uno::RuntimeException( UNISTRING( "getProductName: empty component context" ), *this ); + + uno::Reference< lang::XMultiComponentFactory > xServiceManager( mxContext->getServiceManager() ); + + if( !xServiceManager.is() ) + throw uno::RuntimeException( UNISTRING( "getProductName: unable to obtain service manager from component context" ), *this ); + + uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider( + xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.configuration.ConfigurationProvider" ), mxContext ), + uno::UNO_QUERY_THROW); + + beans::PropertyValue aProperty; + aProperty.Name = UNISTRING( "nodepath" ); + aProperty.Value = uno::makeAny( UNISTRING("org.openoffice.Setup/Product") ); + + uno::Sequence< uno::Any > aArgumentList( 1 ); + aArgumentList[0] = uno::makeAny( aProperty ); + + uno::Reference< uno::XInterface > xConfigAccess; + xConfigAccess = xConfigurationProvider->createInstanceWithArguments( UNISTRING("com.sun.star.configuration.ConfigurationAccess"), + aArgumentList ); + + uno::Reference< container::XNameAccess > xNameAccess( xConfigAccess, uno::UNO_QUERY_THROW ); + + rtl::OUString aProductVersion; + rtl::OUString aProductFullVersion; + + xNameAccess->getByName(UNISTRING("ooSetupVersion")) >>= aProductVersion; + aProductFullVersion = aProductVersion; + + sal_Int32 nVerIndex = rString.indexOf( aProductVersion ); + if ( nVerIndex != -1 ) + { + rtl::OUString aPackageVersion = UNISTRING( "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("version") ":OOOPackageVersion}" ); + rtl::Bootstrap::expandMacros( aPackageVersion ); + + if ( aPackageVersion.getLength() ) + { + sal_Int32 nTokIndex = 0; + rtl::OUString aVersionMinor = aPackageVersion.getToken( 1, '.', nTokIndex ); + rtl::OUString aVersionMicro; + + if ( nTokIndex > 0 ) + aVersionMicro = aPackageVersion.getToken( 0, '.', nTokIndex ); + + if ( aVersionMinor.getLength() == 0 ) + aVersionMinor = UNISTRING( "0" ); + if ( aVersionMicro.getLength() == 0 ) + aVersionMicro = UNISTRING( "0" ); + + sal_Int32 nIndex = aProductFullVersion.indexOf( '.' ); + if ( nIndex == -1 ) + { + aProductFullVersion += UNISTRING( "." ); + aProductFullVersion += aVersionMinor; + } + else + { + nIndex = aProductFullVersion.indexOf( '.', nIndex+1 ); + } + if ( nIndex == -1 ) + { + aProductFullVersion += UNISTRING( "." ); + aProductFullVersion += aVersionMicro; + } + else + { + aProductFullVersion = aProductFullVersion.replaceAt( nIndex+1, aProductFullVersion.getLength()-nIndex-1, aVersionMicro ); + } + } + rString = rString.replaceAt( nVerIndex, aProductVersion.getLength(), aProductFullVersion ); + } +} + +//-------------------------------------------------------------------- +bool UpdateHandler::showWarning( const rtl::OUString &rWarningText ) const +{ + bool bRet = false; + + uno::Reference< awt::XControl > xControl( mxUpdDlg, uno::UNO_QUERY ); + if ( !xControl.is() ) return bRet; + + uno::Reference< awt::XWindowPeer > xPeer = xControl->getPeer(); + if ( !xPeer.is() ) return bRet; + + uno::Reference< awt::XToolkit > xToolkit = xPeer->getToolkit(); + if ( !xToolkit.is() ) return bRet; + + awt::WindowDescriptor aDescriptor; + + sal_Int32 nWindowAttributes = awt::WindowAttribute::BORDER | awt::WindowAttribute::MOVEABLE | awt::WindowAttribute::CLOSEABLE; + nWindowAttributes |= awt::VclWindowPeerAttribute::YES_NO; + nWindowAttributes |= awt::VclWindowPeerAttribute::DEF_NO; + + aDescriptor.Type = awt::WindowClass_MODALTOP; + aDescriptor.WindowServiceName = UNISTRING( "warningbox" ); + aDescriptor.ParentIndex = -1; + aDescriptor.Parent = xPeer; + aDescriptor.Bounds = awt::Rectangle( 10, 10, 250, 150 ); + aDescriptor.WindowAttributes = nWindowAttributes; + + uno::Reference< awt::XMessageBox > xMsgBox( xToolkit->createWindow( aDescriptor ), uno::UNO_QUERY ); + if ( xMsgBox.is() ) + { + mbShowsMessageBox = true; + sal_Int16 nRet; + // xMsgBox->setCaptionText( msCancelTitle ); + xMsgBox->setMessageText( rWarningText ); + nRet = xMsgBox->execute(); + if ( nRet == 2 ) // RET_YES == 2 + bRet = true; + mbShowsMessageBox = false; + } + + uno::Reference< lang::XComponent > xComponent( xMsgBox, uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + + return bRet; +} + +//-------------------------------------------------------------------- +bool UpdateHandler::showWarning( const rtl::OUString &rWarningText, + const rtl::OUString &rBtnText_1, + const rtl::OUString &rBtnText_2 ) const +{ + bool bRet = false; + + uno::Reference< awt::XControl > xControl( mxUpdDlg, uno::UNO_QUERY ); + if ( !xControl.is() ) return bRet; + + uno::Reference< awt::XWindowPeer > xPeer = xControl->getPeer(); + if ( !xPeer.is() ) return bRet; + + uno::Reference< awt::XToolkit > xToolkit = xPeer->getToolkit(); + if ( !xToolkit.is() ) return bRet; + + awt::WindowDescriptor aDescriptor; + + sal_Int32 nWindowAttributes = awt::WindowAttribute::BORDER | awt::WindowAttribute::MOVEABLE | awt::WindowAttribute::CLOSEABLE; + nWindowAttributes |= awt::VclWindowPeerAttribute::YES_NO; + nWindowAttributes |= awt::VclWindowPeerAttribute::DEF_NO; + + aDescriptor.Type = awt::WindowClass_MODALTOP; + aDescriptor.WindowServiceName = UNISTRING( "warningbox" ); + aDescriptor.ParentIndex = -1; + aDescriptor.Parent = xPeer; + aDescriptor.Bounds = awt::Rectangle( 10, 10, 250, 150 ); + aDescriptor.WindowAttributes = nWindowAttributes; + + uno::Reference< awt::XMessageBox > xMsgBox( xToolkit->createWindow( aDescriptor ), uno::UNO_QUERY ); + if ( xMsgBox.is() ) + { + uno::Reference< awt::XVclContainer > xMsgBoxCtrls( xMsgBox, uno::UNO_QUERY ); + if ( xMsgBoxCtrls.is() ) + { + uno::Sequence< uno::Reference< awt::XWindow > > xChildren = xMsgBoxCtrls->getWindows(); + + for ( long i=0; i < xChildren.getLength(); i++ ) + { + uno::Reference< awt::XVclWindowPeer > xMsgBoxCtrl( xChildren[i], uno::UNO_QUERY ); + if ( xMsgBoxCtrl.is() ) + { + bool bIsDefault = true; + uno::Any aValue = xMsgBoxCtrl->getProperty( UNISTRING("DefaultButton") ); + aValue >>= bIsDefault; + if ( bIsDefault ) + xMsgBoxCtrl->setProperty( UNISTRING("Text"), uno::Any( rBtnText_1 ) ); + else + xMsgBoxCtrl->setProperty( UNISTRING("Text"), uno::Any( rBtnText_2 ) ); + } + } + } + + sal_Int16 nRet; + // xMsgBox->setCaptionText( msCancelTitle ); + mbShowsMessageBox = true; + xMsgBox->setMessageText( rWarningText ); + nRet = xMsgBox->execute(); + if ( nRet == 2 ) // RET_YES == 2 + bRet = true; + + mbShowsMessageBox = false; + } + + uno::Reference< lang::XComponent > xComponent( xMsgBox, uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + + return bRet; +} + +//-------------------------------------------------------------------- +bool UpdateHandler::showOverwriteWarning( const rtl::OUString& rFileName ) const +{ + rtl::OUString aMsg( msReloadWarning ); + searchAndReplaceAll( aMsg, UNISTRING( "%FILENAME" ), rFileName ); + searchAndReplaceAll( aMsg, UNISTRING( "%DOWNLOAD_PATH" ), msDownloadPath ); + return showWarning( aMsg, msReloadContinue, msReloadReload ); +} + +//-------------------------------------------------------------------- +bool UpdateHandler::showOverwriteWarning() const +{ + return showWarning( msOverwriteWarning ); +} + +//-------------------------------------------------------------------- +#define BUTTON_HEIGHT 14 +#define BUTTON_WIDTH 50 +#define BUTTON_X_OFFSET 7 +#define BUTTON_Y_OFFSET 3 +#define LABEL_HEIGHT 10 + +#define DIALOG_WIDTH 300 +#define DIALOG_BORDER 5 +#define INNER_BORDER 3 +#define TEXT_OFFSET 1 +#define BOX_HEIGHT1 ( LABEL_HEIGHT + 3*BUTTON_HEIGHT + 2*BUTTON_Y_OFFSET + 2*INNER_BORDER ) +#define BOX_HEIGHT2 50 +#define EDIT_WIDTH ( DIALOG_WIDTH - 2 * DIALOG_BORDER ) +#define BOX1_BTN_X ( DIALOG_BORDER + EDIT_WIDTH - BUTTON_WIDTH - INNER_BORDER ) +#define BOX1_BTN_Y ( DIALOG_BORDER + LABEL_HEIGHT + INNER_BORDER) +#define THROBBER_WIDTH 16 +#define THROBBER_HEIGHT 16 +#define THROBBER_X_POS ( DIALOG_BORDER + 8 ) +#define THROBBER_Y_POS ( DIALOG_BORDER + 23 ) +#define BUTTON_BAR_HEIGHT 24 +#define LABEL_OFFSET ( LABEL_HEIGHT + 4 ) +#define DIALOG_HEIGHT ( BOX_HEIGHT1 + BOX_HEIGHT2 + LABEL_OFFSET + BUTTON_BAR_HEIGHT + 3 * DIALOG_BORDER ) +#define LABEL_Y_POS ( 2 * DIALOG_BORDER + BOX_HEIGHT1 ) +#define EDIT2_Y_POS ( LABEL_Y_POS + LABEL_HEIGHT ) +#define BUTTON_BAR_Y_POS ( EDIT2_Y_POS + DIALOG_BORDER + BOX_HEIGHT2 ) +#define BUTTON_Y_POS ( BUTTON_BAR_Y_POS + 8 ) +#define CLOSE_BTN_X ( DIALOG_WIDTH - DIALOG_BORDER - BUTTON_WIDTH ) +#define INSTALL_BTN_X ( CLOSE_BTN_X - 2 * BUTTON_X_OFFSET - BUTTON_WIDTH ) +#define DOWNLOAD_BTN_X ( INSTALL_BTN_X - BUTTON_X_OFFSET - BUTTON_WIDTH ) +#define PROGRESS_WIDTH 80 +#define PROGRESS_HEIGHT 10 +#define PROGRESS_X_POS ( DIALOG_BORDER + 8 ) +#define PROGRESS_Y_POS ( DIALOG_BORDER + 2*LABEL_OFFSET ) + +//-------------------------------------------------------------------- +void UpdateHandler::showControls( short nControls ) +{ + // The buttons from CANCEL_BUTTON to RESUME_BUTTON will be shown or + // hidden on demand + short nShiftMe; + for ( int i = 0; i <= (int)RESUME_BUTTON; i++ ) + { + nShiftMe = (short)(nControls >> i); + showControl( msButtonIDs[i], (bool)(nShiftMe & 0x01) ); + } + + nShiftMe = (short)(nControls >> THROBBER_CTRL); + startThrobber( (bool)(nShiftMe & 0x01) ); + + nShiftMe = (short)(nControls >> PROGRESS_CTRL); + showControl( CTRL_PROGRESS, (bool)(nShiftMe & 0x01) ); + showControl( TEXT_PERCENT, (bool)(nShiftMe & 0x01) ); + + // Status text needs to be smaller, when there are buttons at the right side of the dialog + if ( ( nControls & ( (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) ) ) != 0 ) + setControlProperty( TEXT_STATUS, UNISTRING("Width"), uno::Any( sal_Int32(EDIT_WIDTH - BUTTON_WIDTH - 2*INNER_BORDER - TEXT_OFFSET ) ) ); + else + setControlProperty( TEXT_STATUS, UNISTRING("Width"), uno::Any( sal_Int32(EDIT_WIDTH - 2*TEXT_OFFSET ) ) ); + + // Status text needs to be taller, when we show the progress bar + if ( ( nControls & ( 1<<PROGRESS_CTRL ) ) != 0 ) + setControlProperty( TEXT_STATUS, UNISTRING("Height"), uno::Any( sal_Int32(LABEL_HEIGHT) ) ); + else + setControlProperty( TEXT_STATUS, UNISTRING("Height"), uno::Any( sal_Int32(BOX_HEIGHT1 - 4*TEXT_OFFSET - LABEL_HEIGHT ) ) ); +} + +//-------------------------------------------------------------------- +void UpdateHandler::createDialog() +{ + if ( !mxContext.is() ) + { + OSL_ASSERT( false ); + return; + } + + uno::Reference< lang::XMultiComponentFactory > xServiceManager( mxContext->getServiceManager() ); + + if( xServiceManager.is() ) + { + uno::Reference< frame::XDesktop > xDesktop( + xServiceManager->createInstanceWithContext( UNISTRING( "com.sun.star.frame.Desktop"), mxContext ), + uno::UNO_QUERY ); + if ( xDesktop.is() ) + xDesktop->addTerminateListener( this ); + } + + loadStrings(); + + uno::Reference< lang::XMultiComponentFactory > xFactory( mxContext->getServiceManager(), uno::UNO_QUERY_THROW ); + uno::Reference< awt::XControlModel > xControlModel( xFactory->createInstanceWithContext( + UNISTRING("com.sun.star.awt.UnoControlDialogModel"), + mxContext), uno::UNO_QUERY_THROW ); + { + // @see awt/UnoControlDialogModel.idl + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW ); + + xPropSet->setPropertyValue( UNISTRING("Title"), uno::Any( msDlgTitle ) ); + xPropSet->setPropertyValue( UNISTRING("Closeable"), uno::Any( true ) ); + xPropSet->setPropertyValue( UNISTRING("Enabled"), uno::Any( true ) ); + xPropSet->setPropertyValue( UNISTRING("Moveable"), uno::Any( true ) ); + xPropSet->setPropertyValue( UNISTRING("Sizeable"), uno::Any( true ) ); + xPropSet->setPropertyValue( UNISTRING("DesktopAsParent"), uno::Any( true ) ); + xPropSet->setPropertyValue( UNISTRING("PositionX"), uno::Any(sal_Int32( 100 )) ); + xPropSet->setPropertyValue( UNISTRING("PositionY"), uno::Any(sal_Int32( 100 )) ); + xPropSet->setPropertyValue( UNISTRING("Width"), uno::Any(sal_Int32( DIALOG_WIDTH )) ); + xPropSet->setPropertyValue( UNISTRING("Height"), uno::Any(sal_Int32( DIALOG_HEIGHT )) ); + xPropSet->setPropertyValue( UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_DLG ) ) ); + } + { // Label (fixed text) <status> + uno::Sequence< beans::NamedValue > aProps(1); + + setProperty( aProps, 0, UNISTRING("Label"), uno::Any( msStatusFL ) ); + + insertControlModel( xControlModel, FIXED_TEXT_MODEL, UNISTRING( "fixedLineStatus" ), + awt::Rectangle( DIALOG_BORDER+1, DIALOG_BORDER, EDIT_WIDTH-2, LABEL_HEIGHT ), + aProps ); + } + { // box around <status> text + uno::Sequence< beans::NamedValue > aProps; + + insertControlModel( xControlModel, GROUP_BOX_MODEL, UNISTRING( "StatusBox" ), + awt::Rectangle( DIALOG_BORDER, DIALOG_BORDER + LABEL_HEIGHT, EDIT_WIDTH, BOX_HEIGHT1 - LABEL_HEIGHT ), + aProps ); + } + { // Text (multiline edit) <status> + uno::Sequence< beans::NamedValue > aProps(7); + + setProperty( aProps, 0, UNISTRING("Text"), uno::Any( substVariables(msChecking) ) ); + setProperty( aProps, 1, UNISTRING("Border"), uno::Any( sal_Int16( 0 ) ) ); + setProperty( aProps, 2, UNISTRING("PaintTransparent"), uno::Any( true ) ); + setProperty( aProps, 3, UNISTRING("MultiLine"), uno::Any( true ) ); + setProperty( aProps, 4, UNISTRING("ReadOnly"), uno::Any( true ) ); + setProperty( aProps, 5, UNISTRING("AutoVScroll"), uno::Any( true ) ); + setProperty( aProps, 6, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_STATUS ) ) ); + + insertControlModel( xControlModel, EDIT_FIELD_MODEL, TEXT_STATUS, + awt::Rectangle( DIALOG_BORDER + TEXT_OFFSET, + DIALOG_BORDER + LABEL_HEIGHT + TEXT_OFFSET, + EDIT_WIDTH - 2*TEXT_OFFSET, + BOX_HEIGHT1 - 4*TEXT_OFFSET - LABEL_HEIGHT ), + aProps ); + } + { // Text (edit) <percent> + uno::Sequence< beans::NamedValue > aProps(4); + + setProperty( aProps, 0, UNISTRING("Text"), uno::Any( msPercent ) ); + setProperty( aProps, 1, UNISTRING("Border"), uno::Any( sal_Int16( 0 ) ) ); + setProperty( aProps, 2, UNISTRING("PaintTransparent"), uno::Any( true ) ); + setProperty( aProps, 3, UNISTRING("ReadOnly"), uno::Any( true ) ); + + insertControlModel( xControlModel, EDIT_FIELD_MODEL, TEXT_PERCENT, + awt::Rectangle( PROGRESS_X_POS + PROGRESS_WIDTH + DIALOG_BORDER, + PROGRESS_Y_POS, + EDIT_WIDTH - PROGRESS_WIDTH - BUTTON_WIDTH - 2*DIALOG_BORDER, + LABEL_HEIGHT ), + aProps ); + } + { // pause button + uno::Sequence< beans::NamedValue > aProps(5); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) ); + setProperty( aProps, 3, UNISTRING("Label"), uno::Any( msPauseBtn ) ); + setProperty( aProps, 4, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_PAUSE ) ) ); + + insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[PAUSE_BUTTON], + awt::Rectangle( BOX1_BTN_X, BOX1_BTN_Y, BUTTON_WIDTH, BUTTON_HEIGHT ), + aProps ); + } + { // resume button + uno::Sequence< beans::NamedValue > aProps(5); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) ); + setProperty( aProps, 3, UNISTRING("Label"), uno::Any( msResumeBtn ) ); + setProperty( aProps, 4, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_RESUME ) ) ); + + insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[RESUME_BUTTON], + awt::Rectangle( BOX1_BTN_X, + BOX1_BTN_Y + BUTTON_Y_OFFSET + BUTTON_HEIGHT, + BUTTON_WIDTH, + BUTTON_HEIGHT ), + aProps ); + } + { // abort button + uno::Sequence< beans::NamedValue > aProps(5); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) ); + setProperty( aProps, 3, UNISTRING("Label"), uno::Any( msCancelBtn ) ); + setProperty( aProps, 4, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_CANCEL ) ) ); + + insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[CANCEL_BUTTON], + awt::Rectangle( BOX1_BTN_X, + BOX1_BTN_Y + (2*(BUTTON_HEIGHT+BUTTON_Y_OFFSET)), + BUTTON_WIDTH, + BUTTON_HEIGHT ), + aProps ); + } + { // Label (FixedText) <description> + uno::Sequence< beans::NamedValue > aProps(1); + + setProperty( aProps, 0, UNISTRING("Label"), uno::Any( msDescription ) ); + + insertControlModel( xControlModel, FIXED_TEXT_MODEL, UNISTRING( "fixedTextDescription" ), + awt::Rectangle( DIALOG_BORDER+1, LABEL_Y_POS, EDIT_WIDTH-2, LABEL_HEIGHT ), + aProps ); + } + { // box around <description> text + uno::Sequence< beans::NamedValue > aProps; + + insertControlModel( xControlModel, GROUP_BOX_MODEL, UNISTRING( "DescriptionBox" ), + awt::Rectangle( DIALOG_BORDER, EDIT2_Y_POS, EDIT_WIDTH, BOX_HEIGHT2 ), + aProps ); + } + { // Text (MultiLineEdit) <description> + uno::Sequence< beans::NamedValue > aProps(7); + + setProperty( aProps, 0, UNISTRING("Text"), uno::Any( rtl::OUString() ) ); + setProperty( aProps, 1, UNISTRING("Border"), uno::Any( sal_Int16( 0 ) ) ); + setProperty( aProps, 2, UNISTRING("PaintTransparent"), uno::Any( true ) ); + setProperty( aProps, 3, UNISTRING("MultiLine"), uno::Any( true ) ); + setProperty( aProps, 4, UNISTRING("ReadOnly"), uno::Any( true ) ); + setProperty( aProps, 5, UNISTRING("AutoVScroll"), uno::Any( true ) ); + setProperty( aProps, 6, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_DESCRIPTION ) ) ); + + insertControlModel( xControlModel, EDIT_FIELD_MODEL, TEXT_DESCRIPTION, + awt::Rectangle( DIALOG_BORDER + TEXT_OFFSET, + EDIT2_Y_POS + 2*TEXT_OFFSET, + EDIT_WIDTH - 3*TEXT_OFFSET, + BOX_HEIGHT2 - 3*TEXT_OFFSET ), + aProps ); + } + { // @see awt/UnoControlFixedLineModel.idl + uno::Sequence< beans::NamedValue > aProps(1); + + setProperty( aProps, 0, UNISTRING("Orientation"), uno::Any( sal_Int32( 0 ) ) ); + + insertControlModel( xControlModel, FIXED_LINE_MODEL, UNISTRING("fixedLine"), + awt::Rectangle( 0, BUTTON_BAR_Y_POS, DIALOG_WIDTH, 5 ), + aProps ); + } + { // close button // @see awt/UnoControlButtonModel.idl + uno::Sequence< beans::NamedValue > aProps(5); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + // [property] short PushButtonType + // with own "ButtonActionListener" + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) ); + // with default ActionListener => endDialog(). + // setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_CANCEL) ) ); + // [property] string Label // only if PushButtonType_STANDARD + setProperty( aProps, 3, UNISTRING("Label"), uno::Any( msClose ) ); + setProperty( aProps, 4, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_CLOSE ) ) ); + + insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[ CLOSE_BUTTON ], + awt::Rectangle( CLOSE_BTN_X, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ), + aProps ); + } + { // install button + uno::Sequence< beans::NamedValue > aProps(5); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) ); + setProperty( aProps, 3, UNISTRING("Label"), uno::Any( msInstall ) ); + setProperty( aProps, 4, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_INSTALL ) ) ); + + insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[INSTALL_BUTTON], + awt::Rectangle( INSTALL_BTN_X, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ), + aProps ); + } + { // download button + uno::Sequence< beans::NamedValue > aProps(5); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) ); + setProperty( aProps, 3, UNISTRING("Label"), uno::Any( msDownload ) ); + setProperty( aProps, 4, UNISTRING("HelpURL"), uno::Any( UNISTRING( INET_HID_SCHEME ) + rtl::OUString::createFromAscii( HID_CHECK_FOR_UPD_DOWNLOAD ) ) ); + + insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[DOWNLOAD_BUTTON], + awt::Rectangle( DOWNLOAD_BTN_X, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ), + aProps ); + } + { // help button + uno::Sequence< beans::NamedValue > aProps(3); + + setProperty( aProps, 0, UNISTRING("DefaultButton"), uno::Any( false ) ); + setProperty( aProps, 1, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 2, UNISTRING("PushButtonType"), uno::Any( sal_Int16(awt::PushButtonType_HELP) ) ); + + insertControlModel( xControlModel, BUTTON_MODEL, msButtonIDs[HELP_BUTTON], + awt::Rectangle( DIALOG_BORDER, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ), + aProps ); + } + { // @see awt/UnoControlThrobberModel.idl + uno::Sequence< beans::NamedValue > aProps; + + insertControlModel( xControlModel, UNISTRING("com.sun.star.awt.SpinningProgressControlModel"), CTRL_THROBBER, + awt::Rectangle( THROBBER_X_POS, THROBBER_Y_POS, THROBBER_WIDTH, THROBBER_HEIGHT), + aProps ); + } + { // @see awt/UnoControlProgressBarModel.idl + uno::Sequence< beans::NamedValue > aProps(4); + setProperty( aProps, 0, UNISTRING("Enabled"), uno::Any( true ) ); + setProperty( aProps, 1, UNISTRING("ProgressValue"), uno::Any( sal_Int32( 0 ) ) ); + setProperty( aProps, 2, UNISTRING("ProgressValueMax"), uno::Any( sal_Int32( 100 ) ) ); + setProperty( aProps, 3, UNISTRING("ProgressValueMin"), uno::Any( sal_Int32( 0 ) ) ); + + insertControlModel( xControlModel, UNISTRING("com.sun.star.awt.UnoControlProgressBarModel"), CTRL_PROGRESS, + awt::Rectangle( PROGRESS_X_POS, PROGRESS_Y_POS, PROGRESS_WIDTH, PROGRESS_HEIGHT ), + aProps); + } + + uno::Reference< awt::XControl > xControl( + xFactory->createInstanceWithContext( UNISTRING("com.sun.star.awt.UnoControlDialog"), mxContext), + uno::UNO_QUERY_THROW ); + xControl->setModel( xControlModel ); + + if ( mbVisible == false ) + { + uno::Reference< awt::XWindow > xWindow( xControl, uno::UNO_QUERY ); + + if ( xWindow.is() ) + xWindow->setVisible( false ); + } + + xControl->createPeer( NULL, NULL ); + { + uno::Reference< awt::XControlContainer > xContainer (xControl, uno::UNO_QUERY); + for ( int i = 0; i < HELP_BUTTON; i++ ) + { + uno::Reference< awt::XButton > xButton ( xContainer->getControl( msButtonIDs[i] ), uno::UNO_QUERY); + if (xButton.is()) + { + xButton->setActionCommand( msButtonIDs[i] ); + xButton->addActionListener( this ); + } + } + } + + mxUpdDlg.set( xControl, uno::UNO_QUERY_THROW ); + mnLastCtrlState = -1; +} diff --git a/extensions/source/update/check/updatehdl.hrc b/extensions/source/update/check/updatehdl.hrc new file mode 100644 index 000000000000..ad3d472eaf68 --- /dev/null +++ b/extensions/source/update/check/updatehdl.hrc @@ -0,0 +1,83 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + #define RID_UPDATE_HDL_START 1200 + + #define RID_UPDATE_STR_CHECKING RID_UPDATE_HDL_START + 1 + #define RID_UPDATE_STR_NO_UPD_FOUND RID_UPDATE_HDL_START + 2 + #define RID_UPDATE_STR_UPD_FOUND RID_UPDATE_HDL_START + 3 + #define RID_UPDATE_STR_DLG_TITLE RID_UPDATE_HDL_START + 4 + #define RID_UPDATE_STR_DOWNLOAD_ERR RID_UPDATE_HDL_START + 5 + #define RID_UPDATE_STR_DOWNLOAD_WARN RID_UPDATE_HDL_START + 6 + #define RID_UPDATE_STR_DOWNLOADING RID_UPDATE_HDL_START + 7 + #define RID_UPDATE_STR_READY_INSTALL RID_UPDATE_HDL_START + 8 + #define RID_UPDATE_STR_CANCEL_TITLE RID_UPDATE_HDL_START + 9 + #define RID_UPDATE_STR_CANCEL_DOWNLOAD RID_UPDATE_HDL_START + 10 + #define RID_UPDATE_STR_BEGIN_INSTALL RID_UPDATE_HDL_START + 11 + #define RID_UPDATE_STR_INSTALL_NOW RID_UPDATE_HDL_START + 12 + #define RID_UPDATE_STR_INSTALL_LATER RID_UPDATE_HDL_START + 13 + #define RID_UPDATE_STR_CHECKING_ERR RID_UPDATE_HDL_START + 14 + #define RID_UPDATE_STR_OVERWRITE_WARNING RID_UPDATE_HDL_START + 15 + #define RID_UPDATE_STR_DOWNLOAD_PAUSE RID_UPDATE_HDL_START + 16 + #define RID_UPDATE_STR_DOWNLOAD_UNAVAIL RID_UPDATE_HDL_START + 17 + #define RID_UPDATE_STR_PERCENT RID_UPDATE_HDL_START + 18 + #define RID_UPDATE_STR_DOWNLOAD_DESCR RID_UPDATE_HDL_START + 19 + #define RID_UPDATE_STR_INSTALL_ERROR RID_UPDATE_HDL_START + 20 + #define RID_UPDATE_STR_RELOAD_WARNING RID_UPDATE_HDL_START + 21 + #define RID_UPDATE_STR_RELOAD_RELOAD RID_UPDATE_HDL_START + 22 + #define RID_UPDATE_STR_RELOAD_CONTINUE RID_UPDATE_HDL_START + 23 + + #define RID_UPDATE_FT_DESCRIPTION RID_UPDATE_HDL_START + 25 + #define RID_UPDATE_FT_STATUS RID_UPDATE_HDL_START + 26 + + #define RID_UPDATE_BTN_CLOSE RID_UPDATE_HDL_START + 30 + #define RID_UPDATE_BTN_DOWNLOAD RID_UPDATE_HDL_START + 31 + #define RID_UPDATE_BTN_INSTALL RID_UPDATE_HDL_START + 32 + #define RID_UPDATE_BTN_PAUSE RID_UPDATE_HDL_START + 33 + #define RID_UPDATE_BTN_RESUME RID_UPDATE_HDL_START + 34 + #define RID_UPDATE_BTN_CANCEL RID_UPDATE_HDL_START + 35 + + #define RID_UPDATE_BUBBLE_TEXT_START RID_UPDATE_HDL_START + 40 + #define RID_UPDATE_BUBBLE_UPDATE_AVAIL RID_UPDATE_BUBBLE_TEXT_START + 0 + #define RID_UPDATE_BUBBLE_UPDATE_NO_DOWN RID_UPDATE_BUBBLE_TEXT_START + 1 + #define RID_UPDATE_BUBBLE_AUTO_START RID_UPDATE_BUBBLE_TEXT_START + 2 + #define RID_UPDATE_BUBBLE_DOWNLOADING RID_UPDATE_BUBBLE_TEXT_START + 3 + #define RID_UPDATE_BUBBLE_DOWNLOAD_PAUSED RID_UPDATE_BUBBLE_TEXT_START + 4 + #define RID_UPDATE_BUBBLE_ERROR_DOWNLOADING RID_UPDATE_BUBBLE_TEXT_START + 5 + #define RID_UPDATE_BUBBLE_DOWNLOAD_AVAIL RID_UPDATE_BUBBLE_TEXT_START + 6 + #define RID_UPDATE_BUBBLE_EXT_UPD_AVAIL RID_UPDATE_BUBBLE_TEXT_START + 7 + + #define RID_UPDATE_BUBBLE_T_TEXT_START RID_UPDATE_HDL_START + 50 + #define RID_UPDATE_BUBBLE_T_UPDATE_AVAIL RID_UPDATE_BUBBLE_T_TEXT_START + 0 + #define RID_UPDATE_BUBBLE_T_UPDATE_NO_DOWN RID_UPDATE_BUBBLE_T_TEXT_START + 1 + #define RID_UPDATE_BUBBLE_T_AUTO_START RID_UPDATE_BUBBLE_T_TEXT_START + 2 + #define RID_UPDATE_BUBBLE_T_DOWNLOADING RID_UPDATE_BUBBLE_T_TEXT_START + 3 + #define RID_UPDATE_BUBBLE_T_DOWNLOAD_PAUSED RID_UPDATE_BUBBLE_T_TEXT_START + 4 + #define RID_UPDATE_BUBBLE_T_ERROR_DOWNLOADING RID_UPDATE_BUBBLE_T_TEXT_START + 5 + #define RID_UPDATE_BUBBLE_T_DOWNLOAD_AVAIL RID_UPDATE_BUBBLE_T_TEXT_START + 6 + #define RID_UPDATE_BUBBLE_T_EXT_UPD_AVAIL RID_UPDATE_BUBBLE_T_TEXT_START + 7 + diff --git a/extensions/source/update/check/updatehdl.hxx b/extensions/source/update/check/updatehdl.hxx new file mode 100644 index 000000000000..bd525853dda1 --- /dev/null +++ b/extensions/source/update/check/updatehdl.hxx @@ -0,0 +1,221 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef INCLUDED_UPDATE_HDL_HXX +#define INCLUDED_UPDATE_HDL_HXX + +#include "boost/utility.hpp" +#include <osl/mutex.hxx> +#include "com/sun/star/uno/Any.h" +#ifndef _COM_SUN_STAR_UNO_REFERENCE_HPP_ +#include "com/sun/star/uno/Reference.h" +#endif +#include "com/sun/star/uno/XComponentContext.hpp" +#include "com/sun/star/awt/Rectangle.hpp" +#include "com/sun/star/awt/XActionListener.hpp" +#include "com/sun/star/awt/XControlModel.hpp" +#include "com/sun/star/awt/XDialog.hpp" +#include "com/sun/star/awt/XTopWindowListener.hpp" +#include "com/sun/star/beans/NamedValue.hpp" +#include "com/sun/star/frame/XTerminateListener.hpp" +#include <com/sun/star/resource/XResourceBundle.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include "cppuhelper/implbase4.hxx" + +#include "actionlistener.hxx" + +enum DialogControls +{ + CANCEL_BUTTON = 0, + PAUSE_BUTTON, + RESUME_BUTTON, + INSTALL_BUTTON, + DOWNLOAD_BUTTON, + CLOSE_BUTTON, + HELP_BUTTON, + BUTTON_COUNT, + THROBBER_CTRL, + PROGRESS_CTRL +}; + +enum UpdateState { + UPDATESTATE_CHECKING = 0, + UPDATESTATE_ERROR_CHECKING, + UPDATESTATE_NO_UPDATE_AVAIL, + UPDATESTATE_UPDATE_AVAIL, + UPDATESTATE_UPDATE_NO_DOWNLOAD, + UPDATESTATE_AUTO_START, + UPDATESTATE_DOWNLOADING, + UPDATESTATE_DOWNLOAD_PAUSED, + UPDATESTATE_ERROR_DOWNLOADING, + UPDATESTATE_DOWNLOAD_AVAIL, + UPDATESTATE_EXT_UPD_AVAIL, + UPDATESTATES_COUNT +}; + +class UpdateHandler : ::boost::noncopyable, + public cppu::WeakImplHelper4< com::sun::star::awt::XActionListener, + com::sun::star::awt::XTopWindowListener, + com::sun::star::task::XInteractionHandler, + com::sun::star::frame::XTerminateListener > +{ +private: + com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext > mxContext; + com::sun::star::uno::Reference< com::sun::star::awt::XDialog > mxUpdDlg; + com::sun::star::uno::Reference< com::sun::star::task::XInteractionHandler > mxInteractionHdl; + rtl::Reference< IActionListener > mxActionListener; + + UpdateState meCurState; + UpdateState meLastState; + sal_Int32 mnPercent; + short mnLastCtrlState; + bool mbDownloadBtnHasDots; + bool mbVisible; + bool mbStringsLoaded; + bool mbMinimized; + bool mbListenerAdded; + mutable bool mbShowsMessageBox; + + osl::Mutex maMutex; + + rtl::OUString msNextVersion; + rtl::OUString msDownloadPath; + rtl::OUString msDownloadFile; + rtl::OUString msDescriptionMsg; + rtl::OUString msChecking; // RID_UPDATE_STR_CHECKING + rtl::OUString msCheckingError; // RID_UPDATE_STR_CHECKING_ERR + rtl::OUString msNoUpdFound; // RID_UPDATE_STR_NO_UPD_FOUND + rtl::OUString msUpdFound; // RID_UPDATE_STR_UPD_FOUND + rtl::OUString msDlgTitle; // RID_UPDATE_STR_DLG_TITLE + rtl::OUString msDownloadPause; // RID_UPDATE_STR_DOWNLOAD_PAUSE + rtl::OUString msDownloadError; // RID_UPDATE_STR_DOWNLOAD_ERR + rtl::OUString msDownloadWarning; // RID_UPDATE_STR_DOWNLOAD_WARN + rtl::OUString msDownloadDescr; // RID_UPDATE_STR_DOWNLOAD_WARN + rtl::OUString msDownloadNotAvail; // RID_UPDATE_STR_DOWNLOAD_UNAVAIL + rtl::OUString msDownloading; // RID_UPDATE_STR_DOWNLOADING + rtl::OUString msReady2Install; // RID_UPDATE_STR_READY_INSTALL + rtl::OUString msCancelTitle; // RID_UPDATE_STR_CANCEL_TITLE + rtl::OUString msCancelMessage; // RID_UPDATE_STR_CANCEL_DOWNLOAD + rtl::OUString msInstallMessage; // RID_UPDATE_STR_BEGIN_INSTALL + rtl::OUString msInstallNow; // RID_UPDATE_STR_INSTALL_NOW + rtl::OUString msInstallLater; // RID_UPDATE_STR_INSTALL_LATER + rtl::OUString msInstallError; // RID_UPDATE_STR_INSTALL_ERROR + rtl::OUString msOverwriteWarning; // RID_UPDATE_STR_OVERWRITE_WARNING + rtl::OUString msPercent; // RID_UPDATE_STR_PERCENT + rtl::OUString msReloadWarning; // RID_UPDATE_STR_OVERWRITE_WARNING + rtl::OUString msReloadReload; // RID_UPDATE_STR_OVERWRITE_WARNING + rtl::OUString msReloadContinue; // RID_UPDATE_STR_OVERWRITE_WARNING + rtl::OUString msStatusFL; // RID_UPDATE_FT_STATUS + rtl::OUString msDescription; // RID_UPDATE_FT_DESCRIPTION + rtl::OUString msClose; // RID_UPDATE_BTN_CLOSE + rtl::OUString msDownload; // RID_UPDATE_BTN_DOWNLOAD + rtl::OUString msInstall; // RID_UPDATE_BTN_INSTALL + rtl::OUString msPauseBtn; // RID_UPDATE_BTN_PAUSE + rtl::OUString msResumeBtn; // RID_UPDATE_BTN_RESUME + rtl::OUString msCancelBtn; // RID_UPDATE_BTN_CANCEL + rtl::OUString msButtonIDs[ BUTTON_COUNT ]; + rtl::OUString msBubbleTexts[ UPDATESTATES_COUNT ]; + rtl::OUString msBubbleTitles[ UPDATESTATES_COUNT ]; + + void createDialog(); + void updateState( UpdateState eNewState ); + void startThrobber( bool bStart = true ); + void setControlProperty( const rtl::OUString &rCtrlName, + const rtl::OUString &rPropName, + const com::sun::star::uno::Any &rPropValue ); + void showControl( const rtl::OUString &rCtrlName, bool bShow = true ); + void showControls( short nControls ); + void focusControl( DialogControls eID ); + void enableControls( short nCtrlState ); + void setDownloadBtnLabel( bool bAppendDots ); + void loadStrings(); + rtl::OUString loadString( const com::sun::star::uno::Reference< com::sun::star::resource::XResourceBundle > xBundle, + sal_Int32 nResourceId ) const; + rtl::OUString substVariables( const rtl::OUString &rSource ) const; + static void setProperty( com::sun::star::uno::Sequence< com::sun::star::beans::NamedValue > &rProps, + const int nIndex, const rtl::OUString &rPropName, const com::sun::star::uno::Any &rPropValue ) + { rProps[ nIndex ].Name = rPropName; rProps[ nIndex ].Value = rPropValue; } + static void insertControlModel( com::sun::star::uno::Reference< com::sun::star::awt::XControlModel > & rxDialogModel, + rtl::OUString const & rServiceName, + rtl::OUString const & rControlName, + com::sun::star::awt::Rectangle const & rPosSize, + com::sun::star::uno::Sequence< com::sun::star::beans::NamedValue > const & rProps ); + + void setFullVersion( rtl::OUString& rString ); + void searchAndReplaceAll( rtl::OUString &rText, const rtl::OUString &rWhat, const rtl::OUString &rWith ) const; + +public: + UpdateHandler( const com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext > & rxContext, + const rtl::Reference< IActionListener > & rxActionListener ); + virtual ~UpdateHandler(); + + bool isVisible() const; + bool isMinimized() const { return mbMinimized; } + void setVisible( bool bVisible = true ); + void setProgress( sal_Int32 nPercent ); + void setNextVersion( const rtl::OUString &rNextVersion ) { msNextVersion = rNextVersion; } + void setDownloadPath( const rtl::OUString &rPath ) { msDownloadPath = rPath; } + void setDownloadFile( const rtl::OUString &rPath ); + void setErrorMessage( const rtl::OUString &rErrorMsg ); + void setDescription( const rtl::OUString &rDescription ){ msDescriptionMsg = rDescription; } + + void setState( UpdateState eState ); + rtl::OUString getBubbleText( UpdateState eState ); + rtl::OUString getBubbleTitle( UpdateState eState ); + rtl::OUString getDefaultInstErrMsg(); + bool showWarning( const rtl::OUString &rWarning ) const; + bool showWarning( const rtl::OUString &rWarning, const rtl::OUString& rBtnText_1, const rtl::OUString& rBtnText_2 ) const; + bool showOverwriteWarning( const rtl::OUString &rFileName ) const; + bool showOverwriteWarning() const; + + // Allows runtime exceptions to be thrown by const methods + inline SAL_CALL operator com::sun::star::uno::Reference< com::sun::star::uno::XInterface > () const + { return const_cast< cppu::OWeakObject * > (static_cast< cppu::OWeakObject const * > (this)); }; + + // XActionListener + virtual void SAL_CALL disposing( const com::sun::star::lang::EventObject &rObj ) throw( com::sun::star::uno::RuntimeException ); + virtual void SAL_CALL actionPerformed( com::sun::star::awt::ActionEvent const & rEvent) throw( com::sun::star::uno::RuntimeException ); + + // XTopWindowListener + virtual void SAL_CALL windowOpened( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL windowClosing( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL windowClosed( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL windowMinimized( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL windowNormalized( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL windowActivated( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL windowDeactivated( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); + + // XInteractionHandler + virtual void SAL_CALL handle( const com::sun::star::uno::Reference< com::sun::star::task::XInteractionRequest >& Request ) + throw( com::sun::star::uno::RuntimeException ); + + // XTerminateListener + virtual void SAL_CALL queryTermination( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::frame::TerminationVetoException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL notifyTermination( const ::com::sun::star::lang::EventObject& e ) throw (::com::sun::star::uno::RuntimeException); +}; + +#endif /* INCLUDED_UPDATE_HDL_HXX */ diff --git a/extensions/source/update/check/updatehdl.src b/extensions/source/update/check/updatehdl.src new file mode 100644 index 000000000000..58f88b7e4852 --- /dev/null +++ b/extensions/source/update/check/updatehdl.src @@ -0,0 +1,264 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "updatehdl.hrc" + +String RID_UPDATE_STR_CHECKING +{ + Text [ en-US ] = "Checking..."; +}; + +String RID_UPDATE_STR_CHECKING_ERR +{ + Text [ en-US ] = "Checking for an update failed."; +}; + +String RID_UPDATE_STR_NO_UPD_FOUND +{ + Text [ en-US ] = "%PRODUCTNAME %PRODUCTVERSION is up to date."; +}; + +String RID_UPDATE_STR_UPD_FOUND +{ + Text [ en-US ] = "%PRODUCTNAME %NEXTVERSION is available.\n\nThe installed version is %PRODUCTNAME %PRODUCTVERSION.\n\nNote: Before downloading an update, please ensure that you have sufficient access rights to install it.\nA password, usually the administrator's or root password, may be required."; +}; + +String RID_UPDATE_STR_DLG_TITLE +{ + Text [ en-US ] = "Check for Updates"; +}; + +String RID_UPDATE_STR_DOWNLOAD_PAUSE +{ + Text [ en-US ] = "Downloading %PRODUCTNAME %NEXTVERSION paused at..."; +}; + +String RID_UPDATE_STR_DOWNLOAD_ERR +{ + Text [ en-US ] = "Downloading %PRODUCTNAME %NEXTVERSION stalled at"; +}; + +String RID_UPDATE_STR_DOWNLOAD_WARN +{ + Text [ en-US ] = "The download location is: %DOWNLOAD_PATH.\n\nUnder Tools – Options... - %PRODUCTNAME – Online Update you can change the download location."; +}; + +String RID_UPDATE_STR_DOWNLOAD_DESCR +{ + Text [ en-US ] = "%FILE_NAME has been downloaded to %DOWNLOAD_PATH."; +}; + +String RID_UPDATE_STR_DOWNLOAD_UNAVAIL +{ + Text [ en-US ] = "The automatic download of the update is currently not available.\n\nClick 'Download...' to download %PRODUCTNAME %NEXTVERSION manually from the web site."; +}; + +String RID_UPDATE_STR_DOWNLOADING +{ + Text [ en-US ] = "Downloading %PRODUCTNAME %NEXTVERSION..."; +}; + +String RID_UPDATE_STR_READY_INSTALL +{ + Text [ en-US ] = "Download of %PRODUCTNAME %NEXTVERSION completed. Ready for installation."; +}; + +String RID_UPDATE_STR_CANCEL_TITLE +{ + Text [ en-US ] = "%PRODUCTNAME %PRODUCTVERSION"; +}; + +String RID_UPDATE_STR_CANCEL_DOWNLOAD +{ + Text [ en-US ] = "Do you really want to cancel the download?"; +}; + +String RID_UPDATE_STR_BEGIN_INSTALL +{ + Text [ en-US ] = "To install the update, %PRODUCTNAME %PRODUCTVERSION needs to be closed. Do you want to install the update now?"; +}; + +String RID_UPDATE_STR_INSTALL_NOW +{ + Text [ en-US ] = "Install ~now"; +}; + +String RID_UPDATE_STR_INSTALL_LATER +{ + Text [ en-US ] = "Install ~later"; +}; + +String RID_UPDATE_STR_INSTALL_ERROR +{ + Text [ en-US ] = "Could not run the installer application, please run %FILE_NAME in %DOWNLOAD_PATH manually."; +}; + +String RID_UPDATE_STR_OVERWRITE_WARNING +{ + Text [ en-US ] = "A file with that name already exists! Do you want to overwrite the existing file?"; +}; + +String RID_UPDATE_STR_RELOAD_WARNING +{ + Text [ en-US ] = "A file with the name '%FILENAME' already exists in '%DOWNLOAD_PATH'! Do you want to continue with the download or delete and reload the file?"; +}; + +String RID_UPDATE_STR_RELOAD_RELOAD +{ + Text [ en-US ] = "Reload File"; +}; + +String RID_UPDATE_STR_RELOAD_CONTINUE +{ + Text [ en-US ] = "Continue"; +}; + +String RID_UPDATE_STR_PERCENT +{ + Text [ en-US ] = "%PERCENT%"; +}; + +String RID_UPDATE_FT_STATUS +{ + Text [ en-US ] = "Status"; +}; + +String RID_UPDATE_FT_DESCRIPTION +{ + Text [ en-US ] = "Description"; +}; + +String RID_UPDATE_BTN_CLOSE +{ + Text [ en-US ] = "Close"; +}; + +String RID_UPDATE_BTN_DOWNLOAD +{ + Text [ en-US ] = "~Download"; +}; + +String RID_UPDATE_BTN_INSTALL +{ + Text [ en-US ] = "~Install"; +}; + +String RID_UPDATE_BTN_PAUSE +{ + Text [ en-US ] = "~Pause"; +}; + +String RID_UPDATE_BTN_RESUME +{ + Text [ en-US ] = "~Resume"; +}; + +String RID_UPDATE_BTN_CANCEL +{ + Text [ en-US ] = "Cancel"; +}; + +String RID_UPDATE_BUBBLE_T_UPDATE_AVAIL +{ + Text [ en-US ] = "%PRODUCTNAME update available"; +}; + +String RID_UPDATE_BUBBLE_UPDATE_AVAIL +{ + Text [ en-US ] = "Click here to start the download."; +}; + +String RID_UPDATE_BUBBLE_T_UPDATE_NO_DOWN +{ + Text [ en-US ] = "%PRODUCTNAME update available"; +}; + +String RID_UPDATE_BUBBLE_UPDATE_NO_DOWN +{ + Text [ en-US ] = "Click here for more information."; +}; + +String RID_UPDATE_BUBBLE_T_AUTO_START +{ + Text [ en-US ] = "%PRODUCTNAME update available"; +}; + +String RID_UPDATE_BUBBLE_AUTO_START +{ + Text [ en-US ] = "Download of update begins."; +}; + +String RID_UPDATE_BUBBLE_T_DOWNLOADING +{ + Text [ en-US ] = "Download of update in progress"; +}; + +String RID_UPDATE_BUBBLE_DOWNLOADING +{ + Text [ en-US ] = ""; +}; + +String RID_UPDATE_BUBBLE_T_DOWNLOAD_PAUSED +{ + Text [ en-US ] = "Download of update paused"; +}; + +String RID_UPDATE_BUBBLE_DOWNLOAD_PAUSED +{ + Text [ en-US ] = "Click here to resume."; +}; + +String RID_UPDATE_BUBBLE_T_ERROR_DOWNLOADING +{ + Text [ en-US ] = "Download of update stalled"; +}; + +String RID_UPDATE_BUBBLE_ERROR_DOWNLOADING +{ + Text [ en-US ] = "Click here for more information."; +}; + +String RID_UPDATE_BUBBLE_T_DOWNLOAD_AVAIL +{ + Text [ en-US ] = "Download of update completed"; +}; + +String RID_UPDATE_BUBBLE_DOWNLOAD_AVAIL +{ + Text [ en-US ] = "Click here to start the installation."; +}; + +String RID_UPDATE_BUBBLE_T_EXT_UPD_AVAIL +{ + Text [ en-US ] = "Updates for extensions available"; +}; + +String RID_UPDATE_BUBBLE_EXT_UPD_AVAIL +{ + Text [ en-US ] = "Click here for more information."; +}; + diff --git a/extensions/source/update/check/updateinfo.hxx b/extensions/source/update/check/updateinfo.hxx new file mode 100644 index 000000000000..3249a59ad7d0 --- /dev/null +++ b/extensions/source/update/check/updateinfo.hxx @@ -0,0 +1,86 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _UPDATE_INFO_INCLUDED_ +#define _UPDATE_INFO_INCLUDED_ + +#include <rtl/ustring.hxx> +#include <vector> + +struct DownloadSource +{ + bool IsDirect; + rtl::OUString URL; + + DownloadSource(bool bIsDirect, const rtl::OUString& aURL) : IsDirect(bIsDirect), URL(aURL) {}; + DownloadSource(const DownloadSource& ds) : IsDirect(ds.IsDirect), URL(ds.URL) {}; + + DownloadSource & operator=( const DownloadSource & ds ) { IsDirect = ds.IsDirect; URL = ds.URL; return *this; }; +}; + +struct ReleaseNote +{ + sal_uInt8 Pos; + rtl::OUString URL; + sal_uInt8 Pos2; + rtl::OUString URL2; + + ReleaseNote(sal_uInt8 pos, const rtl::OUString aURL) : Pos(pos), URL(aURL), Pos2(0), URL2() {}; + ReleaseNote(sal_uInt8 pos, const rtl::OUString aURL, sal_uInt8 pos2, const rtl::OUString aURL2) : Pos(pos), URL(aURL), Pos2(pos2), URL2(aURL2) {}; + + ReleaseNote(const ReleaseNote& rn) :Pos(rn.Pos), URL(rn.URL), Pos2(rn.Pos2), URL2(rn.URL2) {}; + ReleaseNote & operator=( const ReleaseNote& rn) { Pos=rn.Pos; URL=rn.URL; Pos2=rn.Pos2; URL2=rn.URL2; return *this; }; +}; + +struct UpdateInfo +{ + rtl::OUString BuildId; + rtl::OUString Version; + rtl::OUString Description; + std::vector< DownloadSource > Sources; + std::vector< ReleaseNote > ReleaseNotes; + + UpdateInfo() : BuildId(), Version(), Description(), Sources(), ReleaseNotes() {}; + UpdateInfo(const UpdateInfo& ui) : BuildId(ui.BuildId), Version(ui.Version), Description(ui.Description), Sources(ui.Sources), ReleaseNotes(ui.ReleaseNotes) {}; + inline UpdateInfo & operator=( const UpdateInfo& ui ); +}; + +UpdateInfo & UpdateInfo::operator=( const UpdateInfo& ui ) +{ + BuildId = ui.BuildId; + Version = ui.Version; + Description = ui.Description; + Sources = ui.Sources; + ReleaseNotes = ui.ReleaseNotes; + return *this; +} + + +// Returns the URL of the release note for the given position +rtl::OUString getReleaseNote(const UpdateInfo& rInfo, sal_uInt8 pos, bool autoDownloadEnabled=false); + +#endif diff --git a/extensions/source/update/check/updateprotocol.cxx b/extensions/source/update/check/updateprotocol.cxx new file mode 100755 index 000000000000..fa460ace702c --- /dev/null +++ b/extensions/source/update/check/updateprotocol.cxx @@ -0,0 +1,335 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <com/sun/star/xml/xpath/XXPathAPI.hpp> + +#include "updateprotocol.hxx" +#include "updatecheckconfig.hxx" + +#ifndef _COM_SUN_STAR_DEPLOYMENT_UPDATEINFORMATINENTRY_HPP_ +#include <com/sun/star/deployment/UpdateInformationEntry.hpp> +#endif +#include <com/sun/star/deployment/XPackageInformationProvider.hpp> + + +#include <rtl/ref.hxx> +#include <rtl/uri.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/bootstrap.hxx> +#include <osl/process.h> + +#include <cppuhelper/implbase1.hxx> + +namespace css = com::sun::star ; +namespace container = css::container ; +namespace deployment = css::deployment ; +namespace lang = css::lang ; +namespace uno = css::uno ; +namespace task = css::task ; +namespace xml = css::xml ; + +#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s)) + +//------------------------------------------------------------------------------ + +static bool +getBootstrapData( + uno::Sequence< ::rtl::OUString > & rRepositoryList, + ::rtl::OUString & rBuildID, + ::rtl::OUString & rInstallSetID) +{ + rBuildID = UNISTRING( "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("version") ":ProductBuildid}" ); + rtl::Bootstrap::expandMacros( rBuildID ); + if ( ! rBuildID.getLength() ) + return false; + + rInstallSetID = UNISTRING( "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version") ":UpdateID}" ); + rtl::Bootstrap::expandMacros( rInstallSetID ); + if ( ! rInstallSetID.getLength() ) + return false; + + rtl::OUString aValue( UNISTRING( "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version") ":UpdateURL}" ) ); + rtl::Bootstrap::expandMacros( aValue ); + + if( aValue.getLength() > 0 ) + { + rRepositoryList.realloc(1); + rRepositoryList[0] = aValue; + } + + return true; +} + +//------------------------------------------------------------------------------ + +// Returns 'true' if successfully connected to the update server +bool +checkForUpdates( + UpdateInfo& o_rUpdateInfo, + uno::Reference< uno::XComponentContext > const & rxContext, + uno::Reference< task::XInteractionHandler > const & rxInteractionHandler, + const uno::Reference< deployment::XUpdateInformationProvider >& rUpdateInfoProvider) +{ + OSL_TRACE("checking for updates ..\n"); + + ::rtl::OUString myArch; + ::rtl::OUString myOS; + + rtl::Bootstrap::get(UNISTRING("_OS"), myOS); + rtl::Bootstrap::get(UNISTRING("_ARCH"), myArch); + + uno::Sequence< ::rtl::OUString > aRepositoryList; + ::rtl::OUString aBuildID; + ::rtl::OUString aInstallSetID; + + if( ! ( getBootstrapData(aRepositoryList, aBuildID, aInstallSetID) && (aRepositoryList.getLength() > 0) ) ) + return false; + + if( !rxContext.is() ) + throw uno::RuntimeException( + UNISTRING( "checkForUpdates: empty component context" ), uno::Reference< uno::XInterface >() ); + + OSL_ASSERT( rxContext->getServiceManager().is() ); + + // XPath implementation + uno::Reference< xml::xpath::XXPathAPI > xXPath( + rxContext->getServiceManager()->createInstanceWithContext( UNISTRING( "com.sun.star.xml.xpath.XPathAPI" ), rxContext ), + uno::UNO_QUERY_THROW); + + xXPath->registerNS( UNISTRING("inst"), UNISTRING("http://installation.openoffice.org/description") ); + + if( rxInteractionHandler.is() ) + rUpdateInfoProvider->setInteractionHandler(rxInteractionHandler); + + try + { + uno::Reference< container::XEnumeration > aUpdateInfoEnumeration = + rUpdateInfoProvider->getUpdateInformationEnumeration( aRepositoryList, aInstallSetID ); + + if ( !aUpdateInfoEnumeration.is() ) + return false; // something went wrong .. + + rtl::OUStringBuffer aBuffer; + aBuffer.appendAscii("/child::inst:description[inst:os=\'"); + aBuffer.append( myOS ); + aBuffer.appendAscii("\' and inst:arch=\'"); + aBuffer.append( myArch ); + aBuffer.appendAscii("\' and inst:buildid>"); + aBuffer.append( aBuildID ); + aBuffer.appendAscii("]"); + + rtl::OUString aXPathExpression = aBuffer.makeStringAndClear(); + + while( aUpdateInfoEnumeration->hasMoreElements() ) + { + deployment::UpdateInformationEntry aEntry; + + if( aUpdateInfoEnumeration->nextElement() >>= aEntry ) + { + uno::Reference< xml::dom::XNode > xNode( aEntry.UpdateDocument.get() ); + uno::Reference< xml::dom::XNodeList > xNodeList; + try { + xNodeList = xXPath->selectNodeList(xNode, aXPathExpression + + UNISTRING("/inst:update/attribute::src")); + } catch (css::xml::xpath::XPathException &) { + // ignore + } + +/* + o_rUpdateInfo.Sources.push_back( DownloadSource(true, + UNISTRING("http://openoffice.bouncer.osuosl.org/?product=OpenOffice.org&os=solarissparcwjre&lang=en-US&version=2.2.1") ) ); +*/ + + sal_Int32 i, imax = xNodeList->getLength(); + for( i = 0; i < imax; ++i ) + { + uno::Reference< xml::dom::XNode > xNode2( xNodeList->item(i) ); + + if( xNode2.is() ) + { + uno::Reference< xml::dom::XElement > xParent(xNode2->getParentNode(), uno::UNO_QUERY_THROW); + rtl::OUString aType = xParent->getAttribute(UNISTRING("type")); + bool bIsDirect = ( sal_False == aType.equalsIgnoreAsciiCaseAscii("text/html") ); + + o_rUpdateInfo.Sources.push_back( DownloadSource(bIsDirect, xNode2->getNodeValue()) ); + } + } + + uno::Reference< xml::dom::XNode > xNode2; + try { + xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression + + UNISTRING("/inst:version/text()")); + } catch (css::xml::xpath::XPathException &) { + // ignore + } + + if( xNode2.is() ) + o_rUpdateInfo.Version = xNode2->getNodeValue(); + + try { + xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression + + UNISTRING("/inst:buildid/text()")); + } catch (css::xml::xpath::XPathException &) { + // ignore + } + + if( xNode2.is() ) + o_rUpdateInfo.BuildId = xNode2->getNodeValue(); + + o_rUpdateInfo.Description = aEntry.Description; + + // Release Notes + try { + xNodeList = xXPath->selectNodeList(xNode, aXPathExpression + + UNISTRING("/inst:relnote")); + } catch (css::xml::xpath::XPathException &) { + // ignore + } + imax = xNodeList->getLength(); + for( i = 0; i < imax; ++i ) + { + uno::Reference< xml::dom::XElement > xRelNote(xNodeList->item(i), uno::UNO_QUERY); + if( xRelNote.is() ) + { + sal_Int32 pos = xRelNote->getAttribute(UNISTRING("pos")).toInt32(); + + ReleaseNote aRelNote((sal_uInt8) pos, xRelNote->getAttribute(UNISTRING("src"))); + + if( xRelNote->hasAttribute(UNISTRING("src2")) ) + { + pos = xRelNote->getAttribute(UNISTRING("pos2")).toInt32(); + aRelNote.Pos2 = (sal_Int8) pos; + aRelNote.URL2 = xRelNote->getAttribute(UNISTRING("src2")); + } + + o_rUpdateInfo.ReleaseNotes.push_back(aRelNote); + } + } +/* + o_rUpdateInfo.ReleaseNotes.push_back( + ReleaseNote(1, UNISTRING("http://qa.openoffice.org/tests/online_update_test.html")) + ); +*/ + + if( o_rUpdateInfo.Sources.size() > 0 ) + return true; + } + } + } + catch( ... ) + { + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +bool storeExtensionUpdateInfos( const uno::Reference< uno::XComponentContext > & rxContext, + const uno::Sequence< uno::Sequence< rtl::OUString > > &rUpdateInfos ) +{ + bool bNotify = false; + + if ( rUpdateInfos.hasElements() ) + { + rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext ); + + for ( sal_Int32 i = rUpdateInfos.getLength() - 1; i >= 0; i-- ) + { + bNotify |= aConfig->storeExtensionVersion( rUpdateInfos[i][0], rUpdateInfos[i][1] ); + } + } + return bNotify; +} + +//------------------------------------------------------------------------------ +// Returns 'true' if there are updates for any extension + +bool checkForExtensionUpdates( const uno::Reference< uno::XComponentContext > & rxContext ) +{ + uno::Sequence< uno::Sequence< rtl::OUString > > aUpdateList; + + uno::Reference< deployment::XPackageInformationProvider > xInfoProvider; + try + { + uno::Any aValue( rxContext->getValueByName( + UNISTRING( "/singletons/com.sun.star.deployment.PackageInformationProvider" ) ) ); + OSL_VERIFY( aValue >>= xInfoProvider ); + } + catch( const uno::Exception& ) + { + OSL_ENSURE( false, "checkForExtensionUpdates: could not create the PackageInformationProvider!" ); + } + + if ( !xInfoProvider.is() ) return false; + + aUpdateList = xInfoProvider->isUpdateAvailable( ::rtl::OUString() ); + bool bNotify = storeExtensionUpdateInfos( rxContext, aUpdateList ); + + return bNotify; +} + +//------------------------------------------------------------------------------ +// Returns 'true' if there are any pending updates for any extension (offline check) + +bool checkForPendingUpdates( const uno::Reference< uno::XComponentContext > & rxContext ) +{ + uno::Sequence< uno::Sequence< rtl::OUString > > aExtensionList; + uno::Reference< deployment::XPackageInformationProvider > xInfoProvider; + try + { + uno::Any aValue( rxContext->getValueByName( + UNISTRING( "/singletons/com.sun.star.deployment.PackageInformationProvider" ) ) ); + OSL_VERIFY( aValue >>= xInfoProvider ); + } + catch( const uno::Exception& ) + { + OSL_ENSURE( false, "checkForExtensionUpdates: could not create the PackageInformationProvider!" ); + } + + if ( !xInfoProvider.is() ) return false; + + bool bPendingUpdateFound = false; + + aExtensionList = xInfoProvider->getExtensionList(); + if ( aExtensionList.hasElements() ) + { + rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext ); + + for ( sal_Int32 i = aExtensionList.getLength() - 1; i >= 0; i-- ) + { + bPendingUpdateFound = aConfig->checkExtensionVersion( aExtensionList[i][0], aExtensionList[i][1] ); + if ( bPendingUpdateFound ) + break; + } + } + + return bPendingUpdateFound; +} diff --git a/extensions/source/update/check/updateprotocol.hxx b/extensions/source/update/check/updateprotocol.hxx new file mode 100644 index 000000000000..d99ae9e7af2d --- /dev/null +++ b/extensions/source/update/check/updateprotocol.hxx @@ -0,0 +1,56 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/deployment/UpdateInformationProvider.hpp> + +#include <vector> +#include "updateinfo.hxx" + +// Returns 'true' if successfully connected to the update server +bool checkForUpdates( + UpdateInfo& o_rUpdateInfo, + const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& rxContext, + const ::com::sun::star::uno::Reference< ::com::sun::star::task::XInteractionHandler >& rxInteractionHandler, + const ::com::sun::star::uno::Reference< ::com::sun::star::deployment::XUpdateInformationProvider >& rxProvider +); + +// Returns 'true' if there are updates for any extension +bool checkForExtensionUpdates( + const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& rxContext +); + +bool checkForPendingUpdates( + const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& rxContext +); + +bool storeExtensionUpdateInfos( + const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& rxContext, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< rtl::OUString > > &rUpdateInfos +); + diff --git a/extensions/source/update/check/updateprotocoltest.cxx b/extensions/source/update/check/updateprotocoltest.cxx new file mode 100644 index 000000000000..70623cea6224 --- /dev/null +++ b/extensions/source/update/check/updateprotocoltest.cxx @@ -0,0 +1,94 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" + +#include <cppuhelper/bootstrap.hxx> + +#include "updateprotocol.hxx" + +#include <sal/main.h> +#include <osl/process.h> +#include <stdio.h> + +#define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s)) + +namespace task = ::com::sun::star::task; +namespace uno = ::com::sun::star::uno; + +// ----------------------------------------------------------------------- + +SAL_IMPLEMENT_MAIN() +{ + (void) argv; + (void) argc; + + if( osl_getCommandArgCount() != 0 ) + { + fprintf(stderr, "Usage: updateprotocoltest\n"); + return -1; + } + + // create the initial component context + uno::Reference< uno::XComponentContext > rComponentContext = cppu::defaultBootstrap_InitialComponentContext(); + + // initialize UCB + uno::Sequence< uno::Any > theArguments(2); + theArguments[0] = uno::makeAny( UNISTRING( "Local") ); + theArguments[1] = uno::makeAny( UNISTRING( "Office") ); + + uno::Reference< uno::XInterface > xUCB = + rComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + UNISTRING( "com.sun.star.ucb.UniversalContentBroker" ), + theArguments, + rComponentContext ); + + + rtl::OUString aURL; + rtl::OUString aVersion; + + try + { + if( checkForUpdates(rComponentContext, uno::Reference< task::XInteractionHandler > (), aURL, aVersion) ) + { + OSL_TRACE( "Update found: %s on %s", + rtl::OUStringToOString( aVersion, RTL_TEXTENCODING_UTF8).getStr(), + rtl::OUStringToOString( aURL, RTL_TEXTENCODING_UTF8).getStr()); + } + else + { + OSL_TRACE( "no updates found" ); + } + } + catch( ... ) + { + OSL_TRACE( "unhandled exception caught" ); + } + + return 0; +} diff --git a/extensions/source/update/check/updchk.uno.component b/extensions/source/update/check/updchk.uno.component new file mode 100644 index 000000000000..b1eb32917eaf --- /dev/null +++ b/extensions/source/update/check/updchk.uno.component @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--********************************************************************** +* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* Copyright 2000, 2010 Oracle and/or its affiliates. +* +* OpenOffice.org - a multi-platform office productivity suite +* +* This file is part of OpenOffice.org. +* +* OpenOffice.org is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License version 3 +* only, as published by the Free Software Foundation. +* +* OpenOffice.org is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License version 3 for more details +* (a copy is included in the LICENSE file that accompanied this code). +* +* You should have received a copy of the GNU Lesser General Public License +* version 3 along with OpenOffice.org. If not, see +* <http://www.openoffice.org/license.html> +* for a copy of the LGPLv3 License. +* +**********************************************************************--> + +<component loader="com.sun.star.loader.SharedLibrary" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="vnd.sun.UpdateCheck"> + <service name="com.sun.star.setup.UpdateCheck"/> + </implementation> + <implementation name="vnd.sun.UpdateCheckConfig"> + <service name="com.sun.star.setup.UpdateCheckConfig"/> + </implementation> +</component> diff --git a/extensions/source/update/check/updchk.xml b/extensions/source/update/check/updchk.xml new file mode 100644 index 000000000000..25d5d361e1d0 --- /dev/null +++ b/extensions/source/update/check/updchk.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE module-description PUBLIC "-//StarOffice//DTD ComponentDescription 1.0//EN" "module-description.dtd"> +<module-description xmlns:xlink="http://www.w3.org/1999/xlink"> + <module-name>updchk</module-name> + <component-description> + <author> Oliver Braun </author> + <name>vnd.sun.UpdateCheck</name> + <description> The </description> + <loader-name>com.sun.star.loader.SharedLibrary</loader-name> + <language>c++</language> + <status value="beta"/> + <supported-service>com.sun.star.setup.UpdateCheck</supported-service> + <service-dependency>...</service-dependency> + <type>com.sun.star.beans.NamedValue</type> + <type>com.sun.star.beans.PropertyValue</type> + <type>com.sun.star.container.XNameReplace</type> + <type>com.sun.star.deployment.UpdateInformationProvider</type> + <type>com.sun.star.frame.DispatchResultEvent</type> + <type>com.sun.star.frame.DispatchResultState</type> + <type>com.sun.star.frame.XFrame</type> + <type>com.sun.star.io.XInputStream</type> + <type>com.sun.star.io.XOutputStream</type> + <type>com.sun.star.lang.XComponent</type> + <type>com.sun.star.lang.XMultiComponentFactory</type> + <type>com.sun.star.lang.XMultiServiceFactory</type> + <type>com.sun.star.lang.XServiceInfo</type> + <type>com.sun.star.lang.XSingleComponentFactory</type> + <type>com.sun.star.lang.XSingleServiceFactory</type> + <type>com.sun.star.lang.XTypeProvider</type> + <type>com.sun.star.registry.XRegistryKey</type> + <type>com.sun.star.system.SystemShellExecuteFlags</type> + <type>com.sun.star.system.XSystemShellExecute</type> + <type>com.sun.star.task.XInteractionHandler</type> + <type>com.sun.star.task.XJob</type> + <type>com.sun.star.uno.TypeClass</type> + <type>com.sun.star.uno.XAggregation</type> + <type>com.sun.star.uno.XComponentContext</type> + <type>com.sun.star.uno.XCurrentContext</type> + <type>com.sun.star.uno.XWeak</type> + <type>com.sun.star.util.XChangesBatch</type> + <type>com.sun.star.xml.xpath.XXPathAPI</type> + </component-description> + <project-build-dependency>cppuhelper</project-build-dependency> + <project-build-dependency>cppu</project-build-dependency> + <project-build-dependency>sal</project-build-dependency> + <runtime-module-dependency>cppuhelper3$(COM)</runtime-module-dependency> + <runtime-module-dependency>cppu3</runtime-module-dependency> + <runtime-module-dependency>sal3</runtime-module-dependency> +</module-description> |