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 a6b29a005..000000000 Binary files a/resources/install/windows/setup.exe and /dev/null differ diff --git a/resources/install/windows/setup.exe.manifest b/resources/install/windows/setup.exe.manifest deleted file mode 100644 index 4d62b7bad..000000000 --- a/resources/install/windows/setup.exe.manifest +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ 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 584ea4cea..000000000 Binary files a/resources/install/windows/up2date.exe and /dev/null differ 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,