From e05a78a3468123a676267ab6a6199ddd4c57e861 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Wed, 26 Jan 2011 23:15:21 +0000 Subject: [PATCH] Fixes multiple issues in the setup and update functionality on Windows. --- resources/install/build.xml | 72 +- .../install/windows/asInvoker.exe.manifest | 21 + .../windows/heat-component-defines.xsl | 2 +- .../install/windows/heat-component-refs.xsl | 2 +- .../install/windows/installer-windows.wxs | 14 +- resources/install/windows/run.bat | 3 - resources/install/windows/run.exe.manifest | 5 - resources/install/windows/setup.exe | Bin 73728 -> 0 bytes resources/install/windows/setup.exe.manifest | 5 - resources/install/windows/up2date.c | 369 --- resources/install/windows/up2date.exe | Bin 32870 -> 0 bytes .../install/windows/up2date.exe.manifest | 5 - src/native/windows/setup/Makefile | 27 + src/native/windows/setup/setup.c | 941 +++++++ src/native/windows/setup/setup.h | 9 + src/native/windows/setup/setup.rc | 34 + .../GeneralConfigurationPanel.java | 5 +- .../updatechecker/UpdateCheckActivator.java | 2322 +++++++++-------- .../updatechecker/updatecheck.manifest.mf | 6 +- 19 files changed, 2396 insertions(+), 1446 deletions(-) create mode 100644 resources/install/windows/asInvoker.exe.manifest delete mode 100755 resources/install/windows/run.bat delete mode 100644 resources/install/windows/run.exe.manifest delete mode 100644 resources/install/windows/setup.exe delete mode 100644 resources/install/windows/setup.exe.manifest delete mode 100644 resources/install/windows/up2date.c delete mode 100644 resources/install/windows/up2date.exe delete mode 100644 resources/install/windows/up2date.exe.manifest create mode 100644 src/native/windows/setup/Makefile create mode 100644 src/native/windows/setup/setup.c create mode 100644 src/native/windows/setup/setup.h create mode 100644 src/native/windows/setup/setup.rc diff --git a/resources/install/build.xml b/resources/install/build.xml index 0880090e5..1b767087b 100644 --- a/resources/install/build.xml +++ b/resources/install/build.xml @@ -356,15 +356,15 @@ - - + + + file="${inst.resrc}/windows/installer-windows.xml" + tofile="${windows.app.dir}/tmp/tmp-installer-windows.xml" + filtering="yes" /> + @@ -425,6 +426,7 @@ + @@ -450,22 +452,13 @@ - - - - - - - - + @@ -520,19 +513,26 @@ todir="${light.dir}" overwrite="true" /> + + + + + + + + + + + + @@ -598,7 +603,7 @@ @@ -615,7 +620,7 @@ - @@ -638,26 +643,31 @@ - + + + + - - + + - - + + - + - diff --git a/resources/install/windows/asInvoker.exe.manifest b/resources/install/windows/asInvoker.exe.manifest new file mode 100644 index 000000000..ff1b08310 --- /dev/null +++ b/resources/install/windows/asInvoker.exe.manifest @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/resources/install/windows/heat-component-defines.xsl b/resources/install/windows/heat-component-defines.xsl index 19072bd9c..22571d456 100644 --- a/resources/install/windows/heat-component-defines.xsl +++ b/resources/install/windows/heat-component-defines.xsl @@ -8,7 +8,7 @@ + select="*//*[local-name()='Directory' and @Name='light']/*" /> diff --git a/resources/install/windows/heat-component-refs.xsl b/resources/install/windows/heat-component-refs.xsl index 885fc4454..d8968a5ef 100644 --- a/resources/install/windows/heat-component-refs.xsl +++ b/resources/install/windows/heat-component-refs.xsl @@ -8,7 +8,7 @@ + select="*//*[local-name()='ComponentGroup' and @Id='ComponentGroup_HeatExe']/*" /> diff --git a/resources/install/windows/installer-windows.wxs b/resources/install/windows/installer-windows.wxs index f82d73ed1..ec21283a6 100644 --- a/resources/install/windows/installer-windows.wxs +++ b/resources/install/windows/installer-windows.wxs @@ -19,11 +19,11 @@ Comments="@PKG_COMMENTS@" Compressed="yes" Description="@PKG_DESCRIPTION@" - InstallPrivileges="elevated" - InstallScope="perMachine" InstallerVersion="200" + InstallScope="perMachine" Languages="1033" Manufacturer="@APP_NAME@" + Platform="$(var.Platform)" SummaryCodepage="1252" /> @@ -206,8 +206,12 @@ IS_AUTOUPDATE = 1 + + IS_AUTOUPDATE = 1 + + - ""]]> + ""]]> @@ -269,7 +273,7 @@ + Value="[SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR]" /> - ""]]> + ""]]> diff --git a/resources/install/windows/run.bat b/resources/install/windows/run.bat deleted file mode 100755 index 4745d951f..000000000 --- a/resources/install/windows/run.bat +++ /dev/null @@ -1,3 +0,0 @@ -mkdir "%UserProfile%/.sip-communicator/log" -set PATH=%PATH%;native -start run.exe diff --git a/resources/install/windows/run.exe.manifest b/resources/install/windows/run.exe.manifest deleted file mode 100644 index 331db8258..000000000 --- a/resources/install/windows/run.exe.manifest +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/resources/install/windows/setup.exe b/resources/install/windows/setup.exe deleted file mode 100644 index a6b29a00576f917ce087b4b6d20e6a0da377ceb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73728 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 E2S$! - - - - \ No newline at end of file diff --git a/resources/install/windows/up2date.c b/resources/install/windows/up2date.c deleted file mode 100644 index 5ae46f91a..000000000 --- a/resources/install/windows/up2date.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ -#define WIN32_LEAN_AND_MEAN -#include -#include /* ShellExecute */ -#include /* CreateToolhelp32Snapshot */ - -#include /* isspace */ -#include -#include -#include /* _istspace */ - -#ifndef ERROR_ELEVATION_REQUIRED -#define ERROR_ELEVATION_REQUIRED 740 -#endif - -#ifndef _tcsncicmp -#ifdef _UNICODE -#define _tcsncicmp _wcsnicmp -#else -#define _tcsncicmp _strnicmp -#endif -#endif - -DWORD up2date_createProcess(LPCTSTR); -DWORD up2date_displayError(DWORD error); -LPTSTR up2date_getAllowElevation(LPTSTR, BOOL *); -LPTSTR up2date_getBoolArg(LPCTSTR, LPTSTR, BOOL *); -DWORD up2date_getExePath(LPTSTR, DWORD); -DWORD up2date_getParentProcessId(DWORD *); -LPTSTR up2date_getWaitParent(LPTSTR, BOOL *); -LPTSTR up2date_skipWhitespace(LPTSTR); -LPWSTR up2date_str2wstr(LPCSTR); -DWORD up2date_waitParent(); - -int WINAPI -WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow) { - LPTSTR commandLine; - - LPWSTR wCommandLine; - LPWSTR *argv; - int argc; - LPWSTR wDir; - LPWSTR run; - -#ifdef _UNICODE - commandLine = up2date_str2wstr(cmdLine); -#else - commandLine = cmdLine; -#endif - if (commandLine) { - BOOL waitParent; - LPTSTR noWaitParentCommandLine; - BOOL allowElevation; - LPTSTR noAllowElevationCommandLine; - DWORD error; - - waitParent = FALSE; - noWaitParentCommandLine - = up2date_getWaitParent(commandLine, &waitParent); - if (waitParent) - up2date_waitParent(); - - allowElevation = FALSE; - noAllowElevationCommandLine - = up2date_getAllowElevation( - noWaitParentCommandLine, &allowElevation); - - error = up2date_createProcess(noAllowElevationCommandLine); - if ((ERROR_ELEVATION_REQUIRED == error) && allowElevation) { - TCHAR exePath[MAX_PATH + 1]; - - if (!up2date_getExePath(exePath, MAX_PATH + 1)) { - SHELLEXECUTEINFO ShExecInfo; - ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - ShExecInfo.hwnd = NULL; - ShExecInfo.lpVerb = TEXT("runas"); - ShExecInfo.lpFile = exePath; - ShExecInfo.lpParameters = noAllowElevationCommandLine; - ShExecInfo.lpDirectory = NULL; - ShExecInfo.nShow = SW_HIDE; - ShExecInfo.hInstApp = NULL; - ShellExecuteEx(&ShExecInfo); - WaitForSingleObject(ShExecInfo.hProcess,INFINITE); - -#ifdef _UNICODE - wCommandLine = commandLine; -#else - wCommandLine = up2date_str2wstr(commandLine); - if (!wCommandLine) - return ERROR_NOT_ENOUGH_MEMORY; -#endif - - argv = CommandLineToArgvW(wCommandLine, &argc); - if (argv) - { - wDir = *(argv + 3); - LPWSTR runExe = L"\\run.exe"; - int len = wcslen(wDir); - run = (wchar_t*)(malloc(sizeof(wchar_t) * (len + - wcslen(runExe) + 1))); - wcscpy(run, wDir); - wcscpy(run + len, runExe); - - ShellExecuteW(NULL, L"open", run, - NULL, NULL, SW_SHOWDEFAULT); - - error = GetLastError(); - if (error) - up2date_displayError(error); - - free(run); - } - } - } else if (error) - up2date_displayError(error); - - if (((LPVOID) commandLine) != ((LPVOID) cmdLine)) - free(commandLine); - } - - return 0; -} - -DWORD -up2date_createProcess(LPCTSTR commandLine) { - LPWSTR *argv; - LPWSTR wCommandLine; - int argc; - DWORD error; - -#ifdef _UNICODE - wCommandLine = commandLine; -#else - wCommandLine = up2date_str2wstr(commandLine); - if (!wCommandLine) - return ERROR_NOT_ENOUGH_MEMORY; -#endif - - argv = CommandLineToArgvW(wCommandLine, &argc); - if (argv) { - - switch (argc) { - case 2: { - LPWSTR environmentVariableName - = up2date_str2wstr("SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR"); - - if (!environmentVariableName) - { - error = ERROR_NOT_ENOUGH_MEMORY; - break; - } - if (!SetEnvironmentVariableW( - environmentVariableName, - *(argv + 1))) { - error = GetLastError(); - free(environmentVariableName); - break; - } - free(environmentVariableName); - } - case 1: { - STARTUPINFOW si; - PROCESS_INFORMATION pi; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - if (CreateProcessW(NULL, *argv, NULL, NULL, FALSE, 0, NULL, NULL, - &si, &pi)) - { - error = 0; - WaitForSingleObject( pi.hProcess, INFINITE ); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - else - error = GetLastError(); - } - break; - default: - error = 0; - break; - } - - LocalFree((HLOCAL) argv); - } else - error = GetLastError(); - - if (((LPVOID) wCommandLine) != ((LPVOID) commandLine)) - free(wCommandLine); - - return error; -} - -DWORD -up2date_displayError(DWORD error) { - LPTSTR message; - DWORD ret; - - if (FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - 0, - (LPTSTR) &message, - 0, - NULL)) { - TCHAR caption[MAX_PATH + 1]; - - MessageBox( - NULL, - message, - up2date_getExePath(caption, MAX_PATH + 1) ? NULL : caption, - MB_ICONERROR); - LocalFree((HLOCAL) message); - ret = 0; - } else - ret = GetLastError(); - return ret; -} - -LPTSTR -up2date_getAllowElevation(LPTSTR commandLine, BOOL *allowElevation) { - return - up2date_getBoolArg( - TEXT("--allow-elevation"), - commandLine, - allowElevation); -} - -LPTSTR -up2date_getBoolArg(LPCTSTR argName, LPTSTR commandLine, BOOL *boolValue) { - size_t argNameLength; - BOOL argValue; - - argNameLength = _tcslen(argName); - commandLine = up2date_skipWhitespace(commandLine); - if (0 == _tcsncicmp(commandLine, argName, argNameLength)) { - argValue = TRUE; - commandLine - = up2date_skipWhitespace(commandLine + argNameLength); - } else - argValue = FALSE; - if (boolValue) - *boolValue = argValue; - return commandLine; -} - -DWORD -up2date_getExePath(LPTSTR exePath, DWORD exePathSize) { - return - GetModuleFileName(NULL, (LPTSTR) exePath, exePathSize) - ? 0 - : GetLastError(); -} - -DWORD -up2date_getParentProcessId(DWORD *ppid) { - HANDLE snapshot; - DWORD error; - - snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) - error = GetLastError(); - else { - PROCESSENTRY32 entry; - - entry.dwSize = sizeof(PROCESSENTRY32); - if (Process32First(snapshot, &entry)) { - DWORD pid; - - error = 0; - pid = GetCurrentProcessId(); - if (ppid) - *ppid = 0; - - do { - if (entry.th32ProcessID == pid) { - if (ppid) - *ppid = entry.th32ParentProcessID; - break; - } - if (!Process32Next(snapshot, &entry)) { - error = GetLastError(); - break; - } - } while (1); - } else - error = GetLastError(); - CloseHandle(snapshot); - } - return error; -} - -LPTSTR -up2date_getWaitParent(LPTSTR commandLine, BOOL *waitParent) { - return - up2date_getBoolArg( - TEXT("--wait-parent"), - commandLine, - waitParent); -} - -LPTSTR -up2date_skipWhitespace(LPTSTR str) { - TCHAR ch; - - while ((ch = *str) && _istspace(ch)) - ++str; - return str; -} - -LPWSTR -up2date_str2wstr(LPCSTR str) { - int tstrSize; - LPWSTR tstr; - - tstrSize = - MultiByteToWideChar(CP_THREAD_ACP, 0, str, -1, NULL, 0); - if (tstrSize) { - tstr = (LPWSTR) malloc(tstrSize * sizeof(WCHAR)); - if (tstr) { - tstrSize - = MultiByteToWideChar(CP_THREAD_ACP, 0, str, -1, tstr, tstrSize); - if (!tstrSize) { - free(tstr); - tstr = NULL; - } - } else - tstr = NULL; - } else - tstr = NULL; - return tstr; -} - -DWORD -up2date_waitParent() { - DWORD error; - DWORD ppid; - - error = up2date_getParentProcessId(&ppid); - if (!error) { - HANDLE parentProcess; - - parentProcess = OpenProcess(SYNCHRONIZE, FALSE, ppid); - if (parentProcess) { - DWORD event; - - error = 0; - - do { - event = WaitForSingleObject(parentProcess, INFINITE); - if (WAIT_FAILED == event) { - error = GetLastError(); - break; - } - } while (WAIT_TIMEOUT == event); - CloseHandle(parentProcess); - } else - error = GetLastError(); - } - return error; -} diff --git a/resources/install/windows/up2date.exe b/resources/install/windows/up2date.exe deleted file mode 100644 index 584ea4ceacf9f4ca4ef4ff427804ada01d6ac0fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32870 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 diff --git a/resources/install/windows/up2date.exe.manifest b/resources/install/windows/up2date.exe.manifest deleted file mode 100644 index 331db8258..000000000 --- a/resources/install/windows/up2date.exe.manifest +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/native/windows/setup/Makefile b/src/native/windows/setup/Makefile new file mode 100644 index 000000000..048172362 --- /dev/null +++ b/src/native/windows/setup/Makefile @@ -0,0 +1,27 @@ +MINGW_HOME ?= C:/mingw +TARGET_BASENAME ?= setup +TARGET_DIR ?= ../../../../release/windows/tmp + +CC = $(MINGW_HOME)/bin/gcc +CPPFLAGS = \ + -O2 \ + -Wall -Wreturn-type \ + -DWINVER=0x0502 -D_WIN32_WINNT=0x0502 +LDFLAGS = -mwindows +LIBS = +TARGET ?= $(TARGET_DIR)/$(TARGET_BASENAME).exe + +MACHINE = $(shell $(CC) -dumpmachine) +WINDRES = $(MINGW_HOME)/bin/windres +ifneq ("x$(MACHINE)","x") +ifeq ($(wildcard $(MINGW_HOME)/bin/$(MACHINE)-windres.*),$(MINGW_HOME)/bin/$(MACHINE)-windres.exe) + WINDRES = $(MINGW_HOME)/bin/$(MACHINE)-windres +endif +endif + +$(TARGET): setup.c $(TARGET_DIR)/setup.res + $(CC) $(CPPFLAGS) $^ $(LDFLAGS) -o $@ $(LIBS) + -$(MINGW_HOME)/$(MACHINE)/bin/strip $(TARGET) + +$(TARGET_DIR)/setup.res: setup.rc + $(WINDRES) -I../../../../resources/install/windows $^ -O coff -o $@ diff --git a/src/native/windows/setup/setup.c b/src/native/windows/setup/setup.c new file mode 100644 index 000000000..e68d28459 --- /dev/null +++ b/src/native/windows/setup/setup.c @@ -0,0 +1,941 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +#include "setup.h" + +#include /* isspace */ +#include +#include + +#ifndef ERROR_RESOURCE_ENUM_USER_STOP +#define ERROR_RESOURCE_ENUM_USER_STOP 0x3B02 +#endif /* #ifndef ERROR_RESOURCE_ENUM_USER_STOP */ +#include +#ifndef SEE_MASK_NOASYNC +#define SEE_MASK_NOASYNC 0x00000100 +#endif /* #ifndef SEE_MASK_NOASYNC */ +#include /* CreateToolhelp32Snapshot */ + +static LPWSTR Setup_commandLine = NULL; +static LPTSTR Setup_fileName = NULL; +static LPTSTR Setup_productName = NULL; +static BOOL Setup_waitForParentProcess_ = FALSE; + +BOOL CALLBACK Setup_enumResNameProc(HMODULE module, LPCTSTR type, LPTSTR name, LONG_PTR param); +static DWORD Setup_executeMsi(LPCTSTR path); +static DWORD Setup_extractAndExecuteMsi(LPVOID ptr, DWORD size); +static LPTSTR Setup_getBoolArg(LPCTSTR argName, LPTSTR commandLine, BOOL *boolValue); +static LPCTSTR Setup_getFileName(); +static DWORD Setup_getParentProcess(DWORD *ppid, LPTSTR *fileName); +static LPCTSTR Setup_getProductName(); +static int Setup_isWow64Acceptable(); +LRESULT CALLBACK Setup_isWow64AcceptableMessageBoxCallWndRetProc(int code, WPARAM wParam, LPARAM lParam); +static DWORD Setup_parseCommandLine(LPSTR cmdLine); +static LPTSTR Setup_skipWhitespace(LPTSTR str); +static LPWSTR Setup_str2wstr(LPCSTR str); +static DWORD Setup_terminateUp2DateExe(); +static DWORD Setup_waitForParentProcess(); + +BOOL CALLBACK +Setup_enumResNameProc( + HMODULE module, + LPCTSTR type, LPTSTR name, + LONG_PTR param) +{ + BOOL proceed = TRUE; + DWORD error = ERROR_SUCCESS; + + if (!IS_INTRESOURCE(name) + && (_tcslen(name) > 3) + && (_tcsnicmp(name, _T("MSI"), 3) == 0)) + { + HRSRC rsrc = FindResource(module, name, type); + + if (rsrc) + { + DWORD size = SizeofResource(module, rsrc); + + if (size) + { + HGLOBAL global = LoadResource(module, rsrc); + + if (global) + { + LPVOID ptr = LockResource(global); + + if (ptr) + { + proceed = FALSE; + error = Setup_extractAndExecuteMsi(ptr, size); + } + else + error = GetLastError(); + } + else + error = GetLastError(); + } + else + error = GetLastError(); + } + else + error = GetLastError(); + } + if (param) + *((DWORD *) param) = error; + return proceed; +} + +static DWORD +Setup_executeMsi(LPCTSTR path) +{ + DWORD error = ERROR_SUCCESS; + LPWSTR p0, p1, p2, p3; + size_t p0Length, p1Length, p2Length, p3Length; + LPWSTR parameters; + + p0 = L"/i \""; + p0Length = wcslen(p0); +#ifdef _UNICODE + p1 = path; +#else + p1 = Setup_str2wstr(path); +#endif /* #ifdef _UNICODE */ + if (p1) + p1Length = wcslen(p1); + else + { + error = ERROR_OUTOFMEMORY; + return error; + } + p2 = L"\" "; + p2Length = wcslen(p2); + p3 = Setup_commandLine; + p3Length = p3 ? wcslen(p3) : 0; + + parameters + = (LPWSTR) + malloc( + sizeof(wchar_t) + * (p0Length + p1Length + p2Length + p3Length + 1)); + if (parameters) + { + LPWSTR str = parameters; + SHELLEXECUTEINFOW sei; + + wcsncpy(str, p0, p0Length); + str += p0Length; + wcsncpy(str, p1, p1Length); + str += p1Length; + wcsncpy(str, p2, p2Length); + str += p2Length; + if (p3Length) + { + wcsncpy(str, p3, p3Length); + str += p3Length; + } + *str = 0; + + ZeroMemory(&sei, sizeof(sei)); + sei.cbSize = sizeof(sei); + sei.fMask + = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = L"open"; + sei.lpFile = L"msiexec.exe"; + sei.lpParameters = parameters; + sei.nShow = SW_SHOWNORMAL; + if (ShellExecuteExW(&sei) && (((int) (sei.hInstApp)) > 32)) + { + if (sei.hProcess) + { + DWORD event; + + do + { + event = WaitForSingleObject(sei.hProcess, INFINITE); + if (WAIT_FAILED == event) + { + error = GetLastError(); + break; + } + } + while (WAIT_TIMEOUT == event); + CloseHandle(sei.hProcess); + } + } + else + error = GetLastError(); + + free(parameters); + } + else + error = ERROR_OUTOFMEMORY; + + if (((LPVOID) p1) != ((LPVOID) path)) + free(p1); + + return error; +} + +static DWORD +Setup_extractAndExecuteMsi(LPVOID ptr, DWORD size) +{ + TCHAR path[MAX_PATH + 1]; + DWORD pathSize = sizeof(path) / sizeof(TCHAR); + DWORD tempPathLength = GetTempPath(pathSize, path); + DWORD error = ERROR_SUCCESS; + + if (tempPathLength) + { + if (tempPathLength > pathSize) + error = ERROR_NOT_ENOUGH_MEMORY; + else + { + LPCTSTR fileName = Setup_getFileName(); + HANDLE file = INVALID_HANDLE_VALUE; + + if (fileName) + { + size_t fileNameLength = _tcslen(fileName); + + if ((fileNameLength > 4 /* .exe */) + && (tempPathLength + fileNameLength + 1 <= pathSize)) + { + LPTSTR str = path + tempPathLength; + + _tcsncpy(str, fileName, fileNameLength - 4); + str += (fileNameLength - 4); + _tcsncpy(str, _T(".msi"), 4); + *(str + 4) = 0; + + file + = CreateFile( + path, + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_TEMPORARY, + NULL); + } + + if (INVALID_HANDLE_VALUE == file) + { + LPTSTR tempPath; + + path[tempPathLength] = 0; + tempPath = _tcsdup(path); + + if (tempPath) + { + if (0 + == GetTempFileName( + tempPath, + _T("MSI"), + 0, + path)) + { + error = GetLastError(); + } + else + { + file + = CreateFile( + path, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY, + NULL); + } + + free(tempPath); + } + else + error = ERROR_OUTOFMEMORY; + } + + if (INVALID_HANDLE_VALUE != file) + { + DWORD written; + + if ((FALSE == WriteFile(file, ptr, size, &written, NULL)) + || (written != size)) + { + error = GetLastError(); + CloseHandle(file); + } + else + { + if (Setup_waitForParentProcess_) + Setup_waitForParentProcess(); + + CloseHandle(file); + error = Setup_executeMsi(path); + } + DeleteFile(path); + } + } + } + } + else + error = GetLastError(); + return error; +} + +static LPTSTR +Setup_getBoolArg(LPCTSTR argName, LPTSTR commandLine, BOOL *boolValue) +{ + size_t argNameLength; + BOOL argValue; + + argNameLength = _tcslen(argName); + commandLine = Setup_skipWhitespace(commandLine); + if (0 == _tcsnicmp(commandLine, argName, argNameLength)) + { + argValue = TRUE; + commandLine = Setup_skipWhitespace(commandLine + argNameLength); + } + else + argValue = FALSE; + if (boolValue) + *boolValue = argValue; + return commandLine; +} + +static LPCTSTR +Setup_getFileName() +{ + if (!Setup_fileName) + { + TCHAR moduleFileName[MAX_PATH + 1]; + DWORD moduleFileNameSize = sizeof(moduleFileName) / sizeof(TCHAR); + DWORD moduleFileNameLength + = GetModuleFileName(NULL, moduleFileName, moduleFileNameSize); + + if (moduleFileNameLength) + { + TCHAR *fileNameEnd = moduleFileName + moduleFileNameLength - 1; + TCHAR *fileNameBegin = fileNameEnd; + size_t fileNameLength; + LPTSTR fileName; + + for (; fileNameBegin >= moduleFileName; fileNameBegin--) + { + TCHAR c = *fileNameBegin; + + if (('\\' == c) || ('/' == c)) + break; + } + fileNameBegin + = (fileNameBegin == fileNameEnd) + ? moduleFileName + : (fileNameBegin + 1); + + fileNameLength = (fileNameEnd - fileNameBegin) + 1; + fileName = (LPTSTR) malloc((fileNameLength + 1) * sizeof(TCHAR)); + if (fileName) + { + _tcsncpy(fileName, fileNameBegin, fileNameLength); + *(fileName + fileNameLength) = 0; + Setup_fileName = fileName; + } + } + } + return Setup_fileName; +} + +static DWORD +Setup_getParentProcess(DWORD *ppid, LPTSTR *fileName) +{ + HANDLE snapshot; + DWORD error; + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) + error = GetLastError(); + else + { + PROCESSENTRY32 entry; + + entry.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(snapshot, &entry)) + { + DWORD pid; + + error = ERROR_SUCCESS; + pid = GetCurrentProcessId(); + if (ppid) + *ppid = 0; + + do + { + if (entry.th32ProcessID == pid) + { + if (ppid) + *ppid = entry.th32ParentProcessID; + break; + } + if (!Process32Next(snapshot, &entry)) + { + error = GetLastError(); + break; + } + } + while (1); + } else + error = GetLastError(); + if ((ERROR_SUCCESS == error) && fileName && ppid && *ppid) + { + if (Process32First(snapshot, &entry)) + { + do + { + if (entry.th32ProcessID == *ppid) + { + *fileName = _tcsdup(entry.szExeFile); + if (NULL == *fileName) + error = ERROR_OUTOFMEMORY; + break; + } + if (!Process32Next(snapshot, &entry)) + { + error = GetLastError(); + break; + } + } + while (1); + } else + error = GetLastError(); + } + CloseHandle(snapshot); + } + return error; +} + +static LPCTSTR +Setup_getProductName() +{ + if (!Setup_productName) + { + /* TODO Auto-generated method stub */ + LPCTSTR fileName = Setup_getFileName(); + + if (fileName) + { + int fileNameLength = _tcslen(fileName); + + if ((fileNameLength > 4) + && (_tcsnicmp(fileName + fileNameLength - 4, _T(".exe"), 4) + == 0)) + { + LPTSTR productName; + + fileNameLength -= 4; + productName + = (LPTSTR) malloc((fileNameLength + 1) * sizeof(TCHAR)); + if (productName) + { + _tcsncpy(productName, fileName, fileNameLength); + *(productName + fileNameLength) = 0; + Setup_productName = productName; + } + } + if (!Setup_productName) + Setup_productName = (LPTSTR) fileName; + } + } + return Setup_productName; +} + +static int +Setup_isWow64Acceptable() +{ + HMODULE kernel32 = GetModuleHandle(_T("kernel32")); + int answer = IDYES; + + if (kernel32) + { + typedef BOOL (WINAPI *LPISWOW64PROCESS)(HANDLE, PBOOL); + + LPISWOW64PROCESS isWow64Process + = (LPISWOW64PROCESS) GetProcAddress(kernel32, _T("IsWow64Process")); + BOOL wow64Process = FALSE; + + if (isWow64Process + && isWow64Process(GetCurrentProcess(), &wow64Process) + && wow64Process) + { + TCHAR fileName[MAX_PATH + 1]; + + if (GetModuleFileName(NULL, fileName, sizeof(fileName) / sizeof(TCHAR))) + { + UINT questionId; + UINT buttonType; + DWORD questionLength; + TCHAR question[1024]; + +#ifdef X64_SETUP_URL + HHOOK hook + = SetWindowsHookEx( + WH_CALLWNDPROCRET, + (HOOKPROC) Setup_isWow64AcceptableMessageBoxCallWndRetProc, + NULL, + GetCurrentThreadId()); + + if (hook) + { + questionId = IDS_ISWOW64ACCEPTABLE3; + buttonType = MB_YESNOCANCEL | MB_DEFBUTTON3; + } + else +#endif /* #ifdef X64_SETUP_URL */ + { + questionId = IDS_ISWOW64ACCEPTABLE2; + buttonType = MB_YESNO; + } + + questionLength + = LoadString( + GetModuleHandle(NULL), + questionId, + question, + sizeof(question) / sizeof(TCHAR)); + if (questionLength) + { + answer + = MessageBox( + NULL, + question, + fileName, + MB_ICONQUESTION | buttonType); + LocalFree(question); + } + +#ifdef X64_SETUP_URL + if (hook) + { + UnhookWindowsHookEx(hook); + + switch (answer) + { + case IDNO: // Continue + answer = IDYES; + break; + case IDYES: // Download + answer = IDNO; + ShellExecute( + NULL, + _T("open"), + _T(X64_SETUP_URL), + NULL, + NULL, + SW_SHOWNORMAL); + break; + } + } +#endif /* #ifdef X64_SETUP_URL */ + } + } + } + return answer; +} + +LRESULT CALLBACK +Setup_isWow64AcceptableMessageBoxCallWndRetProc( + int code, + WPARAM wParam, + LPARAM lParam) +{ + CWPRETSTRUCT *cwprs = (CWPRETSTRUCT *) lParam; + + if (cwprs && (WM_INITDIALOG == cwprs->message)) + { + HWND yes, no; + + yes = GetDlgItem(cwprs->hwnd, IDYES); + if (yes) + SendMessage(yes, WM_SETTEXT, 0, (LPARAM) _T("&Download")); + + no = GetDlgItem(cwprs->hwnd, IDNO); + if (no) + SendMessage(no, WM_SETTEXT, 0, (LPARAM) _T("&Continue")); + } + return CallNextHookEx(NULL, code, wParam, lParam); +} + +static DWORD +Setup_parseCommandLine(LPSTR cmdLine) +{ + LPTSTR commandLine; + DWORD error = ERROR_SUCCESS; + +#ifdef _UNICODE + if (cmdLine) + { + commandLine = Setup_str2wstr(cmdLine); + if (!commandLine) + error = ERROR_OUTOFMEMORY; + } + else + commandLine = NULL; +#else + commandLine = cmdLine; +#endif /* #ifdef _UNICODE */ + + if (commandLine) + { + LPTSTR noWaitParentCommandLine + = Setup_getBoolArg( + _T("--wait-parent"), + commandLine, + &Setup_waitForParentProcess_); + /* + * The command line argument --allow-elevation is up2date legacy which + * has to be taken into account by removing it in order to prevent it + * from breaking msiexec. + */ + BOOL up2date; + LPTSTR noAllowElevationCommandLine + = Setup_getBoolArg( + _T("--allow-elevation"), + noWaitParentCommandLine, + &up2date); + size_t noAllowElevationCommandLineLength + = _tcslen(noAllowElevationCommandLine); + TCHAR envVarValue[1 /* " */ + MAX_PATH + 1 /* " */ + 1]; + + if (!up2date && !noAllowElevationCommandLineLength) + { + DWORD envVarValueSize = (sizeof(envVarValue) / sizeof(TCHAR)) - 2 /* "" */; + DWORD envVarValueLength + = GetEnvironmentVariable( + _T("SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR"), + &(envVarValue[1]), + envVarValueSize); + + if (envVarValueLength) + { + if (envVarValueLength > envVarValueSize) + error = ERROR_NOT_ENOUGH_MEMORY; + else + { + if ((envVarValueLength >= 2) + && ('\"' == envVarValue[1]) + && ('\"' == envVarValue[1 + envVarValueLength - 1])) + { + noAllowElevationCommandLine = &(envVarValue[1]); + } + else + { + envVarValue[0] = '\"'; + envVarValue[1 + envVarValueLength] = '\"'; + envVarValue[1 + envVarValueLength + 1] = 0; + envVarValueLength += 2; + noAllowElevationCommandLine = envVarValue; + } + noAllowElevationCommandLineLength = envVarValueLength; + up2date = TRUE; + } + } + else + { + DWORD envVarError = GetLastError(); + + if (ERROR_ENVVAR_NOT_FOUND != envVarError) + error = envVarError; + } + } + + if (up2date && noAllowElevationCommandLineLength) + { + LPWSTR commandLineW; + +#ifdef _UNICODE + commandLineW = noAllowElevationCommandLine; +#else + commandLineW = Setup_str2wstr(noAllowElevationCommandLine); + if (!commandLineW) + error = ERROR_OUTOFMEMORY; +#endif /* #ifdef _UNICODE */ + + if (commandLineW) + { + int argc; + LPWSTR *argv = CommandLineToArgvW(commandLineW, &argc); + + if (argv) + { + if ((1 == argc) || (2 == argc)) + { + LPWSTR argv1 = *(argv + (argc - 1)); + size_t argv1Length = wcslen(argv1); + + if ((argv1Length >= 2) + && (L'\"' == argv1[0]) + && (L'\"' == argv1[argv1Length - 1])) + { + argv1++; + argv1Length -= 2; + } + if (argv1Length) + { + LPCWSTR propertyBegin + = L"SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR=\""; + size_t propertyBeginLength = wcslen(propertyBegin); + LPCWSTR propertyEnd = L"\""; + size_t propertyEndLength = wcslen(propertyEnd); + + Setup_commandLine + = (LPWSTR) + malloc( + sizeof(wchar_t) + * (propertyBeginLength + + argv1Length + + propertyEndLength + + 1)); + if (Setup_commandLine) + { + LPWSTR str = Setup_commandLine; + + wcsncpy( + str, + propertyBegin, + propertyBeginLength); + str += propertyBeginLength; + wcsncpy(str, argv1, argv1Length); + str += argv1Length; + wcsncpy(str, propertyEnd, propertyEndLength); + *(str + propertyEndLength) = 0; + } + else + error = ERROR_OUTOFMEMORY; + } + } + LocalFree(argv); + } + else + error = GetLastError(); + + if (((LPVOID) commandLineW) + != ((LPVOID) noAllowElevationCommandLine)) + free(commandLineW); + } + } + /* + * If up2date.exe is running while the MSI is being installed, the MSI + * will display a dialog notifying of the fact and asking the user to + * either let it close the application in question (and it will not be + * able to if the user actually chooses the option) or reboot. + */ + if (up2date) + Setup_terminateUp2DateExe(); + + if (!up2date && noAllowElevationCommandLineLength) + { +#ifdef _UNICODE + Setup_commandLine = _wcsdup(noAllowElevationCommandLine); +#else + Setup_commandLine = Setup_str2wstr(noAllowElevationCommandLine); +#endif /* #ifdef _UNICODE */ + if (Setup_commandLine) + { + /* + * At the time of this writing, we expect a single property to + * pass on to msiexec which wants it in the format + * PROPERTY="VALUE" if value contains spaces. Unfortunately, + * ProcessBuilder on the Java side will break it by quoting the + * whole command line argument. + */ + size_t commandLineLength = wcslen(Setup_commandLine); + + if ((commandLineLength > 3) + && (L'"' == *Setup_commandLine) + && (L'"' == *(Setup_commandLine + (commandLineLength - 2))) + && (L'"' == *(Setup_commandLine + (commandLineLength - 1)))) + { + *(Setup_commandLine + (commandLineLength - 1)) = 0; + Setup_commandLine++; + } + } + else + error = ERROR_OUTOFMEMORY; + } + + if (commandLine != cmdLine) + free(commandLine); + } + return error; +} + +static LPTSTR +Setup_skipWhitespace(LPTSTR str) +{ + TCHAR c; + + while ((c = *str) && _istspace(c)) + ++str; + return str; +} + +static LPWSTR +Setup_str2wstr(LPCSTR str) +{ + int tstrSize; + LPWSTR tstr; + + tstrSize = MultiByteToWideChar(CP_THREAD_ACP, 0, str, -1, NULL, 0); + if (tstrSize) + { + tstr = (LPWSTR) malloc(tstrSize * sizeof(WCHAR)); + if (tstr) + { + tstrSize + = MultiByteToWideChar(CP_THREAD_ACP, 0, str, -1, tstr, tstrSize); + if (!tstrSize) + { + free(tstr); + tstr = NULL; + } + } + else + tstr = NULL; + } + else + tstr = NULL; + return tstr; +} + +static DWORD +Setup_terminateUp2DateExe() +{ + DWORD error; + DWORD ppid = 0; + LPTSTR ppFileName = NULL; + + error = Setup_getParentProcess(&ppid, &ppFileName); + if ((ERROR_SUCCESS == error) && ppFileName) + { + size_t ppFileNameLength = _tcslen(ppFileName); + LPCTSTR up2DateExe = _T("up2date.exe"); + size_t up2DateExeLength = _tcslen(up2DateExe); + + if ((ppFileNameLength >= up2DateExeLength) + && (_tcsncmp( + ppFileName + (ppFileNameLength - up2DateExeLength), + up2DateExe, + up2DateExeLength) + == 0)) + { + HANDLE parentProcess + = OpenProcess(PROCESS_TERMINATE, FALSE, ppid); + + if (parentProcess) + { + if (!TerminateProcess(parentProcess, 0)) + error = GetLastError(); + CloseHandle(parentProcess); + } + else + error = GetLastError(); + } + } + return error; +} + +static DWORD +Setup_waitForParentProcess() +{ + DWORD error; + DWORD ppid = 0; + + error = Setup_getParentProcess(&ppid, NULL); + if (ERROR_SUCCESS == error) + { + HANDLE parentProcess = OpenProcess(SYNCHRONIZE, FALSE, ppid); + + if (parentProcess) + { + DWORD event; + + error = ERROR_SUCCESS; + do + { + event = WaitForSingleObject(parentProcess, INFINITE); + if (WAIT_FAILED == event) + { + error = GetLastError(); + break; + } + } + while (WAIT_TIMEOUT == event); + CloseHandle(parentProcess); + } + else + error = GetLastError(); + } + return error; +} + +int CALLBACK +WinMain( + HINSTANCE instance, HINSTANCE prevInstance, + LPSTR cmdLine, + int cmdShow) +{ + DWORD error = ERROR_SUCCESS; + + Setup_parseCommandLine(cmdLine); + + if ((ERROR_SUCCESS == error) + && (IDYES == Setup_isWow64Acceptable()) + && (FALSE + == EnumResourceNames( + NULL, + RT_RCDATA, + Setup_enumResNameProc, + (LONG_PTR) &error)) + && (ERROR_SUCCESS == error)) + { + DWORD enumResourceNamesError = GetLastError(); + + if ((ERROR_SUCCESS != enumResourceNamesError) + && (ERROR_RESOURCE_ENUM_USER_STOP != enumResourceNamesError)) + error = enumResourceNamesError; + } + + if (ERROR_SUCCESS != error) + { + LPTSTR message; + DWORD messageLength + = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + LANG_USER_DEFAULT, + (LPTSTR) &message, + 0, + NULL); + + if (messageLength) + { + MessageBox( + NULL, + message, + Setup_getProductName(), + MB_ICONERROR | MB_OK); + LocalFree(message); + } + } + + if (Setup_productName && (Setup_productName != Setup_fileName)) + free(Setup_productName); + if (Setup_fileName) + free(Setup_fileName); + + return 0; +} diff --git a/src/native/windows/setup/setup.h b/src/native/windows/setup/setup.h new file mode 100644 index 000000000..2350578e4 --- /dev/null +++ b/src/native/windows/setup/setup.h @@ -0,0 +1,9 @@ +#ifndef _NET_JAVA_SIP_COMMUNICATOR_WINDOWS_SETUP_H_ +#define _NET_JAVA_SIP_COMMUNICATOR_WINDOWS_SETUP_H_ + +#include + +#define IDS_ISWOW64ACCEPTABLE2 2 +#define IDS_ISWOW64ACCEPTABLE3 3 + +#endif /* #ifndef _NET_JAVA_SIP_COMMUNICATOR_WINDOWS_SETUP_H_ */ diff --git a/src/native/windows/setup/setup.rc b/src/native/windows/setup/setup.rc new file mode 100644 index 000000000..395de9684 --- /dev/null +++ b/src/native/windows/setup/setup.rc @@ -0,0 +1,34 @@ +#include "setup.h" + +SCLogoIcon ICON sc-logo.ico + +STRINGTABLE +BEGIN + IDS_ISWOW64ACCEPTABLE2 "The version of this file is x86 (32-bit) and the version of Windows you're running is x64 (64-bit). It is recommended to install the x64 (64-bit) version of the program.\012\012Continue anyway?" + IDS_ISWOW64ACCEPTABLE3 "The version of this file is x86 (32-bit) and the version of Windows you're running is x64 (64-bit). It is recommended to install the x64 (64-bit) version of the program." +END + +1 VERSIONINFO + FILEVERSION 0,0,0,0 + FILETYPE VFT_APP + PRODUCTVERSION 0,0,0,0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "Comments", "" + VALUE "CompanyName", "SIP Communicator" + VALUE "FileDescription", "SIP Communicator Setup" + VALUE "FileVersion", "0.0.0.0" + VALUE "InternalName", "setup" + VALUE "OriginalFilename", "setup.exe" + VALUE "ProductName", "SIP Communicator" + VALUE "ProductVersion", "0.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java index 11bd0efcb..c4af174b3 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java @@ -740,11 +740,14 @@ public Component createStartupConfigPanel() + ":"), BorderLayout.WEST); - Component updateCheckBox = createUpdateCheckBox(); + Component updateCheckBox = null; Component autoStartCheckBox = null; if (OSUtils.IS_WINDOWS) + { autoStartCheckBox = createAutoStartCheckBox(); + updateCheckBox = createUpdateCheckBox(); + } if (updateCheckBox != null && autoStartCheckBox != null) { diff --git a/src/net/java/sip/communicator/plugin/updatechecker/UpdateCheckActivator.java b/src/net/java/sip/communicator/plugin/updatechecker/UpdateCheckActivator.java index 08cd66880..4a7accc26 100644 --- a/src/net/java/sip/communicator/plugin/updatechecker/UpdateCheckActivator.java +++ b/src/net/java/sip/communicator/plugin/updatechecker/UpdateCheckActivator.java @@ -1,1017 +1,1305 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ -package net.java.sip.communicator.plugin.updatechecker; - -import java.awt.*; -import java.awt.event.*; -import java.io.*; -import java.net.*; -import java.util.*; - -import javax.net.ssl.*; -import javax.swing.*; - -import net.java.sip.communicator.service.browserlauncher.*; -import net.java.sip.communicator.service.configuration.*; -import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.service.gui.Container; // disambiguation -import net.java.sip.communicator.service.certificate.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.resources.*; -import net.java.sip.communicator.service.shutdown.*; -import net.java.sip.communicator.service.version.*; -import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; - -import org.osgi.framework.*; - -/** - * Activates the UpdateCheck plugin - * - * @author Damian Minkov - * @author Lubomir Marinov - */ -public class UpdateCheckActivator - implements BundleActivator -{ - /** - * The Logger used by the UpdateCheckActivator class and - * its instances for logging output. - */ - private static final Logger logger - = Logger.getLogger(UpdateCheckActivator.class); - - /** - * The bundle context. - */ - private static BundleContext bundleContext = null; - - /** - * Reference to the BrowserLauncherService. - */ - private static BrowserLauncherService browserLauncherService; - - /** - * Reference to the ResourceManagementService. - */ - private static ResourceManagementService resourcesService; - - /** - * Reference to the ConfigurationService. - */ - private static ConfigurationService configService; - - /** - * Reference to the UIService. - */ - private static UIService uiService = null; - - /** - * Reference to the CertificateVerificationService. - */ - private static CertificateVerificationService certificateService = null; - - /** - * The download link of the update. - */ - private String downloadLink = null; - - /** - * The last version of the software. - */ - private String lastVersion = null; - - /** - * The ChangeLog link. - */ - private String changesLink = null; - - /** - * The user credentials. - */ - private static UserCredentials userCredentials = null; - - /** - * The error message is any. - */ - private static String errorMessage = null; - - /** - * The host we are querying for updates. - */ - private static String host = null; - - /** - * Whether user has canceled authentication process. - */ - private static boolean isAuthenticationCanceled = false; - - /** - * Property name of the username used if HTTP authentication is required. - */ - private static final String UPDATE_USERNAME_CONFIG = - "net.java.sip.communicator.plugin.updatechecker.UPDATE_SITE_USERNAME"; - - /** - * Property name of the password used if HTTP authentication is required. - */ - private static final String UPDATE_PASSWORD_CONFIG = - "net.java.sip.communicator.plugin.updatechecker.UPDATE_SITE_PASSWORD"; - - /** - * Property indicating whether update check is enabled. - */ - private static final String UPDATECHECKER_ENABLED = - "net.java.sip.communicator.plugin.updatechecker.ENABLED"; - - /** - * Property name for the update link in the configuration file. - */ - private static final String PROP_UPDATE_LINK = - "net.java.sip.communicator.UPDATE_LINK"; - - static - { - removeDownloadRestrictions(); - } - - /** - * Starts this bundle - * - * @param bundleContext BundleContext provided by OSGi framework - * @throws Exception if something goes wrong during start - */ - public void start(BundleContext bundleContext) throws Exception - { - if (logger.isDebugEnabled()) - logger.debug("Update checker [STARTED]"); - - try - { - logger.logEntry(); - UpdateCheckActivator.bundleContext = bundleContext; - } - finally - { - logger.logExit(); - } - - Thread updateThread = new Thread(new UpdateCheckThread()); - updateThread.setDaemon(true); - updateThread.start(); - - if (logger.isDebugEnabled()) - logger.debug("Update checker [REGISTERED]"); - } - - /** - * Stop the bundle. Nothing to stop for now. - * @param bundleContext BundleContext provided by OSGi framework - * @throws Exception if something goes wrong during stop - */ - public void stop(BundleContext bundleContext) - throws Exception - { - if (logger.isDebugEnabled()) - logger.debug("Update checker [STOPPED]"); - } - - /** - * Returns the BrowserLauncherService obtained from the bundle - * context. - * @return the BrowserLauncherService obtained from the bundle - * context - */ - private static BrowserLauncherService getBrowserLauncher() - { - if (browserLauncherService == null) - { - ServiceReference serviceReference = bundleContext - .getServiceReference(BrowserLauncherService.class.getName()); - - browserLauncherService = (BrowserLauncherService) bundleContext - .getService(serviceReference); - } - - return browserLauncherService; - } - - /** - * Returns the ConfigurationService obtained from the bundle - * context. - * - * @return the ConfigurationService obtained from the bundle - * context - */ - private static ConfigurationService getConfigurationService() - { - if (configService == null) - { - ServiceReference configReference = - bundleContext.getServiceReference(ConfigurationService.class - .getName()); - - configService = - (ConfigurationService) bundleContext - .getService(configReference); - } - - return configService; - } - - /** - * Gets a reference to a ShutdownService implementation - * currently registered in the bundle context of the active - * UpdateCheckActivator instance. - *

- * The returned reference to ShutdownService is not being - * cached. - *

- * - * @return reference to a ShutdownService implementation - * currently registered in the bundle context of the active - * UpdateCheckActivator instance - */ - private static ShutdownService getShutdownService() - { - return - (ShutdownService) - bundleContext.getService( - bundleContext.getServiceReference( - ShutdownService.class.getName())); - } - - /** - * Returns a reference to the UIService implementation currently registered - * in the bundle context or null if no such implementation was found. - * - * @return a reference to a UIService implementation currently registered - * in the bundle context or null if no such implementation was found. - */ - private static UIService getUIService() - { - if(uiService == null) - { - ServiceReference uiServiceReference - = bundleContext.getServiceReference( - UIService.class.getName()); - uiService = (UIService)bundleContext - .getService(uiServiceReference); - } - return uiService; - } - - /** - * Returns resource service. - * @return the resource service. - */ - private static ResourceManagementService getResources() - { - if (resourcesService == null) - { - ServiceReference serviceReference = bundleContext - .getServiceReference(ResourceManagementService.class.getName()); - - if(serviceReference == null) - return null; - - resourcesService = (ResourceManagementService) bundleContext - .getService(serviceReference); - } - - return resourcesService; - } - - /** - * Return the certificate verification service impl. - * @return the CertificateVerification service. - */ - private static CertificateVerificationService - getCertificateVerificationService() - { - if(certificateService == null) - { - ServiceReference certVerifyReference - = bundleContext.getServiceReference( - CertificateVerificationService.class.getName()); - if(certVerifyReference != null) - certificateService - = (CertificateVerificationService)bundleContext.getService( - certVerifyReference); - } - - return certificateService; - } - - /** - * Check the first link as files on the web are sorted by date - * @return whether we are using the latest version or not. - */ - private boolean isNewestVersion() - { - try - { - ServiceReference serviceReference = bundleContext - .getServiceReference( net.java.sip.communicator.service.version. - VersionService.class.getName()); - - VersionService verService = (VersionService) bundleContext - .getService(serviceReference); - - net.java.sip.communicator.service.version.Version - ver = verService.getCurrentVersion(); - - String configString = null; - - configString = getConfigurationService().getString( - PROP_UPDATE_LINK); - - if(configString == null) - { - configString = Resources.getConfigString("update_link"); - } - - if(configString == null) - { - if (logger.isDebugEnabled()) - logger.debug( - "Updates are disabled. Faking latest version."); - return true; - } - - URL url = new URL(configString); - URLConnection conn = url.openConnection(); - - if (conn instanceof HttpURLConnection) - { - while(((HttpURLConnection)conn).getResponseCode() == - HttpURLConnection.HTTP_UNAUTHORIZED - && !isAuthenticationCanceled) - { - if(userCredentials.getUserName() != null) - { - errorMessage = getResources().getI18NString( - "service.gui.AUTHENTICATION_FAILED", - new String[]{ - userCredentials.getUserName(), - host}); - - userCredentials.setUserName(null); - userCredentials.setPasswordPersistent(false); - userCredentials = null; - - getConfigurationService().removeProperty( - UPDATE_USERNAME_CONFIG); - getConfigurationService().removeProperty( - UPDATE_PASSWORD_CONFIG); - - conn = url.openConnection(); - } - else - break; - } - } - conn.setConnectTimeout(10000); - conn.setReadTimeout(10000); - - Properties props = new Properties(); - props.load(conn.getInputStream()); - - lastVersion = props.getProperty("last_version"); - downloadLink = props.getProperty("download_link"); - - changesLink = - configString.substring(0, configString.lastIndexOf("/") + 1) - + props.getProperty("changes_html"); - - return lastVersion.compareTo(ver.toString()) <= 0; - } - catch (Exception e) - { - logger.warn("Cannot get and compare versions!"); - if (logger.isDebugEnabled()) - logger.debug("Error was: ", e); - // if we get an exception this mean we were unable to compare - // versions will return that current is newest to prevent opening - // info dialog about new version - return true; - } - } - - /** - * Show dialog informing about new version with button Download which - * triggers browser launching - */ - private void UpdaterShow() - { - final JDialog dialog = new SIPCommDialog() - { - private static final long serialVersionUID = 0L; - - protected void close(boolean isEscaped) - { - } - }; - dialog.setTitle( - getResources().getI18NString( - "plugin.updatechecker.DIALOG_TITLE")); - - JEditorPane contentMessage = new JEditorPane(); - contentMessage.setContentType("text/html"); - contentMessage.setOpaque(false); - contentMessage.setEditable(false); - - String dialogMsg = - getResources().getI18NString( - "plugin.updatechecker.DIALOG_MESSAGE", - new String[]{getResources() - .getSettingsString("service.gui.APPLICATION_NAME")}); - - if(lastVersion != null) - dialogMsg += - getResources().getI18NString( - "plugin.updatechecker.DIALOG_MESSAGE_2", - new String[]{ - getResources().getSettingsString( - "service.gui.APPLICATION_NAME"), - lastVersion}); - - contentMessage.setText(dialogMsg); - - JPanel contentPane = new TransparentPanel(new BorderLayout(5,5)); - contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, - 10)); - contentPane.add(contentMessage, BorderLayout.CENTER); - - JPanel buttonPanel - = new TransparentPanel(new FlowLayout(FlowLayout.CENTER, 10, - 10)); - JButton closeButton = new JButton( - getResources().getI18NString( - "plugin.updatechecker.BUTTON_CLOSE")); - - closeButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) - { - dialog.setVisible(false); - } - }); - - if(downloadLink != null) - { - JButton downloadButton = new JButton(getResources(). - getI18NString("plugin.updatechecker.BUTTON_DOWNLOAD")); - - downloadButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - if(OSUtils.IS_LINUX64) - downloadLink - = downloadLink.replace("i386", "amd64"); - - getBrowserLauncher().openURL(downloadLink); - dialog.dispose(); - } - }); - - buttonPanel.add(downloadButton); - } - - buttonPanel.add(closeButton); - - contentPane.add(buttonPanel, BorderLayout.SOUTH); - - dialog.setContentPane(contentPane); - - dialog.pack(); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - dialog.setLocation( - screenSize.width/2 - dialog.getWidth()/2, - screenSize.height/2 - dialog.getHeight()/2 - ); - - dialog.setVisible(true); - } - - /** - * Shows dialog informing about new version with button Install - * which triggers the update process. - */ - private void windowsUpdaterShow() - { - final JDialog dialog = new SIPCommDialog() - { - private static final long serialVersionUID = 0L; - - protected void close(boolean isEscaped) - { - } - }; - - dialog.setTitle( - getResources().getI18NString("plugin.updatechecker.DIALOG_TITLE")); - - JEditorPane contentMessage = new JEditorPane(); - contentMessage.setContentType("text/html"); - contentMessage.setOpaque(false); - contentMessage.setEditable(false); - - /* - * Use the font of the dialog because contentMessage is just like a - * label. - */ - contentMessage.putClientProperty( - JEditorPane.HONOR_DISPLAY_PROPERTIES, - Boolean.TRUE); - - String dialogMsg = - getResources().getI18NString("plugin.updatechecker.DIALOG_MESSAGE", - new String[]{getResources() - .getSettingsString("service.gui.APPLICATION_NAME")}); - - if(lastVersion != null) - dialogMsg += - getResources().getI18NString( - "plugin.updatechecker.DIALOG_MESSAGE_2", - new String[]{ - getResources().getSettingsString( - "service.gui.APPLICATION_NAME"), - lastVersion}); - - contentMessage.setText(dialogMsg); - - JPanel contentPane = new SIPCommFrame.MainContentPane(); - contentMessage.setBorder(BorderFactory.createEmptyBorder(10, 0, 20, 0)); - contentPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); - contentPane.add(contentMessage, BorderLayout.NORTH); - - JScrollPane scrollChanges = new JScrollPane(); - scrollChanges.setPreferredSize(new Dimension(550, 200)); - JEditorPane changesHtml = new JEditorPane(); - changesHtml.setContentType("text/html"); - changesHtml.setEditable(false); - changesHtml.setBorder(BorderFactory.createLoweredBevelBorder()); - scrollChanges.setViewportView(changesHtml); - contentPane.add(scrollChanges, BorderLayout.CENTER); - try - { - changesHtml.setPage(new URL(changesLink)); - } catch (Exception e) - { - logger.error("Cannot set changes Page", e); - } - - JPanel buttonPanel - = new TransparentPanel(new FlowLayout(FlowLayout.CENTER, 10, 10)); - JButton closeButton = new JButton( - getResources().getI18NString("plugin.updatechecker.BUTTON_CLOSE")); - - closeButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) - { - dialog.setVisible(false); - } - }); - - if(downloadLink != null) - { - JButton installButton = new JButton(getResources().getI18NString( - "plugin.updatechecker.BUTTON_INSTALL")); - - installButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) - { - if(OSUtils.IS_WINDOWS64) - downloadLink = downloadLink.replace("x86", "x64"); - - dialog.dispose(); - windowsUpdate(); - } - }); - - buttonPanel.add(installButton); - } - - buttonPanel.add(closeButton); - - contentPane.add(buttonPanel, BorderLayout.SOUTH); - - dialog.setContentPane(contentPane); - - dialog.pack(); - - dialog.setLocation( - Toolkit.getDefaultToolkit().getScreenSize().width/2 - - dialog.getWidth()/2, - Toolkit.getDefaultToolkit().getScreenSize().height/2 - - dialog.getHeight()/2 - ); - - dialog.setVisible(true); - } - - /** - * The update process itself. - * - Downloads the installer in a temp directory. - * - Warns that update will shutdown. - * - Triggers update (installer) in separate process with the help - * of update.exe and shutdowns. - */ - private void windowsUpdate() - { - File tempF = null; - try - { - final File temp = File.createTempFile("sc-install", ".exe"); - tempF = temp; - - URL u = new URL(downloadLink); - URLConnection uc = u.openConnection(); - - if (uc instanceof HttpURLConnection) - { - if(uc instanceof HttpsURLConnection) - { - CertificateVerificationService vs = - getCertificateVerificationService(); - - int port = u.getPort(); - - /* if we do not specify port in the URL - * (http://domain.org:port) we have to set up the default - * port of HTTP (80) or - * HTTPS (443). - */ - if(port == -1) - { - if(u.getProtocol().equals("http")) - { - port = 80; - } - else if(u.getProtocol().equals("https")) - { - port = 443; - } - } - - ((HttpsURLConnection)uc).setSSLSocketFactory( - vs.getSSLContext( - u.getHost(), port).getSocketFactory()); - } - - // we don't handle here authentication fails cause - // still we gone to downloading file we have gone through - // successful authentication - } - - InputStream in = uc.getInputStream(); - - // Chain a ProgressMonitorInputStream to the - // URLConnection's InputStream - final ProgressMonitorInputStream pin - = new ProgressMonitorInputStream(null, u.toString(), in); - - // Set the maximum value of the ProgressMonitor - ProgressMonitor pm = pin.getProgressMonitor(); - pm.setMaximum(uc.getContentLength()); - - final BufferedOutputStream out = - new BufferedOutputStream(new FileOutputStream(temp)); - new Thread(new Runnable() - { - public void run() - { - try - { - int read = -1; - byte[] buff = new byte[1024]; - while((read = pin.read(buff)) != -1) - { - out.write(buff, 0, read); - } - pin.close(); - out.flush(); - out.close(); - - if(getUIService().getPopupDialog(). - showConfirmPopupDialog( - getResources().getI18NString( - "plugin.updatechecker.DIALOG_WARN"), - getResources().getI18NString( - "plugin.updatechecker.DIALOG_TITLE"), - PopupDialog.YES_NO_OPTION, - PopupDialog.QUESTION_MESSAGE - ) != PopupDialog.YES_OPTION) - { - return; - } - - String packageName = getResources().getSettingsString( - "plugin.updatechecker.package.name"); - - // file saved. Now start updater and shutdown. - String workingDir = System.getProperty("user.dir"); - - String updateFileLocation = workingDir + File.separator; - if(packageName != null) - updateFileLocation += packageName + "-up2date.exe"; - else - updateFileLocation += "up2date.exe"; - - ProcessBuilder processBuilder - = new ProcessBuilder( - new String[] - { - updateFileLocation, - "--wait-parent", - "--allow-elevation", - temp.getCanonicalPath(), - workingDir - }); - processBuilder.start(); - - getShutdownService().beginShutdown(); - - } catch (Exception e) - { - logger.error("Error saving", e); - try - { - pin.close(); - out.close(); - } catch (Exception e1) - {} - } - } - }).start(); - - } - catch(FileNotFoundException e) - { - getUIService().getPopupDialog().showMessagePopupDialog( - getResources().getI18NString( - "plugin.updatechecker.DIALOG_MISSING_UPDATE"), - getResources().getI18NString( - "plugin.updatechecker.DIALOG_NOUPDATE_TITLE"), - PopupDialog.INFORMATION_MESSAGE); - tempF.delete(); - } - catch (Exception e) - { - if (logger.isInfoEnabled()) - logger.info("Error starting update process!", e); - tempF.delete(); - } - } - - /** - * Invokes action for checking for updates. - */ - private void checkForUpdate() - { - if(isNewestVersion()) - { - if(isAuthenticationCanceled) - return; - - getUIService().getPopupDialog().showMessagePopupDialog( - getResources().getI18NString( - "plugin.updatechecker.DIALOG_NOUPDATE"), - getResources().getI18NString( - "plugin.updatechecker.DIALOG_NOUPDATE_TITLE"), - PopupDialog.INFORMATION_MESSAGE); - } - else - windowsUpdaterShow(); - } - - /** - * Installs Dummy TrustManager will not try to validate self-signed certs. - * Fix some problems with not proper use of certs. - */ - private static void removeDownloadRestrictions() - { - HostnameVerifier hv = new HostnameVerifier() - { - public boolean verify(String urlHostName, SSLSession session) - { - logger.warn("Warning: URL Host: " + urlHostName + - " vs. " + session.getPeerHost()); - return true; - } - }; - HttpsURLConnection.setDefaultHostnameVerifier(hv); - - Authenticator.setDefault(new Authenticator() - { - protected PasswordAuthentication getPasswordAuthentication() - { - if(userCredentials == null) - { - // if there is something save return it - ConfigurationService config = getConfigurationService(); - String uName - = (String) config.getProperty(UPDATE_USERNAME_CONFIG); - if(uName != null) - { - String pass - = (String) config.getProperty(UPDATE_PASSWORD_CONFIG); - - if(pass != null) - { - userCredentials = new UserCredentials(); - userCredentials.setUserName(uName); - userCredentials.setPassword(new String( - Base64.decode(pass)).toCharArray()); - userCredentials.setPasswordPersistent(true); - } - } - } - - if(userCredentials != null) - { - return new PasswordAuthentication( - userCredentials.getUserName(), - userCredentials.getPassword()); - } - else - { - host = getRequestingHost(); - - AuthenticationWindow authWindow = null; - if(errorMessage == null) - { - authWindow = new AuthenticationWindow(host, true, null); - } - else - { - authWindow = new AuthenticationWindow( - null, null, host, true, null, errorMessage); - // we showed the message, remove it - errorMessage = null; - } - - userCredentials = new UserCredentials(); - - authWindow.setVisible(true); - - if (!authWindow.isCanceled()) - { - isAuthenticationCanceled = false; - userCredentials.setUserName(authWindow.getUserName()); - userCredentials.setPassword(authWindow.getPassword()); - userCredentials.setPasswordPersistent( - authWindow.isRememberPassword()); - - if(authWindow.isRememberPassword()) - { - // if save password is checked save the pass - getConfigurationService().setProperty( - UPDATE_USERNAME_CONFIG, - userCredentials.getUserName()); - getConfigurationService().setProperty( - UPDATE_PASSWORD_CONFIG, - new String(Base64.encode( - userCredentials.getPasswordAsString() - .getBytes()))); - } - - return new PasswordAuthentication( - userCredentials.getUserName(), - userCredentials.getPassword()); - } - else - { - isAuthenticationCanceled = true; - userCredentials.setUserName(null); - userCredentials = null; - - getConfigurationService().removeProperty( - UPDATE_USERNAME_CONFIG); - getConfigurationService().removeProperty( - UPDATE_PASSWORD_CONFIG); - } - - return null; - } - } - }); - } - - /** - * The menu entry under tools menu. - */ - private class UpdateMenuButtonComponent - extends AbstractPluginComponent - { - /** - * The menu item to use. - */ - private final JMenuItem updateMenuItem - = new JMenuItem(getResources(). - getI18NString("plugin.updatechecker.UPDATE_MENU_ENTRY")); - - /** - * Creates update menu component. - * - * @param container the container of the update menu component - */ - UpdateMenuButtonComponent(Container container) - { - super(container); - - updateMenuItem.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - // run outside swing thread, if password is required we - // will block the swing - new Thread() - { - public void run() - { - checkForUpdate(); - } - }.start(); - } - }); - } - - /** - * Get the name of the component. - * - * @return name of the component - */ - public String getName() - { - return getResources().getI18NString( - "plugin.updatechecker.UPDATE_MENU_ENTRY"); - } - - /** - * Get the Component. - * - * @return the Component - */ - public Object getComponent() - { - return updateMenuItem; - } - } - - /** - * The thread that do the actual checking. - */ - private class UpdateCheckThread - implements Runnable - { - /** - * Thread entry point. - */ - public void run() - { - if (OSUtils.IS_WINDOWS) - { - // register update button - Hashtable toolsMenuFilter - = new Hashtable(); - toolsMenuFilter.put( Container.CONTAINER_ID, - Container.CONTAINER_HELP_MENU.getID()); - - bundleContext.registerService( - PluginComponent.class.getName(), - new UpdateMenuButtonComponent( - Container.CONTAINER_HELP_MENU), - toolsMenuFilter); - } - - // check whether check at startup is enabled - if(!getConfigurationService().getBoolean(UPDATECHECKER_ENABLED, - true)) - { - return; - } - - if(isNewestVersion()) - return; - - if (OSUtils.IS_WINDOWS) - { - windowsUpdaterShow(); - } - else - { - UpdaterShow(); - } - } - } -} +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.updatechecker; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.util.*; + +import javax.net.ssl.*; +import javax.swing.*; +import javax.swing.text.*; + +import net.java.sip.communicator.service.browserlauncher.*; +import net.java.sip.communicator.service.certificate.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.gui.Container; // disambiguation +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.service.shutdown.*; +import net.java.sip.communicator.service.version.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.swing.*; + +import org.osgi.framework.*; + +/** + * Implements BundleActivator for the updatechecker plug-in. + * + * @author Damian Minkov + * @author Lyubomir Marinov + */ +public class UpdateCheckActivator + implements BundleActivator +{ + /** + * The Logger used by the UpdateCheckActivator class and + * its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(UpdateCheckActivator.class); + + /** + * The bundle context. + */ + private static BundleContext bundleContext = null; + + /** + * Reference to the BrowserLauncherService. + */ + private static BrowserLauncherService browserLauncher; + + /** + * Reference to the ResourceManagementService. + */ + private static ResourceManagementService resources; + + /** + * Reference to the ConfigurationService. + */ + private static ConfigurationService configuration; + + /** + * Reference to the UIService. + */ + private static UIService uiService = null; + + /** + * Reference to the CertificateVerificationService. + */ + private static CertificateVerificationService certificateVerification = null; + + /** + * The download link of the update. + */ + private String downloadLink = null; + + /** + * The last version of the software. + */ + private String lastVersion = null; + + /** + * The ChangeLog link. + */ + private String changesLink = null; + + /** + * The user credentials. + */ + private static UserCredentials userCredentials = null; + + /** + * The error message is any. + */ + private static String errorMessage = null; + + /** + * The host we are querying for updates. + */ + private static String host = null; + + /** + * Whether user has canceled authentication process. + */ + private static boolean isAuthenticationCanceled = false; + + /** + * Property name of the username used if HTTP authentication is required. + */ + private static final String UPDATE_USERNAME_CONFIG = + "net.java.sip.communicator.plugin.updatechecker.UPDATE_SITE_USERNAME"; + + /** + * Property name of the password used if HTTP authentication is required. + */ + private static final String UPDATE_PASSWORD_CONFIG = + "net.java.sip.communicator.plugin.updatechecker.UPDATE_SITE_PASSWORD"; + + /** + * Property indicating whether update check is enabled. + */ + private static final String UPDATECHECKER_ENABLED = + "net.java.sip.communicator.plugin.updatechecker.ENABLED"; + + /** + * Property name for the update link in the configuration file. + */ + private static final String PROP_UPDATE_LINK = + "net.java.sip.communicator.UPDATE_LINK"; + + /** + * The JDialog, if any, which is associated with the currently + * executing "Check for Updates". While the "Check for Updates" + * functionality cannot be entered, clicking the "Check for Updates" menu + * item will bring it to the front. + */ + private JDialog checkForUpdatesDialog; + + /** + * The "Check for Updates" PluginComponent registered by this + * UpdateCheckActivator. + */ + private CheckForUpdatesMenuItemComponent checkForUpdatesMenuItemComponent; + + /** + * The indicator/counter which determines how many methods are currently + * executing the "Check for Updates" functionality so that it is known + * whether it can be entered. + */ + private int inCheckForUpdates = 0; + + static + { + removeDownloadRestrictions(); + } + + /** + * Starts this bundle + * + * @param bundleContext BundleContext provided by OSGi framework + * @throws Exception if something goes wrong during start + */ + public void start(BundleContext bundleContext) throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("Update checker [STARTED]"); + + UpdateCheckActivator.bundleContext = bundleContext; + + if (OSUtils.IS_WINDOWS) + { + // Register the "Check for Updates" menu item. + checkForUpdatesMenuItemComponent + = new CheckForUpdatesMenuItemComponent( + Container.CONTAINER_HELP_MENU); + + Hashtable toolsMenuFilter + = new Hashtable(); + toolsMenuFilter.put( + Container.CONTAINER_ID, + Container.CONTAINER_HELP_MENU.getID()); + + bundleContext.registerService( + PluginComponent.class.getName(), + checkForUpdatesMenuItemComponent, + toolsMenuFilter); + + // Check for software update upon startup if enabled. + if(getConfiguration().getBoolean(UPDATECHECKER_ENABLED, true)) + checkForUpdates(false); + } + + if (logger.isDebugEnabled()) + logger.debug("Update checker [REGISTERED]"); + } + + /** + * Stop the bundle. Nothing to stop for now. + * @param bundleContext BundleContext provided by OSGi framework + * @throws Exception if something goes wrong during stop + */ + public void stop(BundleContext bundleContext) + throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("Update checker [STOPPED]"); + } + + /** + * Returns the BrowserLauncherService obtained from the bundle + * context. + * @return the BrowserLauncherService obtained from the bundle + * context + */ + private static BrowserLauncherService getBrowserLauncher() + { + if (browserLauncher == null) + { + browserLauncher + = ServiceUtils.getService( + bundleContext, + BrowserLauncherService.class); + } + return browserLauncher; + } + + /** + * Returns the ConfigurationService obtained from the bundle + * context. + * + * @return the ConfigurationService obtained from the bundle + * context + */ + private static ConfigurationService getConfiguration() + { + if (configuration == null) + { + configuration + = ServiceUtils.getService( + bundleContext, + ConfigurationService.class); + } + return configuration; + } + + /** + * Gets a reference to a ShutdownService implementation + * currently registered in the bundle context of the active + * UpdateCheckActivator instance. + *

+ * The returned reference to ShutdownService is not being + * cached. + *

+ * + * @return reference to a ShutdownService implementation + * currently registered in the bundle context of the active + * UpdateCheckActivator instance + */ + private static ShutdownService getShutdownService() + { + return ServiceUtils.getService(bundleContext, ShutdownService.class); + } + + /** + * Returns a reference to the UIService implementation currently registered + * in the bundle context or null if no such implementation was found. + * + * @return a reference to a UIService implementation currently registered + * in the bundle context or null if no such implementation was found. + */ + private static UIService getUIService() + { + if(uiService == null) + uiService = ServiceUtils.getService(bundleContext, UIService.class); + return uiService; + } + + /** + * Returns resource service. + * @return the resource service. + */ + private static ResourceManagementService getResources() + { + if (resources == null) + { + resources + = ServiceUtils.getService( + bundleContext, + ResourceManagementService.class); + } + return resources; + } + + /** + * Returns the certificate verification service implementation. + * + * @return the CertificateVerificationService + */ + private static CertificateVerificationService getCertificateVerification() + { + if(certificateVerification == null) + { + certificateVerification + = ServiceUtils.getService( + bundleContext, + CertificateVerificationService.class); + } + return certificateVerification; + } + + /** + * Checks the first link as files on the web are sorted by date. + * + * @return true if we are currently running the newest version; + * otherwise, false + */ + private boolean isNewestVersion() + { + try + { + ServiceReference serviceReference = bundleContext + .getServiceReference( net.java.sip.communicator.service.version. + VersionService.class.getName()); + + VersionService verService = (VersionService) bundleContext + .getService(serviceReference); + + net.java.sip.communicator.service.version.Version + ver = verService.getCurrentVersion(); + + String configString + = getConfiguration().getString(PROP_UPDATE_LINK); + if(configString == null) + configString = Resources.getConfigString("update_link"); + if(configString == null) + { + if (logger.isDebugEnabled()) + logger.debug( + "Updates are disabled. Faking latest version."); + return true; + } + + URL url = new URL(configString); + URLConnection conn = url.openConnection(); + + if (conn instanceof HttpURLConnection) + { + while(((HttpURLConnection)conn).getResponseCode() == + HttpURLConnection.HTTP_UNAUTHORIZED + && !isAuthenticationCanceled) + { + if(userCredentials.getUserName() != null) + { + errorMessage = getResources().getI18NString( + "service.gui.AUTHENTICATION_FAILED", + new String[]{ + userCredentials.getUserName(), + host}); + + userCredentials.setUserName(null); + userCredentials.setPasswordPersistent(false); + userCredentials = null; + + getConfiguration().removeProperty( + UPDATE_USERNAME_CONFIG); + getConfiguration().removeProperty( + UPDATE_PASSWORD_CONFIG); + + conn = url.openConnection(); + } + else + break; + } + } + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + + Properties props = new Properties(); + props.load(conn.getInputStream()); + + lastVersion = props.getProperty("last_version"); + downloadLink = props.getProperty("download_link"); + + changesLink = + configString.substring(0, configString.lastIndexOf("/") + 1) + + props.getProperty("changes_html"); + + return lastVersion.compareTo(ver.toString()) <= 0; + } + catch (Exception e) + { + logger.warn("Cannot get and compare versions!"); + if (logger.isDebugEnabled()) + logger.debug("Error was: ", e); + // if we get an exception this mean we were unable to compare + // versions will return that current is newest to prevent opening + // info dialog about new version + return true; + } + } + + /** + * Shows dialog informing about new version with button Download which + * triggers browser launching + */ + private void showGenericNewVersionAvailableDialog() + { + /* + * Before showing the dialog, we'll enterCheckForUpdates() in order to + * notify that it is not safe to enter "Check for Updates" again. If we + * don't manage to show the dialog, we'll have to exitCheckForUpdates(). + * If we manage though, we'll have to exitCheckForUpdates() but only + * once depending on its modality. + */ + final boolean[] exitCheckForUpdates = new boolean[] { false }; + final JDialog dialog = new SIPCommDialog() + { + private static final long serialVersionUID = 0L; + + protected void close(boolean escaped) + { + synchronized (exitCheckForUpdates) + { + if (exitCheckForUpdates[0]) + exitCheckForUpdates(this); + } + } + }; + dialog.setTitle( + getResources().getI18NString( + "plugin.updatechecker.DIALOG_TITLE")); + + JEditorPane contentMessage = new JEditorPane(); + contentMessage.setContentType("text/html"); + contentMessage.setOpaque(false); + contentMessage.setEditable(false); + + String dialogMsg = + getResources().getI18NString( + "plugin.updatechecker.DIALOG_MESSAGE", + new String[]{getResources() + .getSettingsString("service.gui.APPLICATION_NAME")}); + + if(lastVersion != null) + dialogMsg += + getResources().getI18NString( + "plugin.updatechecker.DIALOG_MESSAGE_2", + new String[]{ + getResources().getSettingsString( + "service.gui.APPLICATION_NAME"), + lastVersion}); + + contentMessage.setText(dialogMsg); + + JPanel contentPane = new TransparentPanel(new BorderLayout(5,5)); + contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, + 10)); + contentPane.add(contentMessage, BorderLayout.CENTER); + + JPanel buttonPanel + = new TransparentPanel(new FlowLayout(FlowLayout.CENTER, 10, + 10)); + final JButton closeButton = new JButton( + getResources().getI18NString( + "plugin.updatechecker.BUTTON_CLOSE")); + + closeButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + dialog.dispose(); + if (exitCheckForUpdates[0]) + exitCheckForUpdates(dialog); + } + }); + + if(downloadLink != null) + { + JButton downloadButton + = new JButton( + getResources().getI18NString( + "plugin.updatechecker.BUTTON_DOWNLOAD")); + + downloadButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if(OSUtils.IS_LINUX64) + downloadLink + = downloadLink.replace("i386", "amd64"); + + getBrowserLauncher().openURL(downloadLink); + + /* + * Do the same as the Close button in order to not duplicate + * the code. + */ + closeButton.doClick(); + } + }); + + buttonPanel.add(downloadButton); + } + + buttonPanel.add(closeButton); + + contentPane.add(buttonPanel, BorderLayout.SOUTH); + + dialog.setContentPane(contentPane); + + dialog.pack(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + dialog.setLocation( + screenSize.width/2 - dialog.getWidth()/2, + screenSize.height/2 - dialog.getHeight()/2); + + synchronized (exitCheckForUpdates) + { + enterCheckForUpdates(dialog); + exitCheckForUpdates[0] = true; + } + try + { + dialog.setVisible(true); + } + finally + { + synchronized (exitCheckForUpdates) + { + if (exitCheckForUpdates[0] && dialog.isModal()) + exitCheckForUpdates(dialog); + } + } + } + + /** + * Shows dialog informing about new version with button Install + * which triggers the update process. + */ + private void showWindowsNewVersionAvailableDialog() + { + /* + * Before showing the dialog, we'll enterCheckForUpdates() in order to + * notify that it is not safe to enter "Check for Updates" again. If we + * don't manage to show the dialog, we'll have to exitCheckForUpdates(). + * If we manage though, we'll have to exitCheckForUpdates() but only + * once depending on its modality. + */ + final boolean[] exitCheckForUpdates = new boolean[] { false }; + final JDialog dialog = new SIPCommDialog() + { + private static final long serialVersionUID = 0L; + + protected void close(boolean escaped) + { + synchronized (exitCheckForUpdates) + { + if (exitCheckForUpdates[0]) + exitCheckForUpdates(this); + } + } + }; + ResourceManagementService resources = getResources(); + + dialog.setTitle( + resources.getI18NString("plugin.updatechecker.DIALOG_TITLE")); + + JEditorPane contentMessage = new JEditorPane(); + contentMessage.setContentType("text/html"); + contentMessage.setOpaque(false); + contentMessage.setEditable(false); + + /* + * Use the font of the dialog because contentMessage is just like a + * label. + */ + contentMessage.putClientProperty( + JEditorPane.HONOR_DISPLAY_PROPERTIES, + Boolean.TRUE); + + String dialogMsg + = resources.getI18NString( + "plugin.updatechecker.DIALOG_MESSAGE", + new String[] + { + resources.getSettingsString( + "service.gui.APPLICATION_NAME") + }); + + if(lastVersion != null) + { + dialogMsg + += resources.getI18NString( + "plugin.updatechecker.DIALOG_MESSAGE_2", + new String[] + { + resources.getSettingsString( + "service.gui.APPLICATION_NAME"), + lastVersion + }); + } + + contentMessage.setText(dialogMsg); + + JPanel contentPane = new SIPCommFrame.MainContentPane(); + contentMessage.setBorder(BorderFactory.createEmptyBorder(10, 0, 20, 0)); + contentPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + contentPane.add(contentMessage, BorderLayout.NORTH); + + JScrollPane scrollChanges = new JScrollPane(); + scrollChanges.setPreferredSize(new Dimension(550, 200)); + JEditorPane changesHtml = new JEditorPane(); + changesHtml.setContentType("text/html"); + changesHtml.setEditable(false); + changesHtml.setBorder(BorderFactory.createLoweredBevelBorder()); + scrollChanges.setViewportView(changesHtml); + contentPane.add(scrollChanges, BorderLayout.CENTER); + try + { + Document changesHtmlDocument = changesHtml.getDocument(); + + if (changesHtmlDocument instanceof AbstractDocument) + { + ((AbstractDocument) changesHtmlDocument) + .setAsynchronousLoadPriority(0); + } + changesHtml.setPage(new URL(changesLink)); + } + catch (Exception e) + { + logger.error("Cannot set changes Page", e); + } + + JPanel buttonPanel + = new TransparentPanel(new FlowLayout(FlowLayout.CENTER, 10, 10)); + final JButton closeButton + = new JButton( + resources.getI18NString( + "plugin.updatechecker.BUTTON_CLOSE")); + + closeButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + dialog.dispose(); + if (exitCheckForUpdates[0]) + exitCheckForUpdates(dialog); + } + }); + + if(downloadLink != null) + { + JButton installButton + = new JButton( + resources.getI18NString( + "plugin.updatechecker.BUTTON_INSTALL")); + + installButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if(OSUtils.IS_WINDOWS64) + downloadLink = downloadLink.replace("x86", "x64"); + + enterCheckForUpdates(null); + try + { + /* + * Do the same as the Close button in order to not + * duplicate the code. + */ + closeButton.doClick(); + } + finally + { + boolean windowsUpdateThreadHasStarted = false; + + try + { + new Thread() + { + @Override + public void run() + { + try + { + windowsUpdate(); + } + finally + { + exitCheckForUpdates(null); + } + } + }.start(); + windowsUpdateThreadHasStarted = true; + } + finally + { + if (!windowsUpdateThreadHasStarted) + exitCheckForUpdates(null); + } + } + } + }); + + buttonPanel.add(installButton); + } + + buttonPanel.add(closeButton); + + contentPane.add(buttonPanel, BorderLayout.SOUTH); + + dialog.setContentPane(contentPane); + + dialog.pack(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + dialog.setLocation( + screenSize.width/2 - dialog.getWidth()/2, + screenSize.height/2 - dialog.getHeight()/2); + + synchronized (exitCheckForUpdates) + { + enterCheckForUpdates(dialog); + exitCheckForUpdates[0] = true; + } + try + { + dialog.setVisible(true); + } + finally + { + synchronized (exitCheckForUpdates) + { + if (exitCheckForUpdates[0] && dialog.isModal()) + exitCheckForUpdates(dialog); + } + } + } + + /** + * The update process itself. + * - Downloads the setup in a temporary directory. + * - Warns that update will shut down. + * - Triggers the setup in a separate process and shuts down. + */ + private void windowsUpdate() + { + final File[] tempFile = new File[1]; + FileOutputStream tempFileOutputStream = null; + boolean deleteTempFile = false; + + try + { + URL u = new URL(downloadLink); + + tempFileOutputStream = createTempFileOutputStream(u, tempFile); + + URLConnection uc = u.openConnection(); + + if (uc instanceof HttpURLConnection) + { + if(uc instanceof HttpsURLConnection) + { + CertificateVerificationService cvs + = getCertificateVerification(); + + int port = u.getPort(); + + /* if we do not specify port in the URL + * (http://domain.org:port) we have to set up the default + * port of HTTP (80) or + * HTTPS (443). + */ + if(port == -1) + { + if(u.getProtocol().equals("http")) + port = 80; + else if(u.getProtocol().equals("https")) + port = 443; + } + + ((HttpsURLConnection)uc).setSSLSocketFactory( + cvs.getSSLContext(u.getHost(), port) + .getSocketFactory()); + } + + // we don't handle here authentication fails cause + // still we gone to downloading file we have gone through + // successful authentication + } + + // Track the progress of the download. + final ProgressMonitorInputStream input + = new ProgressMonitorInputStream( + null, + u.toString(), + uc.getInputStream()); + // Set the maximum value of the ProgressMonitor + input.getProgressMonitor().setMaximum(uc.getContentLength()); + + final BufferedOutputStream output + = new BufferedOutputStream(tempFileOutputStream); + + try + { + int read = -1; + byte[] buff = new byte[1024]; + + while((read = input.read(buff)) != -1) + output.write(buff, 0, read); + try + { + input.close(); + } + catch (IOException ioe) + { + /* + * Ignore it because we've already downloaded the + * setup and that's what matters most. + */ + } + output.close(); + + if(getUIService().getPopupDialog() + .showConfirmPopupDialog( + getResources().getI18NString( + "plugin.updatechecker.DIALOG_WARN"), + getResources().getI18NString( + "plugin.updatechecker.DIALOG_TITLE"), + PopupDialog.YES_NO_OPTION, + PopupDialog.QUESTION_MESSAGE) + != PopupDialog.YES_OPTION) + return; + + /* + * The setup has been downloaded. Now start it and shut + * down. + */ + new ProcessBuilder( + tempFile[0].getCanonicalPath(), + "--wait-parent", + "SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR=\"" + + System.getProperty("user.dir") + + "\"") + .start(); + + getShutdownService().beginShutdown(); + } + catch (Exception e) + { + logger.error("Error saving", e); + } + finally + { + try + { + input.close(); + } + catch (IOException ioe) + { + } + try + { + output.close(); + } + catch (IOException ioe) + { + } + } + } + catch(FileNotFoundException fnfe) + { + deleteTempFile = true; + getUIService().getPopupDialog().showMessagePopupDialog( + getResources().getI18NString( + "plugin.updatechecker.DIALOG_MISSING_UPDATE"), + getResources().getI18NString( + "plugin.updatechecker.DIALOG_NOUPDATE_TITLE"), + PopupDialog.INFORMATION_MESSAGE); + } + catch (Exception e) + { + deleteTempFile = true; + if (logger.isInfoEnabled()) + logger.info("Error starting update process!", e);; + } + finally + { + /* + * If we've failed, delete the temporary file into which the setup + * was supposed to be or has already been downloaded. + */ + if (deleteTempFile) + { + if (tempFileOutputStream != null) + { + try + { + tempFileOutputStream.close(); + } + catch (IOException ioe) + { + // Ignore it because there's nothing else we can do. + } + } + if (tempFile[0] != null) + tempFile[0].delete(); + } + } + } + + /** + * Tries to create a new FileOutputStream for a temporary file into + * which the setup is to be downloaded. Because temporary files generally + * have random characters in their names and the name of the setup may be + * shown to the user, first tries to use the name of the URL to be + * downloaded because it likely is prettier. + * + * @param url the URL of the file to be downloaded + * @param tempFile a File array of at least one element which is to + * receive the created File instance at index zero (if successful) + * @return the newly created FileOutputStream + * @throws IOException if anything goes wrong while creating the new + * FileOutputStream + */ + private FileOutputStream createTempFileOutputStream( + URL url, + File[] tempFile) + throws IOException + { + /* + * Try to use the name from the URL because it isn't a "randomly" + * generated one. + */ + String path = url.getPath(); + + File tf = null; + FileOutputStream tfos = null; + + if ((path != null) && (path.length() != 0)) + { + int nameBeginIndex =path.lastIndexOf('/'); + String name; + + if (nameBeginIndex > 0) + { + name = path.substring(nameBeginIndex + 1); + nameBeginIndex = name.lastIndexOf('\\'); + if (nameBeginIndex > 0) + name = name.substring(nameBeginIndex + 1); + } + else + name = path; + + /* + * Make sure the extension of the name is EXE so that we're able to + * execute it later on. + */ + int nameLength = name.length(); + + if (nameLength != 0) + { + int baseNameEnd = name.lastIndexOf('.'); + + if (baseNameEnd == -1) + name += ".exe"; + else if (baseNameEnd == 0) + { + if (!".exe".equalsIgnoreCase(name)) + name += ".exe"; + } + else + name = name.substring(0, baseNameEnd) + ".exe"; + + try + { + String tempDir = System.getProperty("java.io.tmpdir"); + + if ((tempDir != null) && (tempDir.length() != 0)) + { + tf = new File(tempDir, name); + tfos = new FileOutputStream(tf); + } + } + catch (FileNotFoundException fnfe) + { + // Ignore it because we'll try File#createTempFile(). + } + catch (SecurityException se) + { + // Ignore it because we'll try File#createTempFile(). + } + } + } + + // Well, we couldn't use a pretty name so try File#createTempFile(). + if (tfos == null) + { + tf = File.createTempFile("sc-setup", ".exe"); + tfos = new FileOutputStream(tf); + } + + tempFile[0] = tf; + return tfos; + } + + /** + * Invokes "Check for Updates". + * + * @param notifyAboutNewestVersion true if the user is to be + * notified if they have the newest version already; otherwise, + * false + */ + private synchronized void checkForUpdates( + final boolean notifyAboutNewestVersion) + { + if (inCheckForUpdates > 0) + { + if (checkForUpdatesDialog != null) + checkForUpdatesDialog.toFront(); + return; + } + + Thread checkForUpdatesThread + = new Thread() + { + @Override + public void run() + { + try + { + if(isNewestVersion()) + { + if(!isAuthenticationCanceled + && notifyAboutNewestVersion) + { + ResourceManagementService resources + = getResources(); + + getUIService() + .getPopupDialog() + .showMessagePopupDialog( + resources.getI18NString( + "plugin.updatechecker.DIALOG_NOUPDATE"), + resources.getI18NString( + "plugin.updatechecker.DIALOG_NOUPDATE_TITLE"), + PopupDialog.INFORMATION_MESSAGE); + } + } + else if (OSUtils.IS_WINDOWS) + showWindowsNewVersionAvailableDialog(); + else + showGenericNewVersionAvailableDialog(); + } + finally + { + exitCheckForUpdates(null); + } + } + }; + + checkForUpdatesThread.setDaemon(true); + + enterCheckForUpdates(null); + try + { + checkForUpdatesThread.start(); + checkForUpdatesThread = null; + } + finally + { + if (checkForUpdatesThread != null) + exitCheckForUpdates(null); + } + } + + /** + * Notifies this UpdateCheckActivator that a method is entering the + * "Check for Updates" functionality and it is thus not allowed to enter it + * again. + * + * @param checkForUpdatesDialog the JDialog associated with the + * entry in the "Check for Updates" functionality if any. While "Check for + * Updates" cannot be entered again, clicking the "Check for Updates" menu + * item will bring the checkForUpdatesDialog to the front. + */ + private synchronized void enterCheckForUpdates( + JDialog checkForUpdatesDialog) + { + inCheckForUpdates++; +// if (1 == inCheckForUpdates) +// checkForUpdatesMenuItemComponent.getComponent().setEnabled(false); + + if (checkForUpdatesDialog != null) + this.checkForUpdatesDialog = checkForUpdatesDialog; + } + + /** + * Notifies this UpdateCheckActivator that a method is exiting the + * "Check for Updates" functionality and it may thus be allowed to enter it + * again. + * + * @param checkForUpdatesDialog the JDialog which was associated + * with the matching call to {@link #enterCheckForUpdates(JDialog)} if any + */ + private synchronized void exitCheckForUpdates(JDialog checkForUpdatesDialog) + { + if (inCheckForUpdates == 0) + throw new IllegalStateException("inCheckForUpdates"); + else + { + inCheckForUpdates--; +// if (0 == inCheckForUpdates) +// { +// checkForUpdatesMenuItemComponent.getComponent().setEnabled( +// true); +// } + + if ((checkForUpdatesDialog != null) + && (this.checkForUpdatesDialog == checkForUpdatesDialog)) + this.checkForUpdatesDialog = null; + } + } + + /** + * Installs Dummy TrustManager will not try to validate self-signed certs. + * Fix some problems with not proper use of certs. + */ + private static void removeDownloadRestrictions() + { + HostnameVerifier hv = new HostnameVerifier() + { + public boolean verify(String urlHostName, SSLSession session) + { + logger.warn("Warning: URL Host: " + urlHostName + + " vs. " + session.getPeerHost()); + return true; + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(hv); + + Authenticator.setDefault(new Authenticator() + { + protected PasswordAuthentication getPasswordAuthentication() + { + if(userCredentials == null) + { + // if there is something save return it + ConfigurationService config = getConfiguration(); + String uName + = (String) config.getProperty(UPDATE_USERNAME_CONFIG); + if(uName != null) + { + String pass + = (String) config.getProperty(UPDATE_PASSWORD_CONFIG); + + if(pass != null) + { + userCredentials = new UserCredentials(); + userCredentials.setUserName(uName); + userCredentials.setPassword(new String( + Base64.decode(pass)).toCharArray()); + userCredentials.setPasswordPersistent(true); + } + } + } + + if(userCredentials != null) + { + return new PasswordAuthentication( + userCredentials.getUserName(), + userCredentials.getPassword()); + } + else + { + host = getRequestingHost(); + + AuthenticationWindow authWindow = null; + if(errorMessage == null) + { + authWindow = new AuthenticationWindow(host, true, null); + } + else + { + authWindow = new AuthenticationWindow( + null, null, host, true, null, errorMessage); + // we showed the message, remove it + errorMessage = null; + } + + userCredentials = new UserCredentials(); + + authWindow.setVisible(true); + + if (!authWindow.isCanceled()) + { + isAuthenticationCanceled = false; + userCredentials.setUserName(authWindow.getUserName()); + userCredentials.setPassword(authWindow.getPassword()); + userCredentials.setPasswordPersistent( + authWindow.isRememberPassword()); + + if(authWindow.isRememberPassword()) + { + // if save password is checked save the pass + getConfiguration().setProperty( + UPDATE_USERNAME_CONFIG, + userCredentials.getUserName()); + getConfiguration().setProperty( + UPDATE_PASSWORD_CONFIG, + new String( + Base64.encode( + userCredentials + .getPasswordAsString() + .getBytes()))); + } + + return new PasswordAuthentication( + userCredentials.getUserName(), + userCredentials.getPassword()); + } + else + { + isAuthenticationCanceled = true; + userCredentials.setUserName(null); + userCredentials = null; + + getConfiguration().removeProperty( + UPDATE_USERNAME_CONFIG); + getConfiguration().removeProperty( + UPDATE_PASSWORD_CONFIG); + } + + return null; + } + } + }); + } + + /** + * Implements PluginComponent for the "Check for Updates" menu + * item. + */ + private class CheckForUpdatesMenuItemComponent + extends AbstractPluginComponent + { + /** + * The "Check for Updates" menu item. + */ + private final JMenuItem checkForUpdatesMenuItem + = new JMenuItem( + getResources().getI18NString( + "plugin.updatechecker.UPDATE_MENU_ENTRY")); + + /** + * Initializes a new "Check for Updates" menu item. + * + * @param container the container of the update menu component + */ + public CheckForUpdatesMenuItemComponent(Container container) + { + super(container); + + checkForUpdatesMenuItem.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + checkForUpdates(true); + } + }); + } + + /** + * Gets the UI Component of this PluginComponent. + * + * @return the UI Component of this PluginComponent + * @see PluginComponent#getComponent() + */ + public JMenuItem getComponent() + { + return checkForUpdatesMenuItem; + } + + /** + * Gets the name of this PluginComponent. + * + * @return the name of this PluginComponent + * @see PluginComponent#getName() + */ + public String getName() + { + return getResources().getI18NString( + "plugin.updatechecker.UPDATE_MENU_ENTRY"); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/updatechecker/updatecheck.manifest.mf b/src/net/java/sip/communicator/plugin/updatechecker/updatecheck.manifest.mf index 29f266ede..fc2b897c9 100644 --- a/src/net/java/sip/communicator/plugin/updatechecker/updatecheck.manifest.mf +++ b/src/net/java/sip/communicator/plugin/updatechecker/updatecheck.manifest.mf @@ -1,18 +1,18 @@ Bundle-Activator: net.java.sip.communicator.plugin.updatechecker.UpdateCheckActivator -Bundle-Name: UpdateCheckPlugin -Bundle-Description: A bundle that implements the UpdateCheck Plugin Package. +Bundle-Description: Checks for software updates +Bundle-Name: Update Checker Bundle-Vendor: sip-communicator.org Bundle-Version: 0.0.1 System-Bundle: yes Import-Package: org.osgi.framework, net.java.sip.communicator.service.browserlauncher, + net.java.sip.communicator.service.certificate, net.java.sip.communicator.service.configuration, net.java.sip.communicator.service.gui, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.resources, net.java.sip.communicator.service.shutdown, net.java.sip.communicator.service.version, - net.java.sip.communicator.service.certificate, net.java.sip.communicator.util, net.java.sip.communicator.util.swing, javax.imageio,