Ensure the slideshow is stopped on plugin detach
[gpg4win.git] / src / g4wihelp.c
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
4  * Copyright (C) 2016 Intevation GmbH
5  *
6  * This software is provided 'as-is', without any express or implied
7  * warranty. In no event will the authors be held liable for any
8  * damages arising from the use of this software.
9  *
10  * Permission is granted to anyone to use this software for any
11  * purpose, including commercial applications, and to alter it and
12  * redistribute it freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must
15  *    not claim that you wrote the original software. If you use this
16  *    software in a product, an acknowledgment in the product
17  *    documentation would be appreciated but is not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must
20  *    not be misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source
23  *    distribution.
24  ************************************************************
25  * The code for the splash screen has been taken from the Splash
26  * plugin of the NSIS 2.04 distribution.  That code comes without
27  * explicit copyright notices in tyhe source files or author names, it
28  * seems that it has been written by Justin Frankel; not sure about
29  * the year, though. [wk 2005-11-28]
30  */
31
32 #include <windows.h>
33 #include <stdio.h>
34 #include <tlhelp32.h>
35 #include <psapi.h>
36 #include "exdll.h"
37
38 static HINSTANCE g_hInstance; /* Our Instance. */
39 static HWND g_hwndParent;     /* Handle of parent window or NULL. */
40 static HBITMAP g_hbm;         /* Handle of the splash image. */
41 static int sleepint;          /* Milliseconds to show the spals image. */
42
43 void
44 slide_stop(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop);
45
46 /* Standard entry point for DLLs. */
47 int WINAPI
48 DllMain (HANDLE hinst, DWORD reason, LPVOID reserved)
49 {
50    if (reason == DLL_PROCESS_ATTACH)
51      g_hInstance = hinst;
52    else if (reason == DLL_PROCESS_DETACH)
53      slide_stop(NULL, 0, NULL, NULL);
54    return TRUE;
55 }
56
57
58
59 /* Dummy function for testing. */
60 void __declspec(dllexport)
61 dummy (HWND hwndParent, int string_size, char *variables,
62        stack_t **stacktop, extra_parameters_t *extra)
63 {
64   g_hwndParent = hwndParent;
65
66   EXDLL_INIT();
67
68   // note if you want parameters from the stack, pop them off in order.
69   // i.e. if you are called via exdll::myFunction file.dat poop.dat
70   // calling popstring() the first time would give you file.dat,
71   // and the second time would give you poop.dat.
72   // you should empty the stack of your parameters, and ONLY your
73   // parameters.
74
75   // do your stuff here
76   {
77     char buf[1024];
78     sprintf(buf,"$R0=%s\r\n$R1=%s\r\n",
79             getuservariable(INST_R0),
80             getuservariable(INST_R1));
81     MessageBox(g_hwndParent,buf,0,MB_OK);
82
83     sprintf (buf,
84              "autoclose    =%d\r\n"
85              "all_user_var =%d\r\n"
86              "exec_error   =%d\r\n"
87              "abort        =%d\r\n"
88              "exec_reboot  =%d\r\n"
89              "reboot_called=%d\r\n"
90              "silent       =%d\r\n"
91              "instdir_error=%d\r\n"
92              "rtl          =%d\r\n"
93              "errlvl       =%d\r\n",
94              extra->exec_flags->autoclose,
95              extra->exec_flags->all_user_var,
96              extra->exec_flags->exec_error,
97              extra->exec_flags->abort,
98              extra->exec_flags->exec_reboot,
99              extra->exec_flags->reboot_called,
100              extra->exec_flags->silent,
101              extra->exec_flags->instdir_error,
102              extra->exec_flags->rtl,
103              extra->exec_flags->errlvl);
104     MessageBox(g_hwndParent,buf,0,MB_OK);
105   }
106 }
107
108
109
110 void __declspec(dllexport)
111 runonce (HWND hwndParent, int string_size, char *variables,
112          stack_t **stacktop, extra_parameters_t *extra)
113 {
114   const char *result;
115
116   g_hwndParent = hwndParent;
117   EXDLL_INIT();
118
119   CreateMutexA (NULL, 0, getuservariable(INST_R0));
120   result = GetLastError ()? "1":"0";
121   setuservariable (INST_R0, result);
122 }
123
124
125 void __declspec(dllexport)
126 playsound (HWND hwndParent, int string_size, char *variables,
127            stack_t **stacktop, extra_parameters_t *extra)
128 {
129   char fname[MAX_PATH];
130
131   g_hwndParent = hwndParent;
132   EXDLL_INIT();
133
134   if (popstring(fname, sizeof fname))
135     return;
136   PlaySound (fname, NULL, SND_ASYNC|SND_FILENAME|SND_NODEFAULT);
137 }
138
139
140 void __declspec(dllexport)
141 stopsound (HWND hwndParent, int string_size, char *variables,
142            stack_t **stacktop, extra_parameters_t *extra)
143 {
144   g_hwndParent = hwndParent;
145   EXDLL_INIT();
146   PlaySound (NULL, NULL, 0);
147 }
148
149
150 /* Windows procedure to control the splashimage.  This one pauses the
151    execution until the sleep time is over or the user closes this
152    windows. */
153 static LRESULT CALLBACK
154 splash_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
155 {
156   LRESULT result = 0;
157
158   switch (uMsg)
159     {
160     case WM_CREATE:
161       {
162         BITMAP bm;
163         RECT vp;
164
165         GetObject(g_hbm, sizeof(bm), (LPSTR)&bm);
166         SystemParametersInfo(SPI_GETWORKAREA, 0, &vp, 0);
167         SetWindowLong(hwnd,GWL_STYLE,0);
168         SetWindowPos(hwnd,NULL,
169                      vp.left+(vp.right-vp.left-bm.bmWidth)/2,
170                      vp.top+(vp.bottom-vp.top-bm.bmHeight)/2,
171                      bm.bmWidth,bm.bmHeight,
172                      SWP_NOZORDER);
173         ShowWindow(hwnd,SW_SHOW);
174         SetTimer(hwnd,1,sleepint,NULL);
175       }
176       break;
177
178     case WM_PAINT:
179       {
180         PAINTSTRUCT ps;
181         RECT r;
182         HDC curdc=BeginPaint(hwnd,&ps);
183         HDC hdc=CreateCompatibleDC(curdc);
184         HBITMAP oldbm;
185         GetClientRect(hwnd,&r);
186         oldbm=(HBITMAP)SelectObject(hdc,g_hbm);
187         BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top,
188                hdc,0,0,SRCCOPY);
189         SelectObject(hdc,oldbm);
190         DeleteDC(hdc);
191         EndPaint(hwnd,&ps);
192       }
193       break;
194
195     case WM_CLOSE:
196       break;
197
198     case WM_TIMER:
199     case WM_LBUTTONDOWN:
200       DestroyWindow(hwnd);
201       /*(fall through)*/
202     default:
203       result =  DefWindowProc (hwnd, uMsg, wParam, lParam);
204     }
205
206   return result;
207 }
208
209
210 /* Display a splash screen.  Call as
211
212      g4wihelp::showsplash SLEEP FNAME
213
214    With SLEEP being the time in milliseconds to show the splashscreen
215    and FNAME the complete filename of the image.  As of now only BMP
216    is supported.
217 */
218 void __declspec(dllexport)
219 showsplash (HWND hwndParent, int string_size, char *variables,
220            stack_t **stacktop, extra_parameters_t *extra)
221 {
222   static WNDCLASS wc;
223   char sleepstr[30];
224   char fname[MAX_PATH];
225   int err = 0;
226   char *p;
227   char classname[] = "_sp";
228
229   g_hwndParent = hwndParent;
230   EXDLL_INIT();
231   if (popstring(sleepstr, sizeof sleepstr))
232     err = 1;
233   if (popstring(fname, sizeof fname))
234     err = 1;
235   if (err)
236     return;
237
238   if (!*fname)
239     return; /* Nothing to do. */
240
241   for (sleepint=0, p=sleepstr; *p >= '0' && *p <= '9'; p++)
242     {
243       sleepint *= 10;
244       sleepint += *p - '0';
245     }
246   if (sleepint <= 0)
247     return; /* Nothing to do. */
248
249   wc.lpfnWndProc = splash_wndproc;
250   wc.hInstance = g_hInstance;
251   wc.hCursor = LoadCursor(NULL,IDC_ARROW);
252   wc.lpszClassName = classname;
253   if (!RegisterClass(&wc))
254     return; /* Error. */
255
256   g_hbm = LoadImage (NULL, fname, IMAGE_BITMAP,
257                      0, 0 , LR_CREATEDIBSECTION|LR_LOADFROMFILE);
258   if (g_hbm)
259     {
260       MSG msg;
261       HWND hwnd;
262
263       hwnd = CreateWindowEx (WS_EX_TOOLWINDOW, classname, classname,
264                              0, 0, 0, 0, 0, (HWND)hwndParent, NULL,
265                              g_hInstance, NULL);
266
267       while (IsWindow(hwnd) && GetMessage ( &msg, hwnd, 0, 0))
268         {
269           DispatchMessage (&msg);
270         }
271
272       DeleteObject (g_hbm);
273       g_hbm = NULL;
274     }
275   UnregisterClass (classname, g_hInstance);
276 }
277
278 \f
279 /* Service Management.  */
280
281 /* Use this to report unexpected errors.  FIXME: This is really not
282    very descriptive.  */
283 void
284 service_error (const char *str)
285 {
286   char buf[1024];
287   snprintf (buf, sizeof (buf) - 1, "error: %s: ec=%d\r\n", str,
288             GetLastError ());
289   MessageBox(g_hwndParent, buf, 0, MB_OK);
290
291   setuservariable (INST_R0, "1");
292 }
293
294
295 void __declspec(dllexport)
296 service_create (HWND hwndParent, int string_size, char *variables,
297                  stack_t **stacktop, extra_parameters_t *extra)
298 {
299   SC_HANDLE sc;
300   SC_HANDLE service;
301   const char *result = NULL;
302   char service_name[256];
303   char display_name[256];
304   char program[256];
305   int err = 0;
306
307   g_hwndParent = hwndParent;
308   EXDLL_INIT();
309
310   /* The expected stack layout: service_name, display_name, program.  */
311   if (popstring (service_name, sizeof (service_name)))
312     err = 1;
313   if (!err && popstring (display_name, sizeof (display_name)))
314     err = 1;
315   if (!err && popstring (program, sizeof (program)))
316     err = 1;
317   if (err)
318     {
319       setuservariable (INST_R0, "1");
320       return;
321     }
322
323   sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
324   if (sc == NULL)
325     {
326       service_error ("OpenSCManager");
327       return;
328     }
329
330   service = CreateService (sc, service_name, display_name,
331                            SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
332                            /* Use SERVICE_DEMAND_START for testing.
333                               FIXME: Currently not configurable by caller.  */
334                            SERVICE_AUTO_START,
335                            SERVICE_ERROR_NORMAL, program,
336                            NULL, NULL, NULL,
337                            /* FIXME: Currently not configurable by caller.  */
338                            /* FIXME: LocalService or NetworkService
339                               don't work for dirmngr right now.  NOTE!
340                               If you change it here, you also should
341                               adjust make-msi.pl for the msi
342                               installer.  In the future, this should
343                               be an argument to the function and then
344                               the make-msi.pl script can extract it
345                               from the invocation.  */
346                            NULL /* "NT AUTHORITY\\LocalService" */,
347                            NULL);
348   if (service == NULL)
349     {
350       service_error ("CreateService");
351       CloseServiceHandle (sc);
352       return;
353     }
354   CloseServiceHandle (service);
355
356   result = GetLastError () ? "1":"0";
357   setuservariable (INST_R0, result);
358   return;
359 }
360
361
362 /* Requires g_hwndParent to be set!  */
363 SC_HANDLE
364 service_lookup (char *service_name)
365 {
366   SC_HANDLE sc;
367   SC_HANDLE service;
368
369   sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
370   if (sc == NULL)
371     {
372       service_error ("OpenSCManager");
373       return NULL;
374     }
375   service = OpenService (sc, service_name, SC_MANAGER_ALL_ACCESS);
376   if (service == NULL)
377     {
378       /* Fail silently here.  */
379       CloseServiceHandle (sc);
380       return NULL;
381     }
382   CloseServiceHandle (sc);
383   return service;
384 }
385
386
387 /* Returns status.  */
388 void __declspec(dllexport)
389 service_query (HWND hwndParent, int string_size, char *variables,
390                stack_t **stacktop, extra_parameters_t *extra)
391 {
392   SC_HANDLE service;
393   const char *result = NULL;
394   char service_name[256];
395   int err = 0;
396   SERVICE_STATUS status;
397
398   g_hwndParent = hwndParent;
399   EXDLL_INIT();
400
401   /* The expected stack layout: service_name argc [argv].  */
402   if (popstring (service_name, sizeof (service_name)))
403     err = 1;
404   if (err)
405     {
406       setuservariable (INST_R0, "ERROR");
407       return;
408     }
409
410   service = service_lookup (service_name);
411   if (service == NULL)
412   if (err == 0)
413     {
414       setuservariable (INST_R0, "MISSING");
415       return;
416     }
417
418   err = QueryServiceStatus (service, &status);
419   if (err == 0)
420     {
421       setuservariable (INST_R0, "ERROR");
422       CloseServiceHandle (service);
423       return;
424     }
425   CloseServiceHandle (service);
426
427   switch (status.dwCurrentState)
428     {
429     case SERVICE_START_PENDING:
430       result = "START_PENDING";
431       break;
432     case SERVICE_RUNNING:
433       result = "RUNNING";
434       break;
435     case SERVICE_PAUSE_PENDING:
436       result = "PAUSE_PENDING";
437       break;
438     case SERVICE_PAUSED:
439       result = "PAUSED";
440       break;
441     case SERVICE_CONTINUE_PENDING:
442       result = "CONTINUE_PENDING";
443       break;
444     case SERVICE_STOP_PENDING:
445       result = "STOP_PENDING";
446       break;
447     case SERVICE_STOPPED:
448       result = "STOPPED";
449       break;
450     default:
451       result = "UNKNOWN";
452     }
453   setuservariable (INST_R0, result);
454   return;
455 }
456
457
458 void __declspec(dllexport)
459 service_start (HWND hwndParent, int string_size, char *variables,
460                stack_t **stacktop, extra_parameters_t *extra)
461 {
462   SC_HANDLE service;
463   const char *result = NULL;
464   char service_name[256];
465   char argc_str[256];
466 #define NR_ARGS 10
467 #define ARG_MAX 256
468   char argv_str[NR_ARGS][ARG_MAX];
469   char *argv[NR_ARGS + 1];
470   int argc;
471   int i;
472   int err = 0;
473
474   g_hwndParent = hwndParent;
475   EXDLL_INIT();
476
477   /* The expected stack layout: service_name argc [argv].  */
478   if (popstring (service_name, sizeof (service_name)))
479     err = 1;
480   if (!err && popstring (argc_str, sizeof (argc_str)))
481     err = 1;
482   if (!err)
483     {
484       argc = atoi (argc_str);
485       for (i = 0; i < argc; i++)
486         {
487           if (popstring (argv_str[i], ARG_MAX))
488             {
489               err = 1;
490               break;
491             }
492           argv[i] = argv_str[i];
493         }
494       argv[i] = NULL;
495     }
496   if (err)
497     {
498       setuservariable (INST_R0, "1");
499       return;
500     }
501
502   service = service_lookup (service_name);
503   if (service == NULL)
504     return;
505
506   err = StartService (service, argc, argc == 0 ? NULL : argv);
507   if (err == 0)
508     {
509       service_error ("StartService");
510       CloseServiceHandle (service);
511       return;
512     }
513   CloseServiceHandle (service);
514
515   setuservariable (INST_R0, "0");
516   return;
517 }
518
519
520 void __declspec(dllexport)
521 service_stop (HWND hwndParent, int string_size, char *variables,
522               stack_t **stacktop, extra_parameters_t *extra)
523 {
524   SC_HANDLE service;
525   const char *result = NULL;
526   char service_name[256];
527   int err = 0;
528   SERVICE_STATUS status;
529   DWORD timeout = 10000;        /* 10 seconds.  */
530   DWORD start_time;
531
532   g_hwndParent = hwndParent;
533   EXDLL_INIT();
534
535   /* The expected stack layout: service_name argc [argv].  */
536   if (popstring (service_name, sizeof (service_name)))
537     err = 1;
538   if (err)
539     {
540       setuservariable (INST_R0, "1");
541       return;
542     }
543
544   service = service_lookup (service_name);
545   if (service == NULL)
546     return;
547
548   err = QueryServiceStatus (service, &status);
549   if (err == 0)
550     {
551       service_error ("QueryService");
552       CloseServiceHandle (service);
553       return;
554     }
555
556   if (status.dwCurrentState != SERVICE_STOPPED
557       && status.dwCurrentState != SERVICE_STOP_PENDING)
558     {
559       err = ControlService (service, SERVICE_CONTROL_STOP, &status);
560       if (err == 0)
561         {
562           service_error ("ControlService");
563           CloseServiceHandle (service);
564           return;
565         }
566     }
567
568   start_time = GetTickCount ();
569   while (status.dwCurrentState != SERVICE_STOPPED)
570     {
571       Sleep (1000);     /* One second.  */
572       if (!QueryServiceStatus (service, &status))
573         {
574           service_error ("QueryService");
575           CloseServiceHandle (service);
576           return;
577         }
578       if (status.dwCurrentState == SERVICE_STOPPED)
579         break;
580
581       if (GetTickCount () - start_time > timeout)
582         {
583           char buf[1024];
584           snprintf (buf, sizeof (buf) - 1,
585                     "time out waiting for service %s to stop\r\n",
586                     service_name);
587           MessageBox (g_hwndParent, buf, 0, MB_OK);
588           setuservariable (INST_R0, "1");
589           return;
590         }
591     }
592   CloseServiceHandle (service);
593   setuservariable (INST_R0, "0");
594   return;
595 }
596
597
598 void __declspec(dllexport)
599 service_delete (HWND hwndParent, int string_size, char *variables,
600                 stack_t **stacktop, extra_parameters_t *extra)
601 {
602   SC_HANDLE service;
603   const char *result = NULL;
604   char service_name[256];
605   int err = 0;
606
607   g_hwndParent = hwndParent;
608   EXDLL_INIT();
609
610   /* The expected stack layout: service_name argc [argv].  */
611   if (popstring (service_name, sizeof (service_name)))
612     err = 1;
613   if (err)
614     {
615       setuservariable (INST_R0, "1");
616       return;
617     }
618
619   service = service_lookup (service_name);
620   if (service == NULL)
621     return;
622
623   err = DeleteService (service);
624   if (err == 0)
625     {
626       service_error ("DeleteService");
627       CloseServiceHandle (service);
628       return;
629     }
630   CloseServiceHandle (service);
631
632   setuservariable (INST_R0, "0");
633   return;
634 }
635
636 \f
637 #include <stdio.h>
638
639 /* Extract config file parameters.  FIXME: Not particularly robust.
640    We expect some reasonable formatting.  The parser below is very
641    limited.  It expects a command line option /c=FILE or /C=FILE,
642    where FILE must be enclosed in double-quotes if it contains spaces.
643    That file should contain a single section [gpg4win] and KEY=VALUE
644    pairs for each additional configuration file to install.  Comments
645    are supported only on lines by themselves.  VALUE can be quoted in
646    double-quotes, but does not need to be, unless it has whitespace at
647    the beginning or end.  KEY can, for example, be "gpg.conf" (without
648    the quotes).  */
649 void
650 config_init (char **keys, char **values, int max)
651 {
652   /* First, parse the command line.  */
653   char *cmdline;
654   char *begin = NULL;
655   char *end = NULL;
656   char mark;
657   char *fname;
658   char *ptr;
659   FILE *conf;
660
661   *keys = NULL;
662   *values = NULL;
663
664   cmdline = getuservariable (INST_CMDLINE);
665
666   mark = (*cmdline == '"') ? (cmdline++, '"') : ' ';
667   while (*cmdline && *cmdline != mark)
668     cmdline++;
669   if (mark == '"' && *cmdline)
670     cmdline++;
671   while (*cmdline && *cmdline == ' ')
672     cmdline++;
673
674   while (*cmdline)
675     {
676       /* We are at the beginning of a new argument.  */
677       if (cmdline[0] == '/' && (cmdline[1] == 'C' || cmdline[1] == 'c')
678           && cmdline[2] == '=')
679         {
680           cmdline += 3;
681           begin = cmdline;
682         }
683
684       while (*cmdline && *cmdline != ' ')
685         {
686           /* Skip over quoted parts.  */
687           if (*cmdline == '"')
688             {
689               cmdline++;
690               while (*cmdline && *cmdline != '"')
691                 cmdline++;
692               if (*cmdline)
693                 cmdline++;
694             }
695           else
696             cmdline++;
697         }
698       if (begin && !end)
699         {
700           end = cmdline - 1;
701           break;
702         }
703       while (*cmdline && *cmdline == ' ')
704         cmdline++;
705     }
706
707   if (!begin || begin > end)
708     return;
709
710   /* Strip quotes.  */
711   if (*begin == '"' && *end == '"')
712     {
713       begin++;
714       end--;
715     }
716   if (begin > end)
717     return;
718
719   fname = malloc (end - begin + 2);
720   if (!fname)
721     return;
722
723   ptr = fname;
724   while (begin <= end)
725     *(ptr++) = *(begin++);
726   *ptr = '\0';
727
728   conf = fopen (fname, "r");
729   free (fname);
730   if (!conf)
731     return;
732
733   while (max - 1 > 0)
734     {
735       char line[256];
736       char *ptr2;
737
738       if (fgets (line, sizeof (line), conf) == NULL)
739         break;
740       ptr = &line[strlen (line)];
741       while (ptr > line && (ptr[-1] == '\n' || ptr[-1] == '\r'
742                             || ptr[-1] == ' ' || ptr[-1] == '\t'))
743         ptr--;
744       *ptr = '\0';
745
746       ptr = line;
747       while (*ptr && (*ptr == ' ' || *ptr == '\t'))
748         ptr++;
749       /* Ignore comment lines.  */
750       /* FIXME: Ignore section markers.  */
751       if (*ptr == '\0' || *ptr == ';' || *ptr == '[')
752         continue;
753       begin = ptr;
754       while (*ptr && *ptr != '=' && *ptr != ' ' && *ptr != '\t')
755         ptr++;
756       end = ptr - 1;
757       while (*ptr && (*ptr == ' ' || *ptr == '\t'))
758         ptr++;
759       if (*ptr != '=')
760         continue;
761       ptr++;
762
763       if (begin > end)
764         continue;
765
766       /* We found a key.  */
767       *keys = malloc (end - begin + 2);
768       if (!keys)
769         return;
770       ptr2 = *keys;
771       while (begin <= end)
772         *(ptr2++) = *(begin++);
773       *ptr2 = '\0';
774
775       *values = NULL;
776
777       while (*ptr && (*ptr == ' ' || *ptr == '\t'))
778         ptr++;
779       begin = ptr;
780       /* In this case, end points to the byte after the value, which
781          is OK because that is '\0'.  */
782       end = &line[strlen (line)];
783       if (begin > end)
784         begin = end;
785
786       /* Strip quotes.  */
787       if (*begin == '"' && end[-1] == '"')
788         {
789           begin++;
790           end--;
791           *end = '\0';
792         }
793       if (begin > end)
794         return;
795
796       *values = malloc (end - begin + 1);
797       ptr2 = *values;
798       while (begin <= end)
799         *(ptr2++) = *(begin++);
800
801       keys++;
802       values++;
803       max--;
804     }
805
806   fclose (conf);
807   *keys = NULL;
808   *values = NULL;
809 }
810
811
812 char *
813 config_lookup (char *key)
814 {
815 #define MAX_KEYS 128
816   static int initialised = 0;
817   static char *keys[MAX_KEYS];
818   static char *values[MAX_KEYS];
819   int i;
820
821   if (initialised == 0)
822     {
823       initialised = 1;
824       config_init (keys, values, MAX_KEYS);
825
826 #if 0
827       MessageBox(g_hwndParent, "Configuration File:", 0, MB_OK);
828       i = 0;
829       while (keys[i])
830         {
831           char buf[256];
832           sprintf (buf, "%s=%s\r\n", keys[i], values[i]);
833           MessageBox (g_hwndParent, buf, 0, MB_OK);
834           i++;
835         }
836 #endif
837     }
838
839   i = 0;
840   while (keys[i])
841     {
842       if (!strcmp (keys[i], key))
843         return values[i];
844       i++;
845     }
846
847   return NULL;
848 }
849
850
851 void __declspec(dllexport)
852 config_fetch (HWND hwndParent, int string_size, char *variables,
853               stack_t **stacktop, extra_parameters_t *extra)
854 {
855   char key[256];
856   int err = 0;
857   char *value;
858
859   g_hwndParent = hwndParent;
860   EXDLL_INIT();
861
862   /* The expected stack layout: key.  */
863   if (popstring (key, sizeof (key)))
864     err = 1;
865   if (err)
866     {
867       setuservariable (INST_R0, "");
868       return;
869     }
870
871   value = config_lookup (key);
872
873   setuservariable (INST_R0, value == NULL ? "" : value);
874   return;
875 }
876
877
878 void __declspec(dllexport)
879 config_fetch_bool (HWND hwndParent, int string_size, char *variables,
880                    stack_t **stacktop, extra_parameters_t *extra)
881 {
882   char key[256];
883   int err = 0;
884   char *value;
885   int result;
886
887   g_hwndParent = hwndParent;
888   EXDLL_INIT();
889
890   /* The expected stack layout: key.  */
891   if (popstring (key, sizeof (key)))
892     err = 1;
893   if (err)
894     {
895       setuservariable (INST_R0, "");
896       return;
897     }
898
899   value = config_lookup (key);
900   if (value == NULL || *value == '\0')
901     {
902       setuservariable (INST_R0, "");
903       return;
904     }
905
906   result = 0;
907   if (!strcasecmp (value, "true")
908       || !strcasecmp (value, "yes")
909       || atoi (value) != 0)
910     result = 1;
911
912   setuservariable (INST_R0, result == 0 ? "0" : "1");
913   return;
914 }
915
916 \f
917 /* Return a string from the Win32 Registry or NULL in case of error.
918    Caller must release the return value.  A NULL for root is an alias
919    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.  */
920 char *
921 read_w32_registry_string (HKEY root, const char *dir, const char *name)
922 {
923   HKEY root_key;
924   HKEY key_handle;
925   DWORD n1, nbytes, type;
926   char *result = NULL;
927
928   root_key = root;
929   if (! root_key)
930     root_key = HKEY_CURRENT_USER;
931
932   if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
933     {
934       if (root)
935         return NULL; /* no need for a RegClose, so return direct */
936       /* It seems to be common practise to fall back to HKLM. */
937       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
938         return NULL; /* still no need for a RegClose, so return direct */
939     }
940
941   nbytes = 1;
942   if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
943     if (root)
944       goto leave;
945     /* Try to fallback to HKLM also vor a missing value.  */
946     RegCloseKey (key_handle);
947     if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
948       return NULL; /* Nope.  */
949     if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
950       goto leave;
951   }
952
953   result = malloc( (n1=nbytes+1) );
954
955   if( !result )
956     goto leave;
957   if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
958     free(result); result = NULL;
959     goto leave;
960   }
961   result[nbytes] = 0; /* make sure it is really a string  */
962
963  leave:
964   RegCloseKey( key_handle );
965   return result;
966 }
967
968
969 #define ENV_HK HKEY_LOCAL_MACHINE
970 #define ENV_REG "SYSTEM\\CurrentControlSet\\Control\\" \
971     "Session Manager\\Environment"
972   /* The following setting can be used for a per-user setting.  */
973 #if 0
974 #define ENV_HK HKEY_CURRENT_USER
975 #define ENV_REG "Environment"
976 #endif
977 /* Due to a bug in Windows7 (kb 2685893) we better but a lower limit
978    than 8191 on the maximum length of the PATH variable.  Note, that
979    depending on the used toolchain we used to have a 259 byte limit in
980    the past.  */
981 #define PATH_LENGTH_LIMIT 2047
982
983 void __declspec(dllexport)
984 path_add (HWND hwndParent, int string_size, char *variables,
985           stack_t **stacktop, extra_parameters_t *extra)
986 {
987   char dir[PATH_LENGTH_LIMIT];
988   char *path;
989   char *path_new;
990   int path_new_size;
991   char *comp;
992   const char delims[] = ";";
993   HKEY key_handle = 0;
994
995   g_hwndParent = hwndParent;
996   EXDLL_INIT();
997
998   setuservariable (INST_R0, "0");
999
1000 /*   MessageBox (g_hwndParent, "XXX 1", 0, MB_OK); */
1001
1002   /* The expected stack layout: path component.  */
1003   if (popstring (dir, sizeof (dir)))
1004     return;
1005
1006 /*   MessageBox (g_hwndParent, "XXX 2", 0, MB_OK); */
1007
1008   path = read_w32_registry_string (ENV_HK, ENV_REG, "Path");
1009   if (! path)
1010     {
1011       MessageBox (g_hwndParent, "No PATH variable found", 0, MB_OK);
1012       return;
1013     }
1014
1015 /*   MessageBox (g_hwndParent, "XXX 3", 0, MB_OK); */
1016
1017   /* Old path plus semicolon plus dir plus terminating nul.  */
1018   path_new_size = strlen (path) + 1 + strlen (dir) + 1;
1019   if (path_new_size > PATH_LENGTH_LIMIT)
1020     {
1021       MessageBox (g_hwndParent, "PATH env variable too big", 0, MB_OK);
1022       free (path);
1023       return;
1024     }
1025
1026 /*   MessageBox (g_hwndParent, "XXX 4", 0, MB_OK); */
1027
1028   path_new = malloc (path_new_size);
1029   if (!path_new)
1030     {
1031       free (path);
1032       return;
1033     }
1034
1035 /*   MessageBox (g_hwndParent, "XXX 5", 0, MB_OK); */
1036
1037   strcpy (path_new, path);
1038   strcat (path_new, ";");
1039   strcat (path_new, dir);
1040
1041 /*   MessageBox (g_hwndParent, "XXX 6", 0, MB_OK); */
1042 /*   MessageBox (g_hwndParent, dir, 0, MB_OK); */
1043 /*   MessageBox (g_hwndParent, "XXX 7", 0, MB_OK); */
1044
1045   /* Check if the directory already exists in the path.  */
1046   comp = strtok (path, delims);
1047   do
1048     {
1049 /*       MessageBox (g_hwndParent, comp, 0, MB_OK); */
1050
1051       if (!strcmp (comp, dir))
1052         {
1053           free (path);
1054           free (path_new);
1055           return;
1056         }
1057       comp = strtok (NULL, delims);
1058     }
1059   while (comp);
1060   free (path);
1061
1062 /*   MessageBox (g_hwndParent, "XXX 8", 0, MB_OK); */
1063
1064   /* Set a key for our CLSID.  */
1065   RegCreateKey (ENV_HK, ENV_REG, &key_handle);
1066   RegSetValueEx (key_handle, "Path", 0, REG_EXPAND_SZ,
1067                  path_new, path_new_size);
1068   RegCloseKey (key_handle);
1069   SetEnvironmentVariable("PATH", path_new);
1070   free (path_new);
1071
1072 /*   MessageBox (g_hwndParent, "XXX 9", 0, MB_OK); */
1073
1074   setuservariable (INST_R0, "1");
1075 }
1076
1077
1078 void __declspec(dllexport)
1079 path_remove (HWND hwndParent, int string_size, char *variables,
1080              stack_t **stacktop, extra_parameters_t *extra)
1081 {
1082   char dir[PATH_LENGTH_LIMIT];
1083   char *path;
1084   char *path_new;
1085   int path_new_size;
1086   char *comp;
1087   const char delims[] = ";";
1088   HKEY key_handle = 0;
1089   int changed = 0;
1090   int count = 0;
1091
1092   g_hwndParent = hwndParent;
1093   EXDLL_INIT();
1094
1095   setuservariable (INST_R0, "0");
1096
1097   /* The expected stack layout: path component.  */
1098   if (popstring (dir, sizeof (dir)))
1099     return;
1100
1101   path = read_w32_registry_string (ENV_HK, ENV_REG, "Path");
1102   /* Old path plus semicolon plus dir plus terminating nul.  */
1103   path_new_size = strlen (path) + 1;
1104   path_new = malloc (path_new_size);
1105   if (!path_new)
1106     {
1107       free (path);
1108       return;
1109     }
1110   path_new[0] = '\0';
1111
1112   /* Compose the new path.  */
1113   comp = strtok (path, delims);
1114   do
1115     {
1116       if (strcmp (comp, dir))
1117         {
1118           if (count != 0)
1119             strcat (path_new, ";");
1120           strcat (path_new, comp);
1121           count++;
1122         }
1123       else
1124         changed = 1;
1125
1126       comp = strtok (NULL, delims);
1127     }
1128   while (comp);
1129   free (path);
1130
1131   if (! changed)
1132     return;
1133
1134   /* Set a key for our CLSID.  */
1135   RegCreateKey (ENV_HK, ENV_REG, &key_handle);
1136   RegSetValueEx (key_handle, "Path", 0, REG_EXPAND_SZ,
1137                  path_new, path_new_size);
1138   RegCloseKey (key_handle);
1139   free (path_new);
1140
1141   setuservariable (INST_R0, "1");
1142 }
1143
1144 /** @brief Kill processes with the name name.
1145  *
1146  * This function tries to kill a process using ExitProcess.
1147  *
1148  * If it does not work it does not work. No return values.
1149  * The intention is to make an effort to kill something during
1150  * installation / uninstallation.
1151  *
1152  * The function signature is explained by NSIS.
1153  */
1154 void __declspec(dllexport) __cdecl KillProc(HWND hwndParent,
1155                                             int string_size,
1156                                             char *variables,
1157                                             stack_t **stacktop)
1158 {
1159   HANDLE h;
1160   PROCESSENTRY32 pe32;
1161
1162   if (!stacktop || !*stacktop || !(*stacktop)->text)
1163     {
1164       ERRORPRINTF ("Invalid call to KillProc.");
1165       return;
1166     }
1167
1168
1169   h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
1170   if (h == INVALID_HANDLE_VALUE)
1171     {
1172       ERRORPRINTF ("Failed to create Toolhelp snapshot");
1173       return;
1174     }
1175   pe32.dwSize = sizeof (PROCESSENTRY32);
1176
1177   if (!Process32First (h, &pe32))
1178     {
1179       ERRORPRINTF ("Failed to get first process");
1180       CloseHandle (h);
1181       return;
1182     }
1183
1184   do
1185     {
1186       if (!strcmp ((*stacktop)->text, pe32.szExeFile))
1187         {
1188           HANDLE hProc = OpenProcess (PROCESS_ALL_ACCESS, FALSE,
1189                                       pe32.th32ProcessID);
1190           if (!hProc)
1191             {
1192               ERRORPRINTF ("Failed to open process handle.");
1193               continue;
1194             }
1195           if (!TerminateProcess (hProc, 1))
1196             {
1197               ERRORPRINTF ("Failed to terminate process.");
1198             }
1199           CloseHandle (hProc);
1200         }
1201     }
1202   while (Process32Next (h, &pe32));
1203   CloseHandle (h);
1204 }