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