diff --git a/src/exdll.c b/src/exdll.c index 1f1839d7..7815523a 100644 --- a/src/exdll.c +++ b/src/exdll.c @@ -1,286 +1,286 @@ #include "exdll.h" unsigned int g_stringsize; stack_t **g_stacktop; LPTSTR g_variables; // utility functions (not required but often useful) int NSISCALL popstring(LPTSTR str) { stack_t *th; if (!g_stacktop || !*g_stacktop) return 1; th=(*g_stacktop); if (str) lstrcpy(str,th->text); *g_stacktop = th->next; GlobalFree((HGLOBAL)th); return 0; } int NSISCALL popstringn(LPTSTR str, int maxlen) { stack_t *th; if (!g_stacktop || !*g_stacktop) return 1; th=(*g_stacktop); if (str) lstrcpyn(str,th->text,maxlen?maxlen:g_stringsize); *g_stacktop = th->next; GlobalFree((HGLOBAL)th); return 0; } void NSISCALL pushstring(LPCTSTR str) { stack_t *th; if (!g_stacktop) return; th=(stack_t*)GlobalAlloc(GPTR,(sizeof(stack_t)+(g_stringsize)*sizeof(*str))); lstrcpyn(th->text,str,g_stringsize); th->next=*g_stacktop; *g_stacktop=th; } LPTSTR NSISCALL getuservariable(const int varnum) { if (!isvalidnsisvarindex(varnum)) return NULL; - return (LPWSTR)((char*)g_variables+varnum*g_stringsize); + return (LPWSTR)((wchar_t*)g_variables+varnum*g_stringsize); } void NSISCALL setuservariable(const int varnum, LPCTSTR var) { if (var && isvalidnsisvarindex(varnum)) - lstrcpy((LPWSTR)((char*)g_variables + varnum*g_stringsize), var); + lstrcpy((LPWSTR)((wchar_t*)g_variables + varnum*g_stringsize), var); } #ifdef UNICODE int NSISCALL PopStringA(LPSTR ansiStr) { LPWSTR wideStr = (LPWSTR) GlobalAlloc(GPTR, g_stringsize*sizeof(WCHAR)); int rval = popstring(wideStr); WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); GlobalFree((HGLOBAL)wideStr); return rval; } int NSISCALL PopStringNA(LPSTR ansiStr, int maxlen) { int realLen = maxlen ? maxlen : g_stringsize; LPWSTR wideStr = (LPWSTR) GlobalAlloc(GPTR, realLen*sizeof(WCHAR)); int rval = popstringn(wideStr, realLen); WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, realLen, NULL, NULL); GlobalFree((HGLOBAL)wideStr); return rval; } void NSISCALL PushStringA(LPCSTR ansiStr) { LPWSTR wideStr = (LPWSTR) GlobalAlloc(GPTR, g_stringsize*sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); pushstring(wideStr); GlobalFree((HGLOBAL)wideStr); return; } void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr) { lstrcpyW(wideStr, getuservariable(varnum)); } void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr) { LPWSTR wideStr = getuservariable(varnum); WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); } void NSISCALL SetUserVariableA(const int varnum, LPCSTR ansiStr) { if (ansiStr && isvalidnsisvarindex(varnum)) { LPWSTR wideStr = (LPWSTR)((char*)g_variables + varnum * g_stringsize); MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); } } #else // ANSI defs int NSISCALL PopStringW(LPWSTR wideStr) { LPSTR ansiStr = (LPSTR) GlobalAlloc(GPTR, g_stringsize); int rval = popstring(ansiStr); MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); GlobalFree((HGLOBAL)ansiStr); return rval; } int NSISCALL PopStringNW(LPWSTR wideStr, int maxlen) { int realLen = maxlen ? maxlen : g_stringsize; LPSTR ansiStr = (LPSTR) GlobalAlloc(GPTR, realLen); int rval = popstringn(ansiStr, realLen); MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, realLen); GlobalFree((HGLOBAL)ansiStr); return rval; } void NSISCALL PushStringW(LPWSTR wideStr) { LPSTR ansiStr = (LPSTR) GlobalAlloc(GPTR, g_stringsize); WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); pushstring(ansiStr); GlobalFree((HGLOBAL)ansiStr); } void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr) { LPSTR ansiStr = getuservariable(varnum); MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize); } void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr) { lstrcpyA(ansiStr, getuservariable(varnum)); } void NSISCALL SetUserVariableW(const int varnum, LPCWSTR wideStr) { if (wideStr && isvalidnsisvarindex(varnum)) { LPSTR ansiStr = (char*)g_variables + varnum * g_stringsize; WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL); } } #endif // playing with integers INT_PTR NSISCALL nsishelper_str_to_ptr(LPCTSTR s) { INT_PTR v=0; if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) { s++; for (;;) { int c=*(++s); if (c >= _T('0') && c <= _T('9')) c-=_T('0'); else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10; else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10; else break; v<<=4; v+=c; } } else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) { for (;;) { int c=*(++s); if (c >= _T('0') && c <= _T('7')) c-=_T('0'); else break; v<<=3; v+=c; } } else { int sign=0; if (*s == _T('-')) sign++; else s--; for (;;) { int c=*(++s) - _T('0'); if (c < 0 || c > 9) break; v*=10; v+=c; } if (sign) v = -v; } return v; } unsigned int NSISCALL myatou(LPCTSTR s) { unsigned int v=0; for (;;) { unsigned int c=*s++; if (c >= _T('0') && c <= _T('9')) c-=_T('0'); else break; v*=10; v+=c; } return v; } int NSISCALL myatoi_or(LPCTSTR s) { int v=0; if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X'))) { s++; for (;;) { int c=*(++s); if (c >= _T('0') && c <= _T('9')) c-=_T('0'); else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10; else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10; else break; v<<=4; v+=c; } } else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0')) { for (;;) { int c=*(++s); if (c >= _T('0') && c <= _T('7')) c-=_T('0'); else break; v<<=3; v+=c; } } else { int sign=0; if (*s == _T('-')) sign++; else s--; for (;;) { int c=*(++s) - _T('0'); if (c < 0 || c > 9) break; v*=10; v+=c; } if (sign) v = -v; } // Support for simple ORed expressions if (*s == _T('|')) { v |= myatoi_or(s+1); } return v; } INT_PTR NSISCALL popintptr() { TCHAR buf[128]; if (popstringn(buf,COUNTOF(buf))) return 0; return nsishelper_str_to_ptr(buf); } int NSISCALL popint_or() { TCHAR buf[128]; if (popstringn(buf,COUNTOF(buf))) return 0; return myatoi_or(buf); } void NSISCALL pushintptr(INT_PTR value) { TCHAR buffer[30]; wsprintf(buffer, sizeof(void*) > 4 ? _T("%Id") : _T("%d"), value); pushstring(buffer); } diff --git a/src/g4wihelp.c b/src/g4wihelp.c index b83ab4a2..bae4b837 100644 --- a/src/g4wihelp.c +++ b/src/g4wihelp.c @@ -1,1284 +1,1323 @@ -/* g4wihelp.c - NSIS Helper DLL used with gpg4win. -*- coding: latin-1; -*- +/* g4wihelp.c - NSIS Helper DLL used with gpg4win. * Copyright (C) 2005, 2023 g10 Code GmbH * Copyright (C) 2001 Justin Frankel * Copyright (C) 2016, 2017 Intevation GmbH * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any * damages arising from the use of this software. * * Permission is granted to anyone to use this software for any * purpose, including commercial applications, and to alter it and * redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. ************************************************************ * The code for the splash screen has been taken from the Splash * plugin of the NSIS 2.04 distribution. That code comes without * explicit copyright notices in tyhe source files or author names, it * seems that it has been written by Justin Frankel; not sure about * the year, though. [wk 2005-11-28] * * Fixed some compiler warnings. [wk 2014-02-24]. * Merged code from GnuPG version. [wk 2023-04-24]. * * Compile time macros: * ENABLE_SLIDE_SHOW :: Define for Gpg4win. */ #include #include #include #include #include #include #include "exdll.h" /* We keep some code here for documentation reasons. That code has not * yet been converted to the Unicode NSIS plugin API. */ /* #define ENABLE_SOUND_GADGET 1 */ /* #define ENABLE_SPLASH_GADGET 1 */ /* #define ENABLE_SERVICE_MANAGEMENT 1 */ static HINSTANCE g_hInstance; /* Our Instance. */ static HWND g_hwndParent; /* Handle of parent window or NULL. */ static HBITMAP g_hbm; /* Handle of the splash image. */ static int sleepint; /* Milliseconds to show the spals image. */ #ifdef ENABLE_SLIDE_SHOW void slide_stop(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop); #endif /* Standard entry point for DLLs. */ int WINAPI DllMain (HANDLE hinst, DWORD reason, LPVOID reserved) { if (reason == DLL_PROCESS_ATTACH) g_hInstance = hinst; else if (reason == DLL_PROCESS_DETACH) { #ifdef ENABLE_SLIDE_SHOW slide_stop (NULL, 0, NULL, NULL); #endif } return TRUE; } /* Dummy function for testing. */ void __declspec(dllexport) dummy (HWND hwndParent, int string_size, LPTSTR variables, stack_t **stacktop, extra_parameters_t *extra) { g_hwndParent = hwndParent; EXDLL_INIT(); // note if you want parameters from the stack, pop them off in order. // i.e. if you are called via exdll::myFunction file.dat poop.dat // calling popstring() the first time would give you file.dat, // and the second time would give you poop.dat. // you should empty the stack of your parameters, and ONLY your // parameters. - // do your stuff here + /* Let's dump the variables. */ + { + char line[512]; + char *p; + const unsigned char *s = (void*)g_variables; + int i,j; + + for (i=0; i < string_size* __INST_LAST; i+=32, s += 32) + { + for (j=0; j < 32; j++) + if (s[j]) + break; + if (j != 32) + { + p = line; + *p = 0; + snprintf (p, 10, "%05x: ", i); + p += strlen (p); + for (j=0; j < 32; j++) + { + snprintf (p, 10, "%02x", s[j]); + p += strlen (p); + } + strcat (p, " |"); + p += strlen (p); + for (j=0; j < 32; j++) + { + if (s[j] >= 32 && s[j] < 127) + *p = s[j]; + else + *p = '.'; + p++; + } + strcat (p, "|"); + OutputDebugStringA (line); + } + } + } + + { wchar_t buf[1024]; swprintf(buf, 1024, L"stringsize=%d\r\n$0=%s\r\n$1=%s\r\n$R0=%s\r\n$R1=%s\r\n", string_size, getuservariable(INST_0), getuservariable(INST_1), getuservariable(INST_R0), getuservariable(INST_R1)); MessageBoxW(g_hwndParent,buf,0,MB_OK); swprintf (buf, 1024, L"autoclose =%d\r\n" "all_user_var =%d\r\n" "exec_error =%d\r\n" "abort =%d\r\n" "exec_reboot =%d\r\n" "reboot_called=%d\r\n" "api_version =%d\r\n" "silent =%d\r\n" "instdir_error=%d\r\n" "rtl =%d\r\n" "errlvl =%d\r\n", extra->exec_flags->autoclose, extra->exec_flags->all_user_var, extra->exec_flags->exec_error, extra->exec_flags->abort, extra->exec_flags->exec_reboot, extra->exec_flags->reboot_called, extra->exec_flags->plugin_api_version, extra->exec_flags->silent, extra->exec_flags->instdir_error, extra->exec_flags->rtl, extra->exec_flags->errlvl); MessageBoxW(g_hwndParent,buf,0,MB_OK); } } void __declspec(dllexport) runonce (HWND hwndParent, int string_size, LPTSTR variables, stack_t **stacktop, extra_parameters_t *extra) { LPCWSTR result; g_hwndParent = hwndParent; EXDLL_INIT(); CreateMutexW (NULL, 0, getuservariable(INST_R0)); result = GetLastError ()? L"1" : L"0"; setuservariable (INST_R0, result); } #ifdef ENABLE_SOUND_GADGET void __declspec(dllexport) playsound (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { char fname[MAX_PATH]; g_hwndParent = hwndParent; EXDLL_INIT(); if (popstring(fname, sizeof fname)) return; PlaySound (fname, NULL, SND_ASYNC|SND_FILENAME|SND_NODEFAULT); } void __declspec(dllexport) stopsound (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { g_hwndParent = hwndParent; EXDLL_INIT(); PlaySound (NULL, NULL, 0); } #endif /*ENABLE_SOUND_GADGET*/ #ifdef ENABLE_SPLASH_GADGET /* Windows procedure to control the splashimage. This one pauses the execution until the sleep time is over or the user closes this windows. */ static LRESULT CALLBACK splash_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; switch (uMsg) { case WM_CREATE: { BITMAP bm; RECT vp; GetObject(g_hbm, sizeof(bm), (LPSTR)&bm); SystemParametersInfo(SPI_GETWORKAREA, 0, &vp, 0); SetWindowLong(hwnd,GWL_STYLE,0); SetWindowPos(hwnd,NULL, vp.left+(vp.right-vp.left-bm.bmWidth)/2, vp.top+(vp.bottom-vp.top-bm.bmHeight)/2, bm.bmWidth,bm.bmHeight, SWP_NOZORDER); ShowWindow(hwnd,SW_SHOW); SetTimer(hwnd,1,sleepint,NULL); } break; case WM_PAINT: { PAINTSTRUCT ps; RECT r; HDC curdc=BeginPaint(hwnd,&ps); HDC hdc=CreateCompatibleDC(curdc); HBITMAP oldbm; GetClientRect(hwnd,&r); oldbm=(HBITMAP)SelectObject(hdc,g_hbm); BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top, hdc,0,0,SRCCOPY); SelectObject(hdc,oldbm); DeleteDC(hdc); EndPaint(hwnd,&ps); } break; case WM_CLOSE: break; case WM_TIMER: case WM_LBUTTONDOWN: DestroyWindow(hwnd); /*(fall through)*/ default: result = DefWindowProc (hwnd, uMsg, wParam, lParam); } return result; } /* Display a splash screen. Call as g4wihelp::showsplash SLEEP FNAME With SLEEP being the time in milliseconds to show the splashscreen and FNAME the complete filename of the image. As of now only BMP is supported. */ void __declspec(dllexport) showsplash (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { static WNDCLASS wc; char sleepstr[30]; char fname[MAX_PATH]; int err = 0; char *p; char classname[] = "_sp"; g_hwndParent = hwndParent; EXDLL_INIT(); if (popstring(sleepstr, sizeof sleepstr)) err = 1; if (popstring(fname, sizeof fname)) err = 1; if (err) return; if (!*fname) return; /* Nothing to do. */ for (sleepint=0, p=sleepstr; *p >= '0' && *p <= '9'; p++) { sleepint *= 10; sleepint += *p - '0'; } if (sleepint <= 0) return; /* Nothing to do. */ wc.lpfnWndProc = splash_wndproc; wc.hInstance = g_hInstance; wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.lpszClassName = classname; if (!RegisterClass(&wc)) return; /* Error. */ g_hbm = LoadImage (NULL, fname, IMAGE_BITMAP, 0, 0 , LR_CREATEDIBSECTION|LR_LOADFROMFILE); if (g_hbm) { MSG msg; HWND hwnd; hwnd = CreateWindowEx (WS_EX_TOOLWINDOW, classname, classname, 0, 0, 0, 0, 0, (HWND)hwndParent, NULL, g_hInstance, NULL); while (IsWindow(hwnd) && GetMessage ( &msg, hwnd, 0, 0)) { DispatchMessage (&msg); } DeleteObject (g_hbm); g_hbm = NULL; } UnregisterClass (classname, g_hInstance); } #endif /*ENABLE_SPLASH_GADGET*/ #ifdef ENABLE_SERVICE_MANAGEMENT /* Use this to report unexpected errors. FIXME: This is really not very descriptive. */ void service_error (const char *str) { char buf[1024]; snprintf (buf, sizeof (buf), "error: %s: ec=%d\r\n", str, GetLastError ()); MessageBox(g_hwndParent, buf, 0, MB_OK); setuservariable (INST_R0, "1"); } void __declspec(dllexport) service_create (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { SC_HANDLE sc; SC_HANDLE service; const char *result = NULL; char service_name[256]; char display_name[256]; char program[256]; int err = 0; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: service_name, display_name, program. */ if (popstring (service_name, sizeof (service_name))) err = 1; if (!err && popstring (display_name, sizeof (display_name))) err = 1; if (!err && popstring (program, sizeof (program))) err = 1; if (err) { setuservariable (INST_R0, "1"); return; } sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); if (sc == NULL) { service_error ("OpenSCManager"); return; } service = CreateService (sc, service_name, display_name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, /* Use SERVICE_DEMAND_START for testing. FIXME: Currently not configurable by caller. */ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, program, NULL, NULL, NULL, /* FIXME: Currently not configurable by caller. */ /* FIXME: LocalService or NetworkService don't work for dirmngr right now. NOTE! If you change it here, you also should adjust make-msi.pl for the msi installer. In the future, this should be an argument to the function and then the make-msi.pl script can extract it from the invocation. */ NULL /* "NT AUTHORITY\\LocalService" */, NULL); if (service == NULL) { service_error ("CreateService"); CloseServiceHandle (sc); return; } CloseServiceHandle (service); result = GetLastError () ? "1":"0"; setuservariable (INST_R0, result); return; } /* Requires g_hwndParent to be set! */ SC_HANDLE service_lookup (char *service_name) { SC_HANDLE sc; SC_HANDLE service; sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); if (sc == NULL) { service_error ("OpenSCManager"); return NULL; } service = OpenService (sc, service_name, SC_MANAGER_ALL_ACCESS); if (service == NULL) { /* Fail silently here. */ CloseServiceHandle (sc); return NULL; } CloseServiceHandle (sc); return service; } /* Returns status. */ void __declspec(dllexport) service_query (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { SC_HANDLE service; const char *result = NULL; char service_name[256]; int err = 0; SERVICE_STATUS status; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: service_name argc [argv]. */ if (popstring (service_name, sizeof (service_name))) err = 1; if (err) { setuservariable (INST_R0, "ERROR"); return; } service = service_lookup (service_name); if (service == NULL) if (err == 0) { setuservariable (INST_R0, "MISSING"); return; } err = QueryServiceStatus (service, &status); if (err == 0) { setuservariable (INST_R0, "ERROR"); CloseServiceHandle (service); return; } CloseServiceHandle (service); switch (status.dwCurrentState) { case SERVICE_START_PENDING: result = "START_PENDING"; break; case SERVICE_RUNNING: result = "RUNNING"; break; case SERVICE_PAUSE_PENDING: result = "PAUSE_PENDING"; break; case SERVICE_PAUSED: result = "PAUSED"; break; case SERVICE_CONTINUE_PENDING: result = "CONTINUE_PENDING"; break; case SERVICE_STOP_PENDING: result = "STOP_PENDING"; break; case SERVICE_STOPPED: result = "STOPPED"; break; default: result = "UNKNOWN"; } setuservariable (INST_R0, result); return; } void __declspec(dllexport) service_start (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { SC_HANDLE service; const char *result = NULL; char service_name[256]; char argc_str[256]; #define NR_ARGS 10 #define ARG_MAX 256 char argv_str[NR_ARGS][ARG_MAX]; const char *argv[NR_ARGS + 1]; int argc; int i; int err = 0; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: service_name argc [argv]. */ if (popstring (service_name, sizeof (service_name))) err = 1; if (!err && popstring (argc_str, sizeof (argc_str))) err = 1; if (!err) { argc = atoi (argc_str); for (i = 0; i < argc; i++) { if (popstring (argv_str[i], ARG_MAX)) { err = 1; break; } argv[i] = argv_str[i]; } argv[i] = NULL; } if (err) { setuservariable (INST_R0, "1"); return; } service = service_lookup (service_name); if (service == NULL) return; err = StartService (service, argc, argc == 0 ? NULL : argv); if (err == 0) { service_error ("StartService"); CloseServiceHandle (service); return; } CloseServiceHandle (service); setuservariable (INST_R0, "0"); return; } void __declspec(dllexport) service_stop (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { SC_HANDLE service; const char *result = NULL; char service_name[256]; int err = 0; SERVICE_STATUS status; DWORD timeout = 10000; /* 10 seconds. */ DWORD start_time; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: service_name argc [argv]. */ if (popstring (service_name, sizeof (service_name))) err = 1; if (err) { setuservariable (INST_R0, "1"); return; } service = service_lookup (service_name); if (service == NULL) return; err = QueryServiceStatus (service, &status); if (err == 0) { service_error ("QueryService"); CloseServiceHandle (service); return; } if (status.dwCurrentState != SERVICE_STOPPED && status.dwCurrentState != SERVICE_STOP_PENDING) { err = ControlService (service, SERVICE_CONTROL_STOP, &status); if (err == 0) { service_error ("ControlService"); CloseServiceHandle (service); return; } } start_time = GetTickCount (); while (status.dwCurrentState != SERVICE_STOPPED) { Sleep (1000); /* One second. */ if (!QueryServiceStatus (service, &status)) { service_error ("QueryService"); CloseServiceHandle (service); return; } if (status.dwCurrentState == SERVICE_STOPPED) break; if (GetTickCount () - start_time > timeout) { char buf[1024]; snprintf (buf, sizeof (buf), "time out waiting for service %s to stop\r\n", service_name); MessageBox (g_hwndParent, buf, 0, MB_OK); setuservariable (INST_R0, "1"); return; } } CloseServiceHandle (service); setuservariable (INST_R0, "0"); return; } void __declspec(dllexport) service_delete (HWND hwndParent, int string_size, char *variables, stack_t **stacktop, extra_parameters_t *extra) { SC_HANDLE service; const char *result = NULL; char service_name[256]; int err = 0; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: service_name argc [argv]. */ if (popstring (service_name, sizeof (service_name))) err = 1; if (err) { setuservariable (INST_R0, "1"); return; } service = service_lookup (service_name); if (service == NULL) return; err = DeleteService (service); if (err == 0) { service_error ("DeleteService"); CloseServiceHandle (service); return; } CloseServiceHandle (service); setuservariable (INST_R0, "0"); return; } #endif /*ENABLE_SERVICE_MANAGEMENT*/ /* Extract config file parameters. FIXME: Not particularly robust. We expect some reasonable formatting. The parser below is very limited. It expects a command line option /c=FILE or /C=FILE, where FILE must be enclosed in double-quotes if it contains spaces. That file should contain a single section [gpg4win] and KEY=VALUE pairs for each additional configuration file to install. Comments are supported only on lines by themselves. VALUE can be quoted in double-quotes, but does not need to be, unless it has whitespace at the beginning or end. KEY can, for example, be "gpg.conf" (without the quotes). */ void config_init (char **keys, char **values, int max) { /* First, parse the command line. */ LPCWSTR wcmdline; char *cmdline; char *begin = NULL; char *end = NULL; char mark; char *fname; char *ptr; FILE *conf; *keys = NULL; *values = NULL; cmdline = malloc (4096); if (!cmdline) return; wcmdline = getuservariable (INST_CMDLINE); *cmdline = 0; WideCharToMultiByte(CP_ACP, 0, wcmdline, -1, cmdline, 4095, NULL, NULL); if (!*cmdline) return; mark = (*cmdline == '"') ? (cmdline++, '"') : ' '; while (*cmdline && *cmdline != mark) cmdline++; if (mark == '"' && *cmdline) cmdline++; while (*cmdline && *cmdline == ' ') cmdline++; while (*cmdline) { /* We are at the beginning of a new argument. */ if (cmdline[0] == '/' && (cmdline[1] == 'C' || cmdline[1] == 'c') && cmdline[2] == '=') { cmdline += 3; begin = cmdline; } while (*cmdline && *cmdline != ' ') { /* Skip over quoted parts. */ if (*cmdline == '"') { cmdline++; while (*cmdline && *cmdline != '"') cmdline++; if (*cmdline) cmdline++; } else cmdline++; } if (begin && !end) { end = cmdline - 1; break; } while (*cmdline && *cmdline == ' ') cmdline++; } if (!begin || begin > end) return; /* Strip quotes. */ if (*begin == '"' && *end == '"') { begin++; end--; } if (begin > end) return; fname = malloc (end - begin + 2); if (!fname) return; ptr = fname; while (begin <= end) *(ptr++) = *(begin++); *ptr = '\0'; conf = fopen (fname, "r"); free (fname); free (cmdline); if (!conf) return; while (max - 1 > 0) { char line[256]; char *ptr2; if (fgets (line, sizeof (line), conf) == NULL) break; ptr = &line[strlen (line)]; while (ptr > line && (ptr[-1] == '\n' || ptr[-1] == '\r' || ptr[-1] == ' ' || ptr[-1] == '\t')) ptr--; *ptr = '\0'; ptr = line; while (*ptr && (*ptr == ' ' || *ptr == '\t')) ptr++; /* Ignore comment lines. */ /* FIXME: Ignore section markers. */ if (*ptr == '\0' || *ptr == ';' || *ptr == '[') continue; begin = ptr; while (*ptr && *ptr != '=' && *ptr != ' ' && *ptr != '\t') ptr++; end = ptr - 1; while (*ptr && (*ptr == ' ' || *ptr == '\t')) ptr++; if (*ptr != '=') continue; ptr++; if (begin > end) continue; /* We found a key. */ *keys = malloc (end - begin + 2); if (!keys) return; ptr2 = *keys; while (begin <= end) *(ptr2++) = *(begin++); *ptr2 = '\0'; *values = NULL; while (*ptr && (*ptr == ' ' || *ptr == '\t')) ptr++; begin = ptr; /* In this case, end points to the byte after the value, which is OK because that is '\0'. */ end = &line[strlen (line)]; if (begin > end) begin = end; /* Strip quotes. */ if (*begin == '"' && end[-1] == '"') { begin++; end--; *end = '\0'; } if (begin > end) return; *values = malloc (end - begin + 1); ptr2 = *values; while (begin <= end) *(ptr2++) = *(begin++); keys++; values++; max--; } fclose (conf); *keys = NULL; *values = NULL; } char * config_lookup (char *key) { #define MAX_KEYS 128 static int initialised = 0; static char *keys[MAX_KEYS]; static char *values[MAX_KEYS]; int i; if (initialised == 0) { initialised = 1; config_init (keys, values, MAX_KEYS); #if 0 MessageBox(g_hwndParent, "Configuration File:", 0, MB_OK); i = 0; while (keys[i]) { char buf[256]; sprintf (buf, "%s=%s\r\n", keys[i], values[i]); MessageBox (g_hwndParent, buf, 0, MB_OK); i++; } #endif } i = 0; while (keys[i]) { if (!strcmp (keys[i], key)) return values[i]; i++; } return NULL; } void __declspec(dllexport) config_fetch (HWND hwndParent, int string_size, LPTSTR variables, stack_t **stacktop, extra_parameters_t *extra) { char key[256]; int err = 0; char *value; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: key. */ if (PopStringNA (key, sizeof (key))) err = 1; if (err) { setuservariable (INST_R0, L""); return; } value = config_lookup (key); SetUserVariableA (INST_R0, value == NULL ? "" : value); return; } void __declspec(dllexport) config_fetch_bool (HWND hwndParent, int string_size, LPTSTR variables, stack_t **stacktop, extra_parameters_t *extra) { char key[256]; int err = 0; char *value; int result; g_hwndParent = hwndParent; EXDLL_INIT(); /* The expected stack layout: key. */ if (PopStringNA (key, sizeof (key))) err = 1; if (err) { setuservariable (INST_R0, L""); return; } value = config_lookup (key); if (value == NULL || *value == '\0') { setuservariable (INST_R0, L""); return; } result = 0; if (!strcasecmp (value, "true") || !strcasecmp (value, "yes") || atoi (value) != 0) result = 1; SetUserVariableA (INST_R0, result == 0 ? "0" : "1"); return; } /* Return a string from the Win32 Registry or NULL in case of error. Caller must release the return value. A NULL for root is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ static wchar_t * read_w32_registry_string (HKEY root, const wchar_t *dir, const wchar_t *name) { HKEY root_key; HKEY key_handle; DWORD n1, nbytes, type; wchar_t *result = NULL; root_key = root; if (!root_key) root_key = HKEY_CURRENT_USER; if (RegOpenKeyExW (root_key, dir, 0, KEY_READ, &key_handle)) { if (root) return NULL; /* no need for a RegClose, so return direct */ /* It seems to be common practise to fall back to HKLM. */ if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* still no need for a RegClose, so return direct */ } nbytes = 1; if (RegQueryValueExW (key_handle, name, 0, NULL, NULL, &nbytes)) { if (root) goto leave; /* Try to fallback to HKLM also for a missing value. */ RegCloseKey (key_handle); if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* Nope. */ if (RegQueryValueExW (key_handle, name, 0, NULL, NULL, &nbytes)) goto leave; } result = calloc ((n1=nbytes+1), sizeof *result); if (!result) goto leave; if (RegQueryValueExW (key_handle, name, 0, &type, (unsigned char *)result, &n1)) { free (result); result = NULL; goto leave; } result[nbytes] = 0; /* Make sure it is really a string */ leave: RegCloseKey (key_handle); return result; } /* Registry keys for PATH for HKLM and HKCU. */ #define ENV_HK HKEY_LOCAL_MACHINE #define ENV_REG L"SYSTEM\\CurrentControlSet\\Control\\" \ "Session Manager\\Environment" #define ENV_HK_USER HKEY_CURRENT_USER #define ENV_REG_USER L"Environment" /* Due to a bug in Windows7 (kb 2685893) we better put a lower limit * than 8191 on the maximum length of the PATH variable. Note, that * depending on the used toolchain we used to had a 259 byte limit in * the past. * [wk 2023-04-24]: Can this be lifted now that we use the wchar_t API? */ #define PATH_LENGTH_LIMIT 2047 void __declspec(dllexport) path_add (HWND hwndParent, int string_size, LPTSTR variables, stack_t **stacktop, extra_parameters_t *extra) { wchar_t dir[PATH_LENGTH_LIMIT]; wchar_t is_user_install[2]; wchar_t *path; wchar_t *path_new; size_t path_new_size; wchar_t *comp; const wchar_t delims[] = L";"; int is_user; HKEY key_handle = 0; HKEY root_key; const wchar_t *env_reg; /* wchar_t *tokctx; Context var for wcstok - not yet needed. */ g_hwndParent = hwndParent; EXDLL_INIT(); setuservariable (INST_R0, L"0"); /* Default return value. */ /* The expected stack layout: path component. */ if (popstringn (dir, COUNTOF (dir))) return; dir[COUNTOF(dir)-1] = 0; /* The expected stack layout: HKEY component. */ if (popstringn (is_user_install, COUNTOF (is_user_install))) return; is_user_install[COUNTOF(is_user_install)-1] = 0; if (!wcscmp (is_user_install, L"1")) { root_key = ENV_HK_USER; env_reg = ENV_REG_USER; } else { root_key = ENV_HK; env_reg = ENV_REG; } path = read_w32_registry_string (root_key, env_reg, L"Path"); if (!path) { path = wcsdup (L""); } /* Old path plus semicolon plus dir plus terminating nul. */ path_new_size = wcslen (path) + 1 + wcslen (dir) + 1; if (path_new_size > PATH_LENGTH_LIMIT) { MessageBox (g_hwndParent, L"PATH env variable too big", 0, MB_OK); free (path); return; } path_new = calloc (path_new_size, sizeof *path_new); if (!path_new) { free (path); return; } wcscpy (path_new, path); wcscat (path_new, L";"); wcscat (path_new, dir); /* Check if the directory already exists in the path. */ comp = wcstok (path, delims/*, &tokctx*/); do { /* MessageBox (g_hwndParent, comp, 0, MB_OK); */ if (!comp) break; if (!wcscmp (comp, dir)) { free (path); free (path_new); return; } comp = wcstok (NULL, delims/*, &tokctx*/); } while (comp); free (path); /* Update the path key. */ RegCreateKeyW (root_key, env_reg, &key_handle); RegSetValueEx (key_handle, L"Path", 0, REG_EXPAND_SZ, (unsigned char*)path_new, wcslen (path_new) * sizeof *path_new); RegCloseKey (key_handle); SetEnvironmentVariableW(L"PATH", path_new); free (path_new); /* MessageBox (g_hwndParent, "XXX 9", 0, MB_OK); */ setuservariable (INST_R0, L"1"); /* success. */ } void __declspec(dllexport) path_remove (HWND hwndParent, int string_size, LPTSTR variables, stack_t **stacktop, extra_parameters_t *extra) { wchar_t dir[PATH_LENGTH_LIMIT]; wchar_t is_user_install[2]; wchar_t *path; wchar_t *path_new; size_t path_new_size; wchar_t *comp; const wchar_t delims[] = L";"; HKEY key_handle = 0; int changed = 0; int count = 0; HKEY root_key; const wchar_t *env_reg; /* wchar_t *tokctx; Context var for wcstok - not yet needed. */ g_hwndParent = hwndParent; EXDLL_INIT(); setuservariable (INST_R0, L"0"); /* The expected stack layout: path component. */ if (popstringn (dir, COUNTOF (dir))) return; dir[COUNTOF(dir)-1] = 0; /* The expected stack layout: HKEY component. */ if (popstringn (is_user_install, COUNTOF (is_user_install))) return; is_user_install[COUNTOF(is_user_install)-1] = 0; if (!wcscmp (is_user_install, L"1")) { root_key = ENV_HK_USER; env_reg = ENV_REG_USER; } else { root_key = ENV_HK; env_reg = ENV_REG; } path = read_w32_registry_string (root_key, env_reg, L"Path"); if (!path) return; /* Old path plus semicolon plus dir plus terminating nul. */ path_new_size = wcslen (path) + 1; path_new = calloc (path_new_size, sizeof *path_new); if (!path_new) { free (path); return; } /* Compose the new path. */ comp = wcstok (path, delims/*, &tokctx*/); do { if (wcscmp (comp, dir)) { if (count) wcscat (path_new, L";"); wcscat (path_new, comp); count++; } else changed = 1; } while ((comp = wcstok (NULL, delims/*, &tokctx*/))); free (path); if (!changed) { free (path_new); return; } /* Set a key for our CLSID. */ RegCreateKeyW (root_key, env_reg, &key_handle); RegSetValueEx (key_handle, L"Path", 0, REG_EXPAND_SZ, (unsigned char*)path_new, wcslen (path_new) * sizeof *path_new); RegCloseKey (key_handle); free (path_new); setuservariable (INST_R0, L"1"); /* success */ } /** @brief Kill processes with the name name. * * This function tries to kill a process using ExitProcess. * * If it does not work it does not work. No return values. * The intention is to make an effort to kill something during * installation / uninstallation. * * The function signature is explained by NSIS. */ void __declspec(dllexport) __cdecl KillProc(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { HANDLE h; PROCESSENTRY32 pe32; if (!stacktop || !*stacktop || !(*stacktop)->text) { ERRORPRINTF ("Invalid call to KillProc."); return; } h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); if (h == INVALID_HANDLE_VALUE) { ERRORPRINTF ("Failed to create Toolhelp snapshot"); return; } pe32.dwSize = sizeof (PROCESSENTRY32); if (!Process32First (h, &pe32)) { ERRORPRINTF ("Failed to get first process"); CloseHandle (h); return; } do { if (!wcscmp ((*stacktop)->text, pe32.szExeFile)) { HANDLE hProc = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); if (!hProc) { ERRORPRINTF ("Failed to open process handle."); continue; } if (!TerminateProcess (hProc, 1)) { ERRORPRINTF ("Failed to terminate process."); } CloseHandle (hProc); } } while (Process32Next (h, &pe32)); CloseHandle (h); } diff --git a/src/gpg4win-src.nsi b/src/gpg4win-src.nsi index ca41c1c9..b5069618 100644 --- a/src/gpg4win-src.nsi +++ b/src/gpg4win-src.nsi @@ -1,111 +1,113 @@ -# gpg4win-src.nsi - Installer for GPG4Win sources. -*- coding: latin-1; -*- +# gpg4win-src.nsi - Installer for GPG4Win sources. # Copyright (C) 2005, 2007 g10 Code GmbH # # This file is part of Gpg4win. # # Gpg4win is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # Gpg4win is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +Unicode true + !cd "${BUILD_DIR}" !addincludedir "${TOP_SRCDIR}" !addincludedir "${TOP_SRCDIR}/po" !addincludedir "${SRCDIR}" !addplugindir "${BUILD_DIR}" !include "config.nsi" # We need StrRep. !include "StrFunc.nsh" # The package name and version. PRETTY_PACKAGE is a user visible name # only while PACKAGE is useful for filenames etc. PROD_VERSION is the # product version and needs to be in the format "MAJ.MIN.MIC.BUILDNR". # NOTE: Please keep the capitalization of PRETTY_PACKAGE_SHORT as it is # used as registry key. !define PACKAGE "${_PACKAGE}-src" !define PRETTY_PACKAGE "Gpg4win Sources" !define PRETTY_PACKAGE_SHORT "GPG4WinSrc" !define VERSION "${_VERSION}" !define PROD_VERSION "${_BUILD_FILEVERSION}" !define COMPANY "g10 Code GmbH" !define COPYRIGHT "Copyright (C) 2007 g10 Code GmbH" !define DESCRIPTION "Gpg4win: The GNU Privacy Guard and tools for Windows (Source Files)" !define INSTALL_DIR "GnuPG\Source" !define WELCOME_TITLE_STR "$(T_WelcomeTitleGpg4winSrc)" !define ABOUT_STR "$(T_AboutGpg4winSrc) \ $\r$\n$\r$\n$_CLICK $\r$\n$\r$\n$\r$\n$\r$\n$\r$\n \ $(T_AboutGpg4winSrcVersion) $\r$\n \ $(T_AboutGpg4winSrcFileVersion) $\r$\n$\r$\n \ $(T_AboutGpg4winSrcReleaseDate)" # The copyright license of the package. Define only one of these. !define LICENSE_GPL # !define LICENSE_LGPL # Sections !include "Sections.nsh" # We use Memento to remember past installation choices. !include Memento.nsh !define MEMENTO_REGISTRY_ROOT SHCTX !define MEMENTO_REGISTRY_KEY \ Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRETTY_PACKAGE_SHORT} # We need to know wether we are installing to 64 bit. !include "x64.nsh" # The installation directory. !define ipdir "playground/install/pkgs" !define exipdir "playground/install-ex/pkgs" !define bpdir "playground/build" # For source packages we use the standard zip method because it takes # ages to compress a large archive of already packed sources. SetCompressor zlib # Now include all the sections. !define SOURCES !include "inst-sections.nsi" # Now include the generic parts. !include "installer.nsi" # The WelcomeTitle is displayed on the first page. LangString T_WelcomeTitleGpg4winSrc ${LANG_ENGLISH} \ "Welcome to the installation of the Gpg4win sources" # The About string as displayed on the first page. LangString T_AboutGpg4winSrc ${LANG_ENGLISH} \ "Gpg4win is an installer package for Windows for EMail and \ file encryption using the core component GnuPG for Windows. \ Both relevant cryptography standards are supported, OpenPGP \ and S/MIME. Gpg4win and the software included with Gpg4win \ are Free Software." LangString T_AboutGpg4winSrcVersion ${LANG_ENGLISH} \ "This is Gpg4win version ${VERSION}" LangString T_AboutGpg4winSrcFileVersion ${LANG_ENGLISH} \ "file version ${PROD_VERSION}" LangString T_AboutGpg4winSrcReleaseDate ${LANG_ENGLISH} \ "release date ${_BUILD_ISODATE}" # At long last, include all the translations. !include "../po/catalogs.nsi" # Now include the generic parts to end the installation. !include "installer-finish.nsi" diff --git a/src/gpg4win.nsi b/src/gpg4win.nsi index 281f7a42..ca27b4a4 100644 --- a/src/gpg4win.nsi +++ b/src/gpg4win.nsi @@ -1,134 +1,136 @@ -# gpg4win.nsi - Installer for GnuPG 4 Windows. -*- coding: latin-1; -*- +# gpg4win.nsi - Installer for GnuPG 4 Windows. # Copyright (C) 2005, 2007, 2008 g10 Code GmbH # # This file is part of GPG4Win. # # GPG4Win is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # GPG4Win is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +Unicode true + !cd "${BUILD_DIR}" !addincludedir "${TOP_SRCDIR}" !addincludedir "${TOP_SRCDIR}/po" !addincludedir "${SRCDIR}" !addplugindir "${BUILD_DIR}" !include "config.nsi" # Define this early automatically it is only defined after # MUI_LANGUAGE has been called. !define LANG_ENGLISH 1033 # The package name and version. PRETTY_PACKAGE is a user visible name # only while PACKAGE is useful for filenames etc. PROD_VERSION is the # product version and needs to be in the format "MAJ.MIN.MIC.BUILDNR". # NOTE: Please keep the capitalization of PRETTY_PACKAGE_SHORT as it is # used as registry key. !define PACKAGE "${_PACKAGE}" !define PRETTY_PACKAGE "Gpg4win" !define PRETTY_PACKAGE_SHORT "Gpg4win" !define VERSION "${_VERSION}" !define PROD_VERSION "${_BUILD_FILEVERSION}" !define COMPANY "Intevation GmbH" !define COPYRIGHT "Copyright (C) 2020 Intevation GmbH" !define DESCRIPTION "Gpg4win: The GNU Privacy Guard and Tools for Windows" !define INSTALL_DIR "Gpg4win" !define WELCOME_TITLE_STR "$(T_WelcomeTitleGpg4win)" !define ABOUT_STR "$(T_AboutGpg4win) \ $\r$\n$\r$\n$\r$\n$_CLICK $\r$\n$\r$\n$\r$\n$\r$\n$\r$\n\ $(T_AboutGpg4winVersion) $\r$\n$(T_AboutGpg4winReleaseDate)" # The copyright license of the package. Define only one of these. !define LICENSE_GPL # Select the best compression algorithm available. The dictionary # size is the default (8 MB). !ifndef DISABLE_LZMA !ifndef SOURCES SetCompressor /SOLID lzma # SetCompressorDictSize 8 !endif !endif # We support user mode installation but prefer system wide !define MULTIUSER_EXECUTIONLEVEL Highest !define MULTIUSER_MUI !define MULTIUSER_INSTALLMODE_COMMANDLINE !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "Software\${PRETTY_PACKAGE_SHORT}" !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "" !define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "Software\${PRETTY_PACKAGE_SHORT}" !define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME "Install Directory" !define MULTIUSER_INSTALLMODE_INSTDIR "${PRETTY_PACKAGE_SHORT}" !include "MultiUser.nsh" !ifdef DEBUG !undef DEBUG !endif # The installation directory. !define ipdir "playground/install/pkgs" !define exipdir "playground/install-ex/pkgs" !define bpdir "playground/build" # Variables VAR is_minimal VAR with_browser VAR is_update # Sections !include "Sections.nsh" # We use Memento to remember past installation choices. !include Memento.nsh !define MEMENTO_REGISTRY_ROOT SHCTX !define MEMENTO_REGISTRY_KEY \ Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRETTY_PACKAGE_SHORT} # We need to know wether we are installing to 64 bit. !include "x64.nsh" # Now include the sections. !define BINARIES !include "inst-sections.nsi" # Now include the generic parts. !define HAVE_STARTMENU !include "installer.nsi" # The WelcomeTitle is displayed on the first page. LangString T_WelcomeTitleGpg4win ${LANG_ENGLISH} \ "Welcome to the installation of Gpg4win" # The About string as displayed on the first page. LangString T_AboutGpg4win ${LANG_ENGLISH} \ "Gpg4win is an installer package for Windows for EMail and \ file encryption using the core component GnuPG for Windows. \ Both relevant cryptography standards are supported, OpenPGP \ and S/MIME. Gpg4win and the software included with Gpg4win \ is Free Software." LangString T_AboutGpg4winVersion ${LANG_ENGLISH} \ "This is Gpg4win version ${VERSION}" LangString T_AboutGpg4winFileVersion ${LANG_ENGLISH} \ "file version ${PROD_VERSION}" LangString T_AboutGpg4winReleaseDate ${LANG_ENGLISH} \ "Release date ${_BUILD_ISODATE}" # At long last, include all the translations. !include "../po/catalogs.nsi" # Now include the generic parts to end the installation. !include "installer-finish.nsi"