f8ec0a115c513c487772cc214a0fc496b58e62b8
[gpgol.git] / src / config-dialog.c
1 /* config-dialog.c
2  *      Copyright (C) 2005 g10 Code GmbH
3  *      Copyright (C) 2003 Timo Schulz
4  *
5  * This file is part of GPGol.
6  * 
7  * GPGol is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  * 
12  * GPGol is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  * 
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  */
22
23 #include <config.h>
24
25 #include <windows.h>
26 #include <shellapi.h>
27 #include <shlobj.h>
28 #include <time.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <gpgme.h>
32
33 #include "gpgol-ids.h"
34 #include "intern.h"
35
36 /* Registry path to GnuPG */
37 #define REGPATH "Software\\GNU\\GnuPG"
38
39 /* Registry path to store plugin settings */
40 #define GPGOL_REGPATH "Software\\GNU\\GPGol"
41
42 static char*
43 get_open_file_name (const char *dir, const char *title)
44 {
45   static char fname[MAX_PATH+1];
46   OPENFILENAME ofn;
47
48   memset (&ofn, 0, sizeof (ofn));
49   memset (fname, 0, sizeof (fname));
50   ofn.hwndOwner = GetDesktopWindow ();
51   ofn.hInstance = glob_hinst;
52   ofn.lpstrTitle = title;
53   ofn.lStructSize = sizeof (ofn);
54   ofn.lpstrInitialDir = dir;
55   ofn.lpstrFilter = "EXE-Files (*.EXE)\0*.EXE\0\0";
56   ofn.lpstrFile = fname;
57   ofn.nMaxFile = sizeof (fname)-1;
58   if (GetOpenFileName (&ofn) == FALSE)
59     return NULL;
60   return fname;
61 }
62
63
64 static void 
65 SHFree (void *p) 
66 {         
67     IMalloc *pm;         
68     SHGetMalloc (&pm);
69     if (pm) {
70         pm->lpVtbl->Free(pm,p);
71         pm->lpVtbl->Release(pm);         
72     } 
73
74
75
76 /* Open the common dialog to select a folder. Caller has to free the string. */
77 static char*
78 get_folder (const char *title)
79 {
80   char fname[MAX_PATH+1];
81   BROWSEINFO bi;
82   ITEMIDLIST * il;
83   char *path = NULL;
84
85   memset (&bi, 0, sizeof (bi));
86   memset (fname, 0, sizeof (fname));
87   bi.hwndOwner = GetDesktopWindow ();
88   bi.lpszTitle = title;
89   il = SHBrowseForFolder (&bi);
90   if (il != NULL)
91     {
92       SHGetPathFromIDList (il, fname);
93       path = xstrdup (fname);
94       SHFree (il);
95     }
96   return path;
97 }
98
99
100 static int
101 load_config_value_ext (char **val)
102 {
103   static char buf[MAX_PATH+64];
104   
105   /* MSDN: This buffer must be at least MAX_PATH characters in size. */
106   memset (buf, 0, sizeof (buf));
107   if (w32_shgetfolderpath (NULL, CSIDL_APPDATA/*|CSIDL_FLAG_CREATE*/, 
108                            NULL, 0, buf) < 0)
109     return -1;
110   strcat (buf, "\\gnupg");
111   if (GetFileAttributes (buf) == 0xFFFFFFFF)
112     return -1;
113   *val = buf;
114   return 0;
115 }
116
117
118 static char*
119 expand_path (const char *path)
120 {
121     DWORD len;
122     char *p;
123
124     len = ExpandEnvironmentStrings (path, NULL, 0);
125     if (!len)
126         return NULL;
127     len += 1;
128     p = xcalloc (1, len+1);
129     if (!p)
130         return NULL;
131     len = ExpandEnvironmentStrings (path, p, len);
132     if (!len) {
133         xfree (p);
134         return NULL;
135     }
136     return p; 
137 }
138
139 static int
140 load_config_value (HKEY hk, const char *path, const char *key, char **val)
141 {
142     HKEY h;
143     DWORD size=0, type;
144     int ec;
145
146     if (hk == NULL)
147         hk = HKEY_CURRENT_USER;
148     ec = RegOpenKeyEx (hk, path, 0, KEY_READ, &h);
149     if (ec != ERROR_SUCCESS)
150         return load_config_value_ext (val);
151
152     ec = RegQueryValueEx(h, key, NULL, &type, NULL, &size);
153     if (ec != ERROR_SUCCESS) {
154         RegCloseKey (h);
155         return -1;
156     }
157     if (type == REG_EXPAND_SZ) {
158         char tmp[256]; /* XXX: do not use a static buf */
159         RegQueryValueEx (h, key, NULL, NULL, (BYTE*)tmp, &size);
160         *val = expand_path (tmp);
161     }
162     else {
163         *val = xcalloc(1, size+1);
164         ec = RegQueryValueEx (h, key, NULL, &type, (BYTE*)*val, &size);
165         if (ec != ERROR_SUCCESS) {
166             xfree (*val);
167             *val = NULL;
168             RegCloseKey (h);
169             return -1;
170         }
171     }
172     RegCloseKey (h);
173     return 0;
174 }
175
176
177 static int
178 store_config_value (HKEY hk, const char *path, const char *key, const char *val)
179 {
180     HKEY h;
181     int type = REG_SZ;
182     int ec;
183
184     if (hk == NULL)
185         hk = HKEY_CURRENT_USER;
186     ec = RegOpenKeyEx (hk, path, 0, KEY_ALL_ACCESS, &h);
187     if (ec != ERROR_SUCCESS)
188         return -1;
189     if (strchr (val, '%'))
190         type = REG_EXPAND_SZ;
191     ec = RegSetValueEx (h, key, 0, type, (const BYTE*)val, strlen (val));
192     if (ec != ERROR_SUCCESS) {
193         RegCloseKey(h);
194         return -1;
195     }
196     RegCloseKey(h);
197     return 0;
198 }
199
200
201 static int
202 does_folder_exist (const char *path)
203 {
204     int attrs = GetFileAttributes (path);
205     int err = 0;
206
207     if (attrs == 0xFFFFFFFF)
208         err = -1;
209     else if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
210         err = -1;
211     if (err != 0) {
212         const char *fmt = "\"%s\" either does not exist or is not a directory";
213         char *p = xmalloc (strlen (fmt) + strlen (path) + 2 + 2);
214         sprintf (p, fmt, path);
215         MessageBox (NULL, p, "Config Error", MB_ICONERROR|MB_OK);
216         xfree (p);
217     }
218     return err;
219 }
220
221
222 static int
223 does_file_exist (const char *name, int is_file)
224 {
225     struct stat st;
226     const char *s;
227     char *p, *name2;
228     int err = 0;
229
230     /* check WinPT specific flags */
231     if ((p=strstr (name, "--keymanager"))) {
232         name2 = xcalloc (1, (p-name)+2);
233         strncpy (name2, name, (p-name)-1);
234     }
235     else
236         name2 = xstrdup (name);
237
238     if (stat (name2, &st) == -1) {
239         s = "\"%s\" does not exist.";
240         p = xmalloc (strlen (s) + strlen (name2) + 2);
241         sprintf (p, s, name2);
242         MessageBox (NULL, p, "Config Error", MB_ICONERROR|MB_OK);
243         err = -1;
244     }
245     else if (is_file && !(st.st_mode & _S_IFREG)) {
246         s = "\"%s\" is not a regular file.";
247         p = xmalloc (strlen (s) + strlen (name2) + 2);
248         sprintf (p, s, name2);
249         MessageBox (NULL, p, "Config Error", MB_ICONERROR|MB_OK);
250         err = -1;
251     }
252     xfree (name2);
253     xfree (p);
254     return err;
255 }
256
257
258 static void
259 error_box (const char *title)
260 {       
261   TCHAR buf[256];
262   DWORD last_err;
263
264   last_err = GetLastError ();
265   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_err, 
266                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
267                  buf, sizeof (buf)-1, NULL);
268   MessageBox (NULL, buf, title, MB_OK);
269 }
270
271
272 static BOOL CALLBACK
273 config_dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
274 {
275     char *buf = NULL;
276     char name[MAX_PATH+1];
277     int n;
278
279     switch (msg) {
280     case WM_INITDIALOG:
281         center_window (dlg, 0);
282         if (!load_config_value (NULL, REGPATH, "gpgProgram", &buf)) {
283             SetDlgItemText (dlg, IDC_OPT_GPGPRG, buf);
284             xfree (buf); 
285             buf=NULL;
286         }
287         if (!load_config_value (NULL, REGPATH, "HomeDir", &buf)) {
288             SetDlgItemText (dlg, IDC_OPT_HOMEDIR, buf);
289             xfree (buf); 
290             buf=NULL;
291         }
292         if (!load_config_value (NULL, REGPATH, "keyManager", &buf)) {
293             SetDlgItemText (dlg, IDC_OPT_KEYMAN, buf);
294             xfree (buf);
295             buf=NULL;
296         }
297         break;
298
299     case WM_COMMAND:
300         switch (LOWORD (wparam)) {
301         case IDC_OPT_SELPRG:
302             buf = get_open_file_name (NULL, "Select GnuPG Binary");
303             if (buf && *buf)
304                 SetDlgItemText(dlg, IDC_OPT_GPGPRG, buf);
305             break;
306
307         case IDC_OPT_SELHOMEDIR:
308             buf = get_folder ("Select GnuPG Home Directory");
309             if (buf && *buf)
310                 SetDlgItemText(dlg, IDC_OPT_HOMEDIR, buf);
311             xfree (buf);
312             break;
313
314         case IDC_OPT_SELKEYMAN:
315             buf = get_open_file_name (NULL, "Select GnuPG Key Manager");
316             if (buf && *buf)
317                 SetDlgItemText (dlg, IDC_OPT_KEYMAN, buf);
318             break;
319
320         case IDOK:
321             n = GetDlgItemText (dlg, IDC_OPT_GPGPRG, name, MAX_PATH-1);
322             if (n > 0) {
323                 if (does_file_exist (name, 1))
324                     return FALSE;
325                 if (store_config_value (NULL, REGPATH, "gpgProgram", name))
326                     error_box ("GPG Config");
327             }
328             n = GetDlgItemText (dlg, IDC_OPT_KEYMAN, name, MAX_PATH-1);
329             if (n > 0) {
330                 if (does_file_exist (name, 1))
331                     return FALSE;
332                 if (store_config_value (NULL, REGPATH, "keyManager", name))
333                     error_box ("GPG Config");
334             }
335             n = GetDlgItemText (dlg, IDC_OPT_HOMEDIR, name, MAX_PATH-1);
336             if (n > 0) {
337                 if (does_folder_exist (name))
338                     return FALSE;
339                 if (store_config_value (NULL, REGPATH, "HomeDir", name))
340                     error_box ("GPG Config");
341             }
342             EndDialog (dlg, TRUE);
343             break;
344         }
345         break;
346     }
347
348     return FALSE;
349 }
350
351
352 /* Display GPG configuration dialog. */
353 void
354 config_dialog_box (HWND parent)
355 {
356   int resid=0;
357
358   switch (GetUserDefaultLangID ())
359     {
360     case 0x0407:    resid = IDD_OPT_DE;break;
361     default:        resid = IDD_OPT; break;
362     }
363
364   if (parent == NULL)
365     parent = GetDesktopWindow ();
366   DialogBoxParam (glob_hinst, (LPCTSTR)resid, parent,
367                   config_dlg_proc, 0);
368 }
369
370
371 /* Start the key manager specified by the registry entry 'keyManager'. */
372 int
373 start_key_manager (void)
374 {
375   PROCESS_INFORMATION pi;
376   STARTUPINFO si;
377   char *p;
378   char *keyman = NULL;
379   
380   if (load_config_value (NULL, REGPATH, "keyManager", &keyman))
381     {
382       /* In case we did not found a registry entry we try to locate
383          the keymanager in the same directory as the gpgme backend. */
384       gpgme_engine_info_t info;
385
386       if (gpgme_get_engine_info (&info))
387         return -1;
388
389       while (info && info->protocol != GPGME_PROTOCOL_OpenPGP)
390         info = info->next;
391       if (info && info->file_name && *info->file_name)
392         {
393           keyman = xmalloc (strlen (info->file_name) + 10);
394           strcpy (keyman, info->file_name);
395           for (p=keyman; *p; p++)
396             if (*p == '/')
397               *p = '\\';
398           p = strrchr (keyman, '\\');
399           if (!p)
400             {
401               xfree (keyman);
402               return -1;
403             }
404           strcpy (p+1, "winpt.exe");
405           if (access (keyman, F_OK))
406             {
407               strcpy (p+1, "gpa.exe");
408               if (access (keyman, F_OK))
409                 {
410                   xfree (keyman);
411                   return -1;
412                 }
413             }
414         }
415     }
416   
417   /* Create startup info for the keymanager process. */
418   memset (&si, 0, sizeof (si));
419   si.cb = sizeof (STARTUPINFO);
420   si.dwFlags = STARTF_USESHOWWINDOW;
421   si.wShowWindow = SW_SHOW;
422   
423   if (CreateProcess (NULL, keyman,
424                      NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE,
425                      NULL, NULL, &si, &pi) == TRUE)
426     {
427       CloseHandle (pi.hProcess);
428       CloseHandle (pi.hThread);
429     }
430   
431   xfree (keyman);
432   return 0;
433 }
434
435
436 /* Store a key in the registry with the key given by @key and the 
437    value @value. */
438 int
439 store_extension_value (const char *key, const char *val)
440 {
441     return store_config_value (HKEY_CURRENT_USER, GPGOL_REGPATH, key, val);
442 }
443
444 /* Load a key from the registry with the key given by @key. The value is
445    returned in @val and needs to freed by the caller. */
446 int
447 load_extension_value (const char *key, char **val)
448 {
449     return load_config_value (HKEY_CURRENT_USER, GPGOL_REGPATH, key, val);
450 }