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