1 /* g4wihelp.c - NSIS Helper DLL used with gpg4win. -*- coding: latin-1; -*-
2 * Copyright (C) 2005 g10 Code GmbH
3 * Copyright (C) 2001 Justin Frankel
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any
7 * damages arising from the use of this software.
9 * Permission is granted to anyone to use this software for any
10 * purpose, including commercial applications, and to alter it and
11 * redistribute it freely, subject to the following restrictions:
13 * 1. The origin of this software must not be misrepresented; you must
14 * not claim that you wrote the original software. If you use this
15 * software in a product, an acknowledgment in the product
16 * documentation would be appreciated but is not required.
18 * 2. Altered source versions must be plainly marked as such, and must
19 * not be misrepresented as being the original software.
21 * 3. This notice may not be removed or altered from any source
23 ************************************************************
24 * The code for the splash screen has been taken from the Splash
25 * plugin of the NSIS 2.04 distribution. That code comes without
26 * explicit copyright notices in the source files or author names, it
27 * seems that it has been written by Justin Frankel; not sure about
28 * the year, though. [wk 2005-11-28]
30 * Fixed some compiler warnings. [wk 2014-02-24].
37 static HINSTANCE g_hInstance; /* Our Instance. */
38 static HWND g_hwndParent; /* Handle of parent window or NULL. */
39 static HBITMAP g_hbm; /* Handle of the splash image. */
40 static int sleepint; /* Milliseconds to show the spals image. */
43 /* Standard entry point for DLLs. */
45 DllMain (HANDLE hinst, DWORD reason, LPVOID reserved)
47 if (reason == DLL_PROCESS_ATTACH)
54 /* Dummy function for testing. */
55 void __declspec(dllexport)
56 dummy (HWND hwndParent, int string_size, char *variables,
57 stack_t **stacktop, extra_parameters_t *extra)
59 g_hwndParent = hwndParent;
63 // note if you want parameters from the stack, pop them off in order.
64 // i.e. if you are called via exdll::myFunction file.dat poop.dat
65 // calling popstring() the first time would give you file.dat,
66 // and the second time would give you poop.dat.
67 // you should empty the stack of your parameters, and ONLY your
73 snprintf (buf, sizeof buf, "$R0=%s\r\n$R1=%s\r\n",
74 getuservariable(INST_R0),
75 getuservariable(INST_R1));
76 MessageBox (g_hwndParent,buf,0,MB_OK);
78 snprintf (buf, sizeof buf,
80 "all_user_var =%d\r\n"
84 "reboot_called=%d\r\n"
86 "instdir_error=%d\r\n"
89 extra->exec_flags->autoclose,
90 extra->exec_flags->all_user_var,
91 extra->exec_flags->exec_error,
92 extra->exec_flags->abort,
93 extra->exec_flags->exec_reboot,
94 extra->exec_flags->reboot_called,
95 extra->exec_flags->silent,
96 extra->exec_flags->instdir_error,
97 extra->exec_flags->rtl,
98 extra->exec_flags->errlvl);
99 MessageBox(g_hwndParent,buf,0,MB_OK);
104 void __declspec(dllexport)
105 runonce (HWND hwndParent, int string_size, char *variables,
106 stack_t **stacktop, extra_parameters_t *extra)
110 g_hwndParent = hwndParent;
113 CreateMutexA (NULL, 0, getuservariable(INST_R0));
114 result = GetLastError ()? "1":"0";
115 setuservariable (INST_R0, result);
119 void __declspec(dllexport)
120 playsound (HWND hwndParent, int string_size, char *variables,
121 stack_t **stacktop, extra_parameters_t *extra)
123 char fname[MAX_PATH];
125 g_hwndParent = hwndParent;
128 if (popstring(fname, sizeof fname))
130 PlaySound (fname, NULL, SND_ASYNC|SND_FILENAME|SND_NODEFAULT);
134 void __declspec(dllexport)
135 stopsound (HWND hwndParent, int string_size, char *variables,
136 stack_t **stacktop, extra_parameters_t *extra)
138 g_hwndParent = hwndParent;
140 PlaySound (NULL, NULL, 0);
144 /* Windows procedure to control the splashimage. This one pauses the
145 execution until the sleep time is over or the user closes this
147 static LRESULT CALLBACK
148 splash_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
159 GetObject(g_hbm, sizeof(bm), (LPSTR)&bm);
160 SystemParametersInfo(SPI_GETWORKAREA, 0, &vp, 0);
161 SetWindowLong(hwnd,GWL_STYLE,0);
162 SetWindowPos(hwnd,NULL,
163 vp.left+(vp.right-vp.left-bm.bmWidth)/2,
164 vp.top+(vp.bottom-vp.top-bm.bmHeight)/2,
165 bm.bmWidth,bm.bmHeight,
167 ShowWindow(hwnd,SW_SHOW);
168 SetTimer(hwnd,1,sleepint,NULL);
176 HDC curdc=BeginPaint(hwnd,&ps);
177 HDC hdc=CreateCompatibleDC(curdc);
179 GetClientRect(hwnd,&r);
180 oldbm=(HBITMAP)SelectObject(hdc,g_hbm);
181 BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top,
183 SelectObject(hdc,oldbm);
197 result = DefWindowProc (hwnd, uMsg, wParam, lParam);
204 /* Display a splash screen. Call as
206 g4wihelp::showsplash SLEEP FNAME
208 With SLEEP being the time in milliseconds to show the splashscreen
209 and FNAME the complete filename of the image. As of now only BMP
212 void __declspec(dllexport)
213 showsplash (HWND hwndParent, int string_size, char *variables,
214 stack_t **stacktop, extra_parameters_t *extra)
218 char fname[MAX_PATH];
221 char classname[] = "_sp";
223 g_hwndParent = hwndParent;
225 if (popstring(sleepstr, sizeof sleepstr))
227 if (popstring(fname, sizeof fname))
233 return; /* Nothing to do. */
235 for (sleepint=0, p=sleepstr; *p >= '0' && *p <= '9'; p++)
238 sleepint += *p - '0';
241 return; /* Nothing to do. */
243 wc.lpfnWndProc = splash_wndproc;
244 wc.hInstance = g_hInstance;
245 wc.hCursor = LoadCursor(NULL,IDC_ARROW);
246 wc.lpszClassName = classname;
247 if (!RegisterClass(&wc))
250 g_hbm = LoadImage (NULL, fname, IMAGE_BITMAP,
251 0, 0 , LR_CREATEDIBSECTION|LR_LOADFROMFILE);
257 hwnd = CreateWindowEx (WS_EX_TOOLWINDOW, classname, classname,
258 0, 0, 0, 0, 0, (HWND)hwndParent, NULL,
261 while (IsWindow(hwnd) && GetMessage ( &msg, hwnd, 0, 0))
263 DispatchMessage (&msg);
266 DeleteObject (g_hbm);
269 UnregisterClass (classname, g_hInstance);
273 /* Service Management. */
275 /* Use this to report unexpected errors. FIXME: This is really not
278 service_error (const char *str)
281 snprintf (buf, sizeof (buf), "error: %s: ec=%d\r\n", str,
283 MessageBox(g_hwndParent, buf, 0, MB_OK);
285 setuservariable (INST_R0, "1");
289 void __declspec(dllexport)
290 service_create (HWND hwndParent, int string_size, char *variables,
291 stack_t **stacktop, extra_parameters_t *extra)
295 const char *result = NULL;
296 char service_name[256];
297 char display_name[256];
301 g_hwndParent = hwndParent;
304 /* The expected stack layout: service_name, display_name, program. */
305 if (popstring (service_name, sizeof (service_name)))
307 if (!err && popstring (display_name, sizeof (display_name)))
309 if (!err && popstring (program, sizeof (program)))
313 setuservariable (INST_R0, "1");
317 sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
320 service_error ("OpenSCManager");
324 service = CreateService (sc, service_name, display_name,
325 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
326 /* Use SERVICE_DEMAND_START for testing.
327 FIXME: Currently not configurable by caller. */
329 SERVICE_ERROR_NORMAL, program,
331 /* FIXME: Currently not configurable by caller. */
332 /* FIXME: LocalService or NetworkService
333 don't work for dirmngr right now. NOTE!
334 If you change it here, you also should
335 adjust make-msi.pl for the msi
336 installer. In the future, this should
337 be an argument to the function and then
338 the make-msi.pl script can extract it
339 from the invocation. */
340 NULL /* "NT AUTHORITY\\LocalService" */,
344 service_error ("CreateService");
345 CloseServiceHandle (sc);
348 CloseServiceHandle (service);
350 result = GetLastError () ? "1":"0";
351 setuservariable (INST_R0, result);
356 /* Requires g_hwndParent to be set! */
358 service_lookup (char *service_name)
363 sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
366 service_error ("OpenSCManager");
369 service = OpenService (sc, service_name, SC_MANAGER_ALL_ACCESS);
372 /* Fail silently here. */
373 CloseServiceHandle (sc);
376 CloseServiceHandle (sc);
381 /* Returns status. */
382 void __declspec(dllexport)
383 service_query (HWND hwndParent, int string_size, char *variables,
384 stack_t **stacktop, extra_parameters_t *extra)
387 const char *result = NULL;
388 char service_name[256];
390 SERVICE_STATUS status;
392 g_hwndParent = hwndParent;
395 /* The expected stack layout: service_name argc [argv]. */
396 if (popstring (service_name, sizeof (service_name)))
400 setuservariable (INST_R0, "ERROR");
404 service = service_lookup (service_name);
408 setuservariable (INST_R0, "MISSING");
412 err = QueryServiceStatus (service, &status);
415 setuservariable (INST_R0, "ERROR");
416 CloseServiceHandle (service);
419 CloseServiceHandle (service);
421 switch (status.dwCurrentState)
423 case SERVICE_START_PENDING:
424 result = "START_PENDING";
426 case SERVICE_RUNNING:
429 case SERVICE_PAUSE_PENDING:
430 result = "PAUSE_PENDING";
435 case SERVICE_CONTINUE_PENDING:
436 result = "CONTINUE_PENDING";
438 case SERVICE_STOP_PENDING:
439 result = "STOP_PENDING";
441 case SERVICE_STOPPED:
447 setuservariable (INST_R0, result);
452 void __declspec(dllexport)
453 service_start (HWND hwndParent, int string_size, char *variables,
454 stack_t **stacktop, extra_parameters_t *extra)
457 const char *result = NULL;
458 char service_name[256];
462 char argv_str[NR_ARGS][ARG_MAX];
463 const char *argv[NR_ARGS + 1];
468 g_hwndParent = hwndParent;
471 /* The expected stack layout: service_name argc [argv]. */
472 if (popstring (service_name, sizeof (service_name)))
474 if (!err && popstring (argc_str, sizeof (argc_str)))
478 argc = atoi (argc_str);
479 for (i = 0; i < argc; i++)
481 if (popstring (argv_str[i], ARG_MAX))
486 argv[i] = argv_str[i];
492 setuservariable (INST_R0, "1");
496 service = service_lookup (service_name);
500 err = StartService (service, argc, argc == 0 ? NULL : argv);
503 service_error ("StartService");
504 CloseServiceHandle (service);
507 CloseServiceHandle (service);
509 setuservariable (INST_R0, "0");
514 void __declspec(dllexport)
515 service_stop (HWND hwndParent, int string_size, char *variables,
516 stack_t **stacktop, extra_parameters_t *extra)
519 const char *result = NULL;
520 char service_name[256];
522 SERVICE_STATUS status;
523 DWORD timeout = 10000; /* 10 seconds. */
526 g_hwndParent = hwndParent;
529 /* The expected stack layout: service_name argc [argv]. */
530 if (popstring (service_name, sizeof (service_name)))
534 setuservariable (INST_R0, "1");
538 service = service_lookup (service_name);
542 err = QueryServiceStatus (service, &status);
545 service_error ("QueryService");
546 CloseServiceHandle (service);
550 if (status.dwCurrentState != SERVICE_STOPPED
551 && status.dwCurrentState != SERVICE_STOP_PENDING)
553 err = ControlService (service, SERVICE_CONTROL_STOP, &status);
556 service_error ("ControlService");
557 CloseServiceHandle (service);
562 start_time = GetTickCount ();
563 while (status.dwCurrentState != SERVICE_STOPPED)
565 Sleep (1000); /* One second. */
566 if (!QueryServiceStatus (service, &status))
568 service_error ("QueryService");
569 CloseServiceHandle (service);
572 if (status.dwCurrentState == SERVICE_STOPPED)
575 if (GetTickCount () - start_time > timeout)
578 snprintf (buf, sizeof (buf),
579 "time out waiting for service %s to stop\r\n",
581 MessageBox (g_hwndParent, buf, 0, MB_OK);
582 setuservariable (INST_R0, "1");
586 CloseServiceHandle (service);
587 setuservariable (INST_R0, "0");
592 void __declspec(dllexport)
593 service_delete (HWND hwndParent, int string_size, char *variables,
594 stack_t **stacktop, extra_parameters_t *extra)
597 const char *result = NULL;
598 char service_name[256];
601 g_hwndParent = hwndParent;
604 /* The expected stack layout: service_name argc [argv]. */
605 if (popstring (service_name, sizeof (service_name)))
609 setuservariable (INST_R0, "1");
613 service = service_lookup (service_name);
617 err = DeleteService (service);
620 service_error ("DeleteService");
621 CloseServiceHandle (service);
624 CloseServiceHandle (service);
626 setuservariable (INST_R0, "0");
633 /* Extract config file parameters. FIXME: Not particularly robust.
634 We expect some reasonable formatting. The parser below is very
635 limited. It expects a command line option /c=FILE or /C=FILE,
636 where FILE must be enclosed in double-quotes if it contains spaces.
637 That file should contain a single section [gpg4win] and KEY=VALUE
638 pairs for each additional configuration file to install. Comments
639 are supported only on lines by themselves. VALUE can be quoted in
640 double-quotes, but does not need to be, unless it has whitespace at
641 the beginning or end. KEY can, for example, be "gpg.conf" (without
644 config_init (char **keys, char **values, int max)
646 /* First, parse the command line. */
658 cmdline = getuservariable (INST_CMDLINE);
660 mark = (*cmdline == '"') ? (cmdline++, '"') : ' ';
661 while (*cmdline && *cmdline != mark)
663 if (mark == '"' && *cmdline)
665 while (*cmdline && *cmdline == ' ')
670 /* We are at the beginning of a new argument. */
671 if (cmdline[0] == '/' && (cmdline[1] == 'C' || cmdline[1] == 'c')
672 && cmdline[2] == '=')
678 while (*cmdline && *cmdline != ' ')
680 /* Skip over quoted parts. */
684 while (*cmdline && *cmdline != '"')
697 while (*cmdline && *cmdline == ' ')
701 if (!begin || begin > end)
705 if (*begin == '"' && *end == '"')
713 fname = malloc (end - begin + 2);
719 *(ptr++) = *(begin++);
722 conf = fopen (fname, "r");
732 if (fgets (line, sizeof (line), conf) == NULL)
734 ptr = &line[strlen (line)];
735 while (ptr > line && (ptr[-1] == '\n' || ptr[-1] == '\r'
736 || ptr[-1] == ' ' || ptr[-1] == '\t'))
741 while (*ptr && (*ptr == ' ' || *ptr == '\t'))
743 /* Ignore comment lines. */
744 /* FIXME: Ignore section markers. */
745 if (*ptr == '\0' || *ptr == ';' || *ptr == '[')
748 while (*ptr && *ptr != '=' && *ptr != ' ' && *ptr != '\t')
751 while (*ptr && (*ptr == ' ' || *ptr == '\t'))
760 /* We found a key. */
761 *keys = malloc (end - begin + 2);
766 *(ptr2++) = *(begin++);
771 while (*ptr && (*ptr == ' ' || *ptr == '\t'))
774 /* In this case, end points to the byte after the value, which
775 is OK because that is '\0'. */
776 end = &line[strlen (line)];
781 if (*begin == '"' && end[-1] == '"')
790 *values = malloc (end - begin + 1);
793 *(ptr2++) = *(begin++);
807 config_lookup (char *key)
810 static int initialised = 0;
811 static char *keys[MAX_KEYS];
812 static char *values[MAX_KEYS];
815 if (initialised == 0)
818 config_init (keys, values, MAX_KEYS);
821 MessageBox(g_hwndParent, "Configuration File:", 0, MB_OK);
826 sprintf (buf, "%s=%s\r\n", keys[i], values[i]);
827 MessageBox (g_hwndParent, buf, 0, MB_OK);
836 if (!strcmp (keys[i], key))
845 void __declspec(dllexport)
846 config_fetch (HWND hwndParent, int string_size, char *variables,
847 stack_t **stacktop, extra_parameters_t *extra)
853 g_hwndParent = hwndParent;
856 /* The expected stack layout: key. */
857 if (popstring (key, sizeof (key)))
861 setuservariable (INST_R0, "");
865 value = config_lookup (key);
867 setuservariable (INST_R0, value == NULL ? "" : value);
872 void __declspec(dllexport)
873 config_fetch_bool (HWND hwndParent, int string_size, char *variables,
874 stack_t **stacktop, extra_parameters_t *extra)
881 g_hwndParent = hwndParent;
884 /* The expected stack layout: key. */
885 if (popstring (key, sizeof (key)))
889 setuservariable (INST_R0, "");
893 value = config_lookup (key);
894 if (value == NULL || *value == '\0')
896 setuservariable (INST_R0, "");
901 if (!strcasecmp (value, "true")
902 || !strcasecmp (value, "yes")
903 || atoi (value) != 0)
906 setuservariable (INST_R0, result == 0 ? "0" : "1");
911 /* Return a string from the Win32 Registry or NULL in case of error.
912 Caller must release the return value. A NULL for root is an alias
913 for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
915 read_w32_registry_string (HKEY root, const char *dir, const char *name)
919 DWORD n1, nbytes, type;
924 root_key = HKEY_CURRENT_USER;
926 if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
929 return NULL; /* no need for a RegClose, so return direct */
930 /* It seems to be common practise to fall back to HKLM. */
931 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
932 return NULL; /* still no need for a RegClose, so return direct */
936 if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
939 /* Try to fallback to HKLM also vor a missing value. */
940 RegCloseKey (key_handle);
941 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
942 return NULL; /* Nope. */
943 if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
947 result = malloc( (n1=nbytes+1) );
951 if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
952 free(result); result = NULL;
955 result[nbytes] = 0; /* make sure it is really a string */
958 RegCloseKey( key_handle );
963 #define ENV_HK HKEY_LOCAL_MACHINE
964 #define ENV_REG "SYSTEM\\CurrentControlSet\\Control\\" \
965 "Session Manager\\Environment"
966 /* The following setting can be used for a per-user setting. */
968 #define ENV_HK HKEY_CURRENT_USER
969 #define ENV_REG "Environment"
971 /* Due to a bug in Windows7 (kb 2685893) we better put a lower limit
972 than 8191 on the maximum length of the PATH variable. Note, that
973 depending on the used toolchain we used to had a 259 byte limit in
975 #define PATH_LENGTH_LIMIT 2047
977 void __declspec(dllexport)
978 path_add (HWND hwndParent, int string_size, char *variables,
979 stack_t **stacktop, extra_parameters_t *extra)
981 char dir[PATH_LENGTH_LIMIT];
986 const char delims[] = ";";
989 g_hwndParent = hwndParent;
992 setuservariable (INST_R0, "0");
994 /* MessageBox (g_hwndParent, "XXX 1", 0, MB_OK); */
996 /* The expected stack layout: path component. */
997 if (popstring (dir, sizeof (dir)))
1000 /* MessageBox (g_hwndParent, "XXX 2", 0, MB_OK); */
1002 path = read_w32_registry_string (ENV_HK, ENV_REG, "Path");
1005 MessageBox (g_hwndParent, "No PATH variable found", 0, MB_OK);
1009 /* MessageBox (g_hwndParent, "XXX 3", 0, MB_OK); */
1011 /* Old path plus semicolon plus dir plus terminating nul. */
1012 path_new_size = strlen (path) + 1 + strlen (dir) + 1;
1013 if (path_new_size > PATH_LENGTH_LIMIT)
1015 MessageBox (g_hwndParent, "PATH env variable too big", 0, MB_OK);
1020 /* MessageBox (g_hwndParent, "XXX 4", 0, MB_OK); */
1022 path_new = malloc (path_new_size);
1029 /* MessageBox (g_hwndParent, "XXX 5", 0, MB_OK); */
1031 strcpy (path_new, path);
1032 strcat (path_new, ";");
1033 strcat (path_new, dir);
1035 /* MessageBox (g_hwndParent, "XXX 6", 0, MB_OK); */
1036 /* MessageBox (g_hwndParent, dir, 0, MB_OK); */
1037 /* MessageBox (g_hwndParent, "XXX 7", 0, MB_OK); */
1039 /* Check if the directory already exists in the path. */
1040 comp = strtok (path, delims);
1043 /* MessageBox (g_hwndParent, comp, 0, MB_OK); */
1045 if (!strcmp (comp, dir))
1051 comp = strtok (NULL, delims);
1056 /* MessageBox (g_hwndParent, "XXX 8", 0, MB_OK); */
1058 /* Set a key for our CLSID. */
1059 RegCreateKey (ENV_HK, ENV_REG, &key_handle);
1060 RegSetValueEx (key_handle, "Path", 0, REG_EXPAND_SZ,
1061 path_new, path_new_size);
1062 RegCloseKey (key_handle);
1063 SetEnvironmentVariable("PATH", path_new);
1066 /* MessageBox (g_hwndParent, "XXX 9", 0, MB_OK); */
1068 setuservariable (INST_R0, "1");
1072 void __declspec(dllexport)
1073 path_remove (HWND hwndParent, int string_size, char *variables,
1074 stack_t **stacktop, extra_parameters_t *extra)
1076 char dir[PATH_LENGTH_LIMIT];
1081 const char delims[] = ";";
1082 HKEY key_handle = 0;
1086 g_hwndParent = hwndParent;
1089 setuservariable (INST_R0, "0");
1091 /* The expected stack layout: path component. */
1092 if (popstring (dir, sizeof (dir)))
1095 path = read_w32_registry_string (ENV_HK, ENV_REG, "Path");
1096 /* Old path plus semicolon plus dir plus terminating nul. */
1097 path_new_size = strlen (path) + 1;
1098 path_new = malloc (path_new_size);
1106 /* Compose the new path. */
1107 comp = strtok (path, delims);
1110 if (strcmp (comp, dir))
1113 strcat (path_new, ";");
1114 strcat (path_new, comp);
1120 comp = strtok (NULL, delims);
1128 /* Set a key for our CLSID. */
1129 RegCreateKey (ENV_HK, ENV_REG, &key_handle);
1130 RegSetValueEx (key_handle, "Path", 0, REG_EXPAND_SZ,
1131 path_new, path_new_size);
1132 RegCloseKey (key_handle);
1135 setuservariable (INST_R0, "1");