012e4af0be92a19913ef47743ab2dde7c5f50c04
[gnupg.git] / build-aux / speedo / w32 / 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  *
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.
8  *
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:
12  *
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.
17  *
18  * 2. Altered source versions must be plainly marked as such, and must
19  *    not be misrepresented as being the original software.
20  *
21  * 3. This notice may not be removed or altered from any source
22  *    distribution.
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]
29  *
30  * Fixed some compiler warnings. [wk 2014-02-24].
31  */
32
33 #include <stdio.h>
34 #include <windows.h>
35 #include "exdll.h"
36
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. */
41
42
43 /* Standard entry point for DLLs. */
44 int WINAPI
45 DllMain (HANDLE hinst, DWORD reason, LPVOID reserved)
46 {
47    if (reason == DLL_PROCESS_ATTACH)
48      g_hInstance = hinst;
49    return TRUE;
50 }
51
52
53
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)
58 {
59   g_hwndParent = hwndParent;
60
61   EXDLL_INIT();
62
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
68   // parameters.
69
70   // do your stuff here
71   {
72     char buf[1024];
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);
77
78     snprintf (buf, sizeof buf,
79              "autoclose    =%d\r\n"
80              "all_user_var =%d\r\n"
81              "exec_error   =%d\r\n"
82              "abort        =%d\r\n"
83              "exec_reboot  =%d\r\n"
84              "reboot_called=%d\r\n"
85              "silent       =%d\r\n"
86              "instdir_error=%d\r\n"
87              "rtl          =%d\r\n"
88              "errlvl       =%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);
100   }
101 }
102
103
104 void __declspec(dllexport)
105 runonce (HWND hwndParent, int string_size, char *variables,
106          stack_t **stacktop, extra_parameters_t *extra)
107 {
108   const char *result;
109
110   g_hwndParent = hwndParent;
111   EXDLL_INIT();
112
113   CreateMutexA (NULL, 0, getuservariable(INST_R0));
114   result = GetLastError ()? "1":"0";
115   setuservariable (INST_R0, result);
116 }
117
118
119 void __declspec(dllexport)
120 playsound (HWND hwndParent, int string_size, char *variables,
121            stack_t **stacktop, extra_parameters_t *extra)
122 {
123   char fname[MAX_PATH];
124
125   g_hwndParent = hwndParent;
126   EXDLL_INIT();
127
128   if (popstring(fname, sizeof fname))
129     return;
130   PlaySound (fname, NULL, SND_ASYNC|SND_FILENAME|SND_NODEFAULT);
131 }
132
133
134 void __declspec(dllexport)
135 stopsound (HWND hwndParent, int string_size, char *variables,
136            stack_t **stacktop, extra_parameters_t *extra)
137 {
138   g_hwndParent = hwndParent;
139   EXDLL_INIT();
140   PlaySound (NULL, NULL, 0);
141 }
142
143
144 /* Windows procedure to control the splashimage.  This one pauses the
145    execution until the sleep time is over or the user closes this
146    windows. */
147 static LRESULT CALLBACK
148 splash_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
149 {
150   LRESULT result = 0;
151
152   switch (uMsg)
153     {
154     case WM_CREATE:
155       {
156         BITMAP bm;
157         RECT vp;
158
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,
166                      SWP_NOZORDER);
167         ShowWindow(hwnd,SW_SHOW);
168         SetTimer(hwnd,1,sleepint,NULL);
169       }
170       break;
171
172     case WM_PAINT:
173       {
174         PAINTSTRUCT ps;
175         RECT r;
176         HDC curdc=BeginPaint(hwnd,&ps);
177         HDC hdc=CreateCompatibleDC(curdc);
178         HBITMAP oldbm;
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,
182                hdc,0,0,SRCCOPY);
183         SelectObject(hdc,oldbm);
184         DeleteDC(hdc);
185         EndPaint(hwnd,&ps);
186       }
187       break;
188
189     case WM_CLOSE:
190       break;
191
192     case WM_TIMER:
193     case WM_LBUTTONDOWN:
194       DestroyWindow(hwnd);
195       /*(fall through)*/
196     default:
197       result =  DefWindowProc (hwnd, uMsg, wParam, lParam);
198     }
199
200   return result;
201 }
202
203
204 /* Display a splash screen.  Call as
205
206      g4wihelp::showsplash SLEEP FNAME
207
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
210    is supported.
211 */
212 void __declspec(dllexport)
213 showsplash (HWND hwndParent, int string_size, char *variables,
214            stack_t **stacktop, extra_parameters_t *extra)
215 {
216   static WNDCLASS wc;
217   char sleepstr[30];
218   char fname[MAX_PATH];
219   int err = 0;
220   char *p;
221   char classname[] = "_sp";
222
223   g_hwndParent = hwndParent;
224   EXDLL_INIT();
225   if (popstring(sleepstr, sizeof sleepstr))
226     err = 1;
227   if (popstring(fname, sizeof fname))
228     err = 1;
229   if (err)
230     return;
231
232   if (!*fname)
233     return; /* Nothing to do. */
234
235   for (sleepint=0, p=sleepstr; *p >= '0' && *p <= '9'; p++)
236     {
237       sleepint *= 10;
238       sleepint += *p - '0';
239     }
240   if (sleepint <= 0)
241     return; /* Nothing to do. */
242
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))
248     return; /* Error. */
249
250   g_hbm = LoadImage (NULL, fname, IMAGE_BITMAP,
251                      0, 0 , LR_CREATEDIBSECTION|LR_LOADFROMFILE);
252   if (g_hbm)
253     {
254       MSG msg;
255       HWND hwnd;
256
257       hwnd = CreateWindowEx (WS_EX_TOOLWINDOW, classname, classname,
258                              0, 0, 0, 0, 0, (HWND)hwndParent, NULL,
259                              g_hInstance, NULL);
260
261       while (IsWindow(hwnd) && GetMessage ( &msg, hwnd, 0, 0))
262         {
263           DispatchMessage (&msg);
264         }
265
266       DeleteObject (g_hbm);
267       g_hbm = NULL;
268     }
269   UnregisterClass (classname, g_hInstance);
270 }
271
272 \f
273 /* Service Management.  */
274
275 /* Use this to report unexpected errors.  FIXME: This is really not
276    very descriptive.  */
277 void
278 service_error (const char *str)
279 {
280   char buf[1024];
281   snprintf (buf, sizeof (buf), "error: %s: ec=%d\r\n", str,
282             GetLastError ());
283   MessageBox(g_hwndParent, buf, 0, MB_OK);
284
285   setuservariable (INST_R0, "1");
286 }
287
288
289 void __declspec(dllexport)
290 service_create (HWND hwndParent, int string_size, char *variables,
291                  stack_t **stacktop, extra_parameters_t *extra)
292 {
293   SC_HANDLE sc;
294   SC_HANDLE service;
295   const char *result = NULL;
296   char service_name[256];
297   char display_name[256];
298   char program[256];
299   int err = 0;
300
301   g_hwndParent = hwndParent;
302   EXDLL_INIT();
303
304   /* The expected stack layout: service_name, display_name, program.  */
305   if (popstring (service_name, sizeof (service_name)))
306     err = 1;
307   if (!err && popstring (display_name, sizeof (display_name)))
308     err = 1;
309   if (!err && popstring (program, sizeof (program)))
310     err = 1;
311   if (err)
312     {
313       setuservariable (INST_R0, "1");
314       return;
315     }
316
317   sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
318   if (sc == NULL)
319     {
320       service_error ("OpenSCManager");
321       return;
322     }
323
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.  */
328                            SERVICE_AUTO_START,
329                            SERVICE_ERROR_NORMAL, program,
330                            NULL, NULL, NULL,
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" */,
341                            NULL);
342   if (service == NULL)
343     {
344       service_error ("CreateService");
345       CloseServiceHandle (sc);
346       return;
347     }
348   CloseServiceHandle (service);
349
350   result = GetLastError () ? "1":"0";
351   setuservariable (INST_R0, result);
352   return;
353 }
354
355
356 /* Requires g_hwndParent to be set!  */
357 SC_HANDLE
358 service_lookup (char *service_name)
359 {
360   SC_HANDLE sc;
361   SC_HANDLE service;
362
363   sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
364   if (sc == NULL)
365     {
366       service_error ("OpenSCManager");
367       return NULL;
368     }
369   service = OpenService (sc, service_name, SC_MANAGER_ALL_ACCESS);
370   if (service == NULL)
371     {
372       /* Fail silently here.  */
373       CloseServiceHandle (sc);
374       return NULL;
375     }
376   CloseServiceHandle (sc);
377   return service;
378 }
379
380
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)
385 {
386   SC_HANDLE service;
387   const char *result = NULL;
388   char service_name[256];
389   int err = 0;
390   SERVICE_STATUS status;
391
392   g_hwndParent = hwndParent;
393   EXDLL_INIT();
394
395   /* The expected stack layout: service_name argc [argv].  */
396   if (popstring (service_name, sizeof (service_name)))
397     err = 1;
398   if (err)
399     {
400       setuservariable (INST_R0, "ERROR");
401       return;
402     }
403
404   service = service_lookup (service_name);
405   if (service == NULL)
406   if (err == 0)
407     {
408       setuservariable (INST_R0, "MISSING");
409       return;
410     }
411
412   err = QueryServiceStatus (service, &status);
413   if (err == 0)
414     {
415       setuservariable (INST_R0, "ERROR");
416       CloseServiceHandle (service);
417       return;
418     }
419   CloseServiceHandle (service);
420
421   switch (status.dwCurrentState)
422     {
423     case SERVICE_START_PENDING:
424       result = "START_PENDING";
425       break;
426     case SERVICE_RUNNING:
427       result = "RUNNING";
428       break;
429     case SERVICE_PAUSE_PENDING:
430       result = "PAUSE_PENDING";
431       break;
432     case SERVICE_PAUSED:
433       result = "PAUSED";
434       break;
435     case SERVICE_CONTINUE_PENDING:
436       result = "CONTINUE_PENDING";
437       break;
438     case SERVICE_STOP_PENDING:
439       result = "STOP_PENDING";
440       break;
441     case SERVICE_STOPPED:
442       result = "STOPPED";
443       break;
444     default:
445       result = "UNKNOWN";
446     }
447   setuservariable (INST_R0, result);
448   return;
449 }
450
451
452 void __declspec(dllexport)
453 service_start (HWND hwndParent, int string_size, char *variables,
454                stack_t **stacktop, extra_parameters_t *extra)
455 {
456   SC_HANDLE service;
457   const char *result = NULL;
458   char service_name[256];
459   char argc_str[256];
460 #define NR_ARGS 10
461 #define ARG_MAX 256
462   char argv_str[NR_ARGS][ARG_MAX];
463   const char *argv[NR_ARGS + 1];
464   int argc;
465   int i;
466   int err = 0;
467
468   g_hwndParent = hwndParent;
469   EXDLL_INIT();
470
471   /* The expected stack layout: service_name argc [argv].  */
472   if (popstring (service_name, sizeof (service_name)))
473     err = 1;
474   if (!err && popstring (argc_str, sizeof (argc_str)))
475     err = 1;
476   if (!err)
477     {
478       argc = atoi (argc_str);
479       for (i = 0; i < argc; i++)
480         {
481           if (popstring (argv_str[i], ARG_MAX))
482             {
483               err = 1;
484               break;
485             }
486           argv[i] = argv_str[i];
487         }
488       argv[i] = NULL;
489     }
490   if (err)
491     {
492       setuservariable (INST_R0, "1");
493       return;
494     }
495
496   service = service_lookup (service_name);
497   if (service == NULL)
498     return;
499
500   err = StartService (service, argc, argc == 0 ? NULL : argv);
501   if (err == 0)
502     {
503       service_error ("StartService");
504       CloseServiceHandle (service);
505       return;
506     }
507   CloseServiceHandle (service);
508
509   setuservariable (INST_R0, "0");
510   return;
511 }
512
513
514 void __declspec(dllexport)
515 service_stop (HWND hwndParent, int string_size, char *variables,
516               stack_t **stacktop, extra_parameters_t *extra)
517 {
518   SC_HANDLE service;
519   const char *result = NULL;
520   char service_name[256];
521   int err = 0;
522   SERVICE_STATUS status;
523   DWORD timeout = 10000;        /* 10 seconds.  */
524   DWORD start_time;
525
526   g_hwndParent = hwndParent;
527   EXDLL_INIT();
528
529   /* The expected stack layout: service_name argc [argv].  */
530   if (popstring (service_name, sizeof (service_name)))
531     err = 1;
532   if (err)
533     {
534       setuservariable (INST_R0, "1");
535       return;
536     }
537
538   service = service_lookup (service_name);
539   if (service == NULL)
540     return;
541
542   err = QueryServiceStatus (service, &status);
543   if (err == 0)
544     {
545       service_error ("QueryService");
546       CloseServiceHandle (service);
547       return;
548     }
549
550   if (status.dwCurrentState != SERVICE_STOPPED
551       && status.dwCurrentState != SERVICE_STOP_PENDING)
552     {
553       err = ControlService (service, SERVICE_CONTROL_STOP, &status);
554       if (err == 0)
555         {
556           service_error ("ControlService");
557           CloseServiceHandle (service);
558           return;
559         }
560     }
561
562   start_time = GetTickCount ();
563   while (status.dwCurrentState != SERVICE_STOPPED)
564     {
565       Sleep (1000);     /* One second.  */
566       if (!QueryServiceStatus (service, &status))
567         {
568           service_error ("QueryService");
569           CloseServiceHandle (service);
570           return;
571         }
572       if (status.dwCurrentState == SERVICE_STOPPED)
573         break;
574
575       if (GetTickCount () - start_time > timeout)
576         {
577           char buf[1024];
578           snprintf (buf, sizeof (buf),
579                     "time out waiting for service %s to stop\r\n",
580                     service_name);
581           MessageBox (g_hwndParent, buf, 0, MB_OK);
582           setuservariable (INST_R0, "1");
583           return;
584         }
585     }
586   CloseServiceHandle (service);
587   setuservariable (INST_R0, "0");
588   return;
589 }
590
591
592 void __declspec(dllexport)
593 service_delete (HWND hwndParent, int string_size, char *variables,
594                 stack_t **stacktop, extra_parameters_t *extra)
595 {
596   SC_HANDLE service;
597   const char *result = NULL;
598   char service_name[256];
599   int err = 0;
600
601   g_hwndParent = hwndParent;
602   EXDLL_INIT();
603
604   /* The expected stack layout: service_name argc [argv].  */
605   if (popstring (service_name, sizeof (service_name)))
606     err = 1;
607   if (err)
608     {
609       setuservariable (INST_R0, "1");
610       return;
611     }
612
613   service = service_lookup (service_name);
614   if (service == NULL)
615     return;
616
617   err = DeleteService (service);
618   if (err == 0)
619     {
620       service_error ("DeleteService");
621       CloseServiceHandle (service);
622       return;
623     }
624   CloseServiceHandle (service);
625
626   setuservariable (INST_R0, "0");
627   return;
628 }
629
630 \f
631 #include <stdio.h>
632
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
642    the quotes).  */
643 void
644 config_init (char **keys, char **values, int max)
645 {
646   /* First, parse the command line.  */
647   char *cmdline;
648   char *begin = NULL;
649   char *end = NULL;
650   char mark;
651   char *fname;
652   char *ptr;
653   FILE *conf;
654
655   *keys = NULL;
656   *values = NULL;
657
658   cmdline = getuservariable (INST_CMDLINE);
659
660   mark = (*cmdline == '"') ? (cmdline++, '"') : ' ';
661   while (*cmdline && *cmdline != mark)
662     cmdline++;
663   if (mark == '"' && *cmdline)
664     cmdline++;
665   while (*cmdline && *cmdline == ' ')
666     cmdline++;
667
668   while (*cmdline)
669     {
670       /* We are at the beginning of a new argument.  */
671       if (cmdline[0] == '/' && (cmdline[1] == 'C' || cmdline[1] == 'c')
672           && cmdline[2] == '=')
673         {
674           cmdline += 3;
675           begin = cmdline;
676         }
677
678       while (*cmdline && *cmdline != ' ')
679         {
680           /* Skip over quoted parts.  */
681           if (*cmdline == '"')
682             {
683               cmdline++;
684               while (*cmdline && *cmdline != '"')
685                 cmdline++;
686               if (*cmdline)
687                 cmdline++;
688             }
689           else
690             cmdline++;
691         }
692       if (begin && !end)
693         {
694           end = cmdline - 1;
695           break;
696         }
697       while (*cmdline && *cmdline == ' ')
698         cmdline++;
699     }
700
701   if (!begin || begin > end)
702     return;
703
704   /* Strip quotes.  */
705   if (*begin == '"' && *end == '"')
706     {
707       begin++;
708       end--;
709     }
710   if (begin > end)
711     return;
712
713   fname = malloc (end - begin + 2);
714   if (!fname)
715     return;
716
717   ptr = fname;
718   while (begin <= end)
719     *(ptr++) = *(begin++);
720   *ptr = '\0';
721
722   conf = fopen (fname, "r");
723   free (fname);
724   if (!conf)
725     return;
726
727   while (max - 1 > 0)
728     {
729       char line[256];
730       char *ptr2;
731
732       if (fgets (line, sizeof (line), conf) == NULL)
733         break;
734       ptr = &line[strlen (line)];
735       while (ptr > line && (ptr[-1] == '\n' || ptr[-1] == '\r'
736                             || ptr[-1] == ' ' || ptr[-1] == '\t'))
737         ptr--;
738       *ptr = '\0';
739
740       ptr = line;
741       while (*ptr && (*ptr == ' ' || *ptr == '\t'))
742         ptr++;
743       /* Ignore comment lines.  */
744       /* FIXME: Ignore section markers.  */
745       if (*ptr == '\0' || *ptr == ';' || *ptr == '[')
746         continue;
747       begin = ptr;
748       while (*ptr && *ptr != '=' && *ptr != ' ' && *ptr != '\t')
749         ptr++;
750       end = ptr - 1;
751       while (*ptr && (*ptr == ' ' || *ptr == '\t'))
752         ptr++;
753       if (*ptr != '=')
754         continue;
755       ptr++;
756
757       if (begin > end)
758         continue;
759
760       /* We found a key.  */
761       *keys = malloc (end - begin + 2);
762       if (!keys)
763         return;
764       ptr2 = *keys;
765       while (begin <= end)
766         *(ptr2++) = *(begin++);
767       *ptr2 = '\0';
768
769       *values = NULL;
770
771       while (*ptr && (*ptr == ' ' || *ptr == '\t'))
772         ptr++;
773       begin = ptr;
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)];
777       if (begin > end)
778         begin = end;
779
780       /* Strip quotes.  */
781       if (*begin == '"' && end[-1] == '"')
782         {
783           begin++;
784           end--;
785           *end = '\0';
786         }
787       if (begin > end)
788         return;
789
790       *values = malloc (end - begin + 1);
791       ptr2 = *values;
792       while (begin <= end)
793         *(ptr2++) = *(begin++);
794
795       keys++;
796       values++;
797       max--;
798     }
799
800   fclose (conf);
801   *keys = NULL;
802   *values = NULL;
803 }
804
805
806 char *
807 config_lookup (char *key)
808 {
809 #define MAX_KEYS 128
810   static int initialised = 0;
811   static char *keys[MAX_KEYS];
812   static char *values[MAX_KEYS];
813   int i;
814
815   if (initialised == 0)
816     {
817       initialised = 1;
818       config_init (keys, values, MAX_KEYS);
819
820 #if 0
821       MessageBox(g_hwndParent, "Configuration File:", 0, MB_OK);
822       i = 0;
823       while (keys[i])
824         {
825           char buf[256];
826           sprintf (buf, "%s=%s\r\n", keys[i], values[i]);
827           MessageBox (g_hwndParent, buf, 0, MB_OK);
828           i++;
829         }
830 #endif
831     }
832
833   i = 0;
834   while (keys[i])
835     {
836       if (!strcmp (keys[i], key))
837         return values[i];
838       i++;
839     }
840
841   return NULL;
842 }
843
844
845 void __declspec(dllexport)
846 config_fetch (HWND hwndParent, int string_size, char *variables,
847               stack_t **stacktop, extra_parameters_t *extra)
848 {
849   char key[256];
850   int err = 0;
851   char *value;
852
853   g_hwndParent = hwndParent;
854   EXDLL_INIT();
855
856   /* The expected stack layout: key.  */
857   if (popstring (key, sizeof (key)))
858     err = 1;
859   if (err)
860     {
861       setuservariable (INST_R0, "");
862       return;
863     }
864
865   value = config_lookup (key);
866
867   setuservariable (INST_R0, value == NULL ? "" : value);
868   return;
869 }
870
871
872 void __declspec(dllexport)
873 config_fetch_bool (HWND hwndParent, int string_size, char *variables,
874                    stack_t **stacktop, extra_parameters_t *extra)
875 {
876   char key[256];
877   int err = 0;
878   char *value;
879   int result;
880
881   g_hwndParent = hwndParent;
882   EXDLL_INIT();
883
884   /* The expected stack layout: key.  */
885   if (popstring (key, sizeof (key)))
886     err = 1;
887   if (err)
888     {
889       setuservariable (INST_R0, "");
890       return;
891     }
892
893   value = config_lookup (key);
894   if (value == NULL || *value == '\0')
895     {
896       setuservariable (INST_R0, "");
897       return;
898     }
899
900   result = 0;
901   if (!strcasecmp (value, "true")
902       || !strcasecmp (value, "yes")
903       || atoi (value) != 0)
904     result = 1;
905
906   setuservariable (INST_R0, result == 0 ? "0" : "1");
907   return;
908 }
909
910 \f
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.  */
914 char *
915 read_w32_registry_string (HKEY root, const char *dir, const char *name)
916 {
917   HKEY root_key;
918   HKEY key_handle;
919   DWORD n1, nbytes, type;
920   char *result = NULL;
921
922   root_key = root;
923   if (! root_key)
924     root_key = HKEY_CURRENT_USER;
925
926   if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
927     {
928       if (root)
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 */
933     }
934
935   nbytes = 1;
936   if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
937     if (root)
938       goto leave;
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))
944       goto leave;
945   }
946
947   result = malloc( (n1=nbytes+1) );
948
949   if( !result )
950     goto leave;
951   if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
952     free(result); result = NULL;
953     goto leave;
954   }
955   result[nbytes] = 0; /* make sure it is really a string  */
956
957  leave:
958   RegCloseKey( key_handle );
959   return result;
960 }
961
962
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.  */
967 #if 0
968 #define ENV_HK HKEY_CURRENT_USER
969 #define ENV_REG "Environment"
970 #endif
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
974    the past.  */
975 #define PATH_LENGTH_LIMIT 2047
976
977 void __declspec(dllexport)
978 path_add (HWND hwndParent, int string_size, char *variables,
979           stack_t **stacktop, extra_parameters_t *extra)
980 {
981   char dir[PATH_LENGTH_LIMIT];
982   char *path;
983   char *path_new;
984   int path_new_size;
985   char *comp;
986   const char delims[] = ";";
987   HKEY key_handle = 0;
988
989   g_hwndParent = hwndParent;
990   EXDLL_INIT();
991
992   setuservariable (INST_R0, "0");
993
994 /*   MessageBox (g_hwndParent, "XXX 1", 0, MB_OK); */
995
996   /* The expected stack layout: path component.  */
997   if (popstring (dir, sizeof (dir)))
998     return;
999
1000 /*   MessageBox (g_hwndParent, "XXX 2", 0, MB_OK); */
1001
1002   path = read_w32_registry_string (ENV_HK, ENV_REG, "Path");
1003   if (! path)
1004     {
1005       MessageBox (g_hwndParent, "No PATH variable found", 0, MB_OK);
1006       return;
1007     }
1008
1009 /*   MessageBox (g_hwndParent, "XXX 3", 0, MB_OK); */
1010
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)
1014     {
1015       MessageBox (g_hwndParent, "PATH env variable too big", 0, MB_OK);
1016       free (path);
1017       return;
1018     }
1019
1020 /*   MessageBox (g_hwndParent, "XXX 4", 0, MB_OK); */
1021
1022   path_new = malloc (path_new_size);
1023   if (!path_new)
1024     {
1025       free (path);
1026       return;
1027     }
1028
1029 /*   MessageBox (g_hwndParent, "XXX 5", 0, MB_OK); */
1030
1031   strcpy (path_new, path);
1032   strcat (path_new, ";");
1033   strcat (path_new, dir);
1034
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); */
1038
1039   /* Check if the directory already exists in the path.  */
1040   comp = strtok (path, delims);
1041   do
1042     {
1043 /*       MessageBox (g_hwndParent, comp, 0, MB_OK); */
1044
1045       if (!strcmp (comp, dir))
1046         {
1047           free (path);
1048           free (path_new);
1049           return;
1050         }
1051       comp = strtok (NULL, delims);
1052     }
1053   while (comp);
1054   free (path);
1055
1056 /*   MessageBox (g_hwndParent, "XXX 8", 0, MB_OK); */
1057
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);
1064   free (path_new);
1065
1066 /*   MessageBox (g_hwndParent, "XXX 9", 0, MB_OK); */
1067
1068   setuservariable (INST_R0, "1");
1069 }
1070
1071
1072 void __declspec(dllexport)
1073 path_remove (HWND hwndParent, int string_size, char *variables,
1074              stack_t **stacktop, extra_parameters_t *extra)
1075 {
1076   char dir[PATH_LENGTH_LIMIT];
1077   char *path;
1078   char *path_new;
1079   int path_new_size;
1080   char *comp;
1081   const char delims[] = ";";
1082   HKEY key_handle = 0;
1083   int changed = 0;
1084   int count = 0;
1085
1086   g_hwndParent = hwndParent;
1087   EXDLL_INIT();
1088
1089   setuservariable (INST_R0, "0");
1090
1091   /* The expected stack layout: path component.  */
1092   if (popstring (dir, sizeof (dir)))
1093     return;
1094
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);
1099   if (!path_new)
1100     {
1101       free (path);
1102       return;
1103     }
1104   path_new[0] = '\0';
1105
1106   /* Compose the new path.  */
1107   comp = strtok (path, delims);
1108   do
1109     {
1110       if (strcmp (comp, dir))
1111         {
1112           if (count != 0)
1113             strcat (path_new, ";");
1114           strcat (path_new, comp);
1115           count++;
1116         }
1117       else
1118         changed = 1;
1119
1120       comp = strtok (NULL, delims);
1121     }
1122   while (comp);
1123   free (path);
1124
1125   if (! changed)
1126     return;
1127
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);
1133   free (path_new);
1134
1135   setuservariable (INST_R0, "1");
1136 }