Fix "Remove application" for applications that had some custom
[wine] / programs / winecfg / winecfg.c
1 /*
2  * WineCfg configuration management
3  *
4  * Copyright 2002 Jaco Greeff
5  * Copyright 2003 Dimitrie O. Paun
6  * Copyright 2003-2004 Mike Hearn
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * TODO:
23  *  - Use unicode
24  *  - Icons in listviews/icons
25  *  - Better add app dialog, scan c: for EXE files and add to list in background
26  *  - Use [GNOME] HIG style groupboxes rather than win32 style (looks nicer, imho)
27  *
28  */
29
30 #include <assert.h>
31 #include <stdio.h>
32 #include <limits.h>
33 #include <windows.h>
34 #include <winreg.h>
35 #include <wine/debug.h>
36 #include <wine/list.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
39
40 #include "winecfg.h"
41
42 HKEY config_key = NULL;
43
44
45
46 /* this is called from the WM_SHOWWINDOW handlers of each tab page.
47  *
48  * it's a nasty hack, necessary because the property sheet insists on resetting the window title
49  * to the title of the tab, which is utterly useless. dropping the property sheet is on the todo list.
50  */
51 void set_window_title(HWND dialog)
52 {
53     char *newtitle;
54
55     /* update the window title  */
56     if (current_app)
57     {
58         const char *template = "Wine Configuration for %s";
59         newtitle = HeapAlloc(GetProcessHeap(), 0, strlen(template) + strlen(current_app) + 1);
60         sprintf(newtitle, template, current_app);
61     }
62     else
63     {
64         newtitle = strdupA("Wine Configuration");
65     }
66
67     WINE_TRACE("setting title to %s\n", newtitle);
68     SendMessage(GetParent(dialog), PSM_SETTITLE, 0, (LPARAM) newtitle);
69     HeapFree(GetProcessHeap(), 0, newtitle);
70 }
71
72
73 /**
74  * get_config_key: Retrieves a configuration value from the registry
75  *
76  * char *subkey : the name of the config section
77  * char *name : the name of the config value
78  * char *default : if the key isn't found, return this value instead
79  *
80  * Returns a buffer holding the value if successful, NULL if
81  * not. Caller is responsible for releasing the result.
82  *
83  */
84 static char *get_config_key (HKEY root, const char *subkey, const char *name, const char *def)
85 {
86     LPSTR buffer = NULL;
87     DWORD len;
88     HKEY hSubKey = NULL;
89     DWORD res;
90
91     WINE_TRACE("subkey=%s, name=%s, def=%s\n", subkey, name, def);
92
93     res = RegOpenKey(root, subkey, &hSubKey);
94     if (res != ERROR_SUCCESS)
95     {
96         if (res == ERROR_FILE_NOT_FOUND)
97         {
98             WINE_TRACE("Section key not present - using default\n");
99             return def ? strdupA(def) : NULL;
100         }
101         else
102         {
103             WINE_ERR("RegOpenKey failed on wine config key (res=%ld)\n", res);
104         }
105         goto end;
106     }
107
108     res = RegQueryValueExA(hSubKey, name, NULL, NULL, NULL, &len);
109     if (res == ERROR_FILE_NOT_FOUND)
110     {
111         WINE_TRACE("Value not present - using default\n");
112         buffer = def ? strdupA(def) : NULL;
113         goto end;
114     } else if (res != ERROR_SUCCESS)
115     {
116         WINE_ERR("Couldn't query value's length (res=%ld)\n", res);
117         goto end;
118     }
119
120     buffer = HeapAlloc(GetProcessHeap(), 0, len + 1);
121
122     RegQueryValueEx(hSubKey, name, NULL, NULL, (LPBYTE) buffer, &len);
123
124     WINE_TRACE("buffer=%s\n", buffer);
125 end:
126     if (hSubKey && hSubKey != root) RegCloseKey(hSubKey);
127
128     return buffer;
129 }
130
131 /**
132  * set_config_key: convenience wrapper to set a key/value pair
133  *
134  * const char *subKey : the name of the config section
135  * const char *valueName : the name of the config value
136  * const char *value : the value to set the configuration key to
137  *
138  * Returns 0 on success, non-zero otherwise
139  *
140  * If valueName or value is NULL, an empty section will be created
141  */
142 static int set_config_key(HKEY root, const char *subkey, const char *name, const void *value, DWORD type) {
143     DWORD res = 1;
144     HKEY key = NULL;
145
146     WINE_TRACE("subkey=%s: name=%s, value=%p, type=%ld\n", subkey, name, value, type);
147
148     assert( subkey != NULL );
149
150     if (subkey[0])
151     {
152         res = RegCreateKey(root, subkey, &key);
153         if (res != ERROR_SUCCESS) goto end;
154     }
155     else key = root;
156     if (name == NULL || value == NULL) goto end;
157
158     switch (type)
159     {
160         case REG_SZ: res = RegSetValueEx(key, name, 0, REG_SZ, value, strlen(value) + 1); break;
161         case REG_DWORD: res = RegSetValueEx(key, name, 0, REG_DWORD, value, sizeof(DWORD)); break;
162     }
163     if (res != ERROR_SUCCESS) goto end;
164
165     res = 0;
166 end:
167     if (key && key != root) RegCloseKey(key);
168     if (res != 0) WINE_ERR("Unable to set configuration key %s in section %s, res=%ld\n", name, subkey, res);
169     return res;
170 }
171
172 /* removes the requested value from the registry, however, does not
173  * remove the section if empty. Returns S_OK (0) on success.
174  */
175 static HRESULT remove_value(HKEY root, const char *subkey, const char *name)
176 {
177     HRESULT hr;
178     HKEY key;
179
180     WINE_TRACE("subkey=%s, name=%s\n", subkey, name);
181
182     hr = RegOpenKey(root, subkey, &key);
183     if (hr != S_OK) return hr;
184
185     hr = RegDeleteValue(key, name);
186     if (hr != ERROR_SUCCESS) return hr;
187
188     return S_OK;
189 }
190
191 /* removes the requested subkey from the registry, assuming it exists */
192 static LONG remove_path(HKEY root, char *section) {
193     HKEY branch_key;
194     DWORD max_sub_key_len;
195     DWORD subkeys;
196     DWORD curr_len;
197     LONG ret = ERROR_SUCCESS;
198     long int i;
199     char *buffer;
200
201     WINE_TRACE("section=%s\n", section);
202
203     if ((ret = RegOpenKey(root, section, &branch_key)) != ERROR_SUCCESS)
204         return ret;
205
206     /* get size information and resize the buffers if necessary */
207     if ((ret = RegQueryInfoKey(branch_key, NULL, NULL, NULL,
208                                &subkeys, &max_sub_key_len,
209                                NULL, NULL, NULL, NULL, NULL, NULL
210                               )) != ERROR_SUCCESS)
211         return ret;
212
213     curr_len = strlen(section);
214     buffer = HeapAlloc(GetProcessHeap(), 0, max_sub_key_len + curr_len + 1);
215     strcpy(buffer, section);
216
217     buffer[curr_len] = '\\';
218     for (i = subkeys - 1; i >= 0; i--)
219     {
220         DWORD buf_len = max_sub_key_len - curr_len - 1;
221
222         ret = RegEnumKeyEx(branch_key, i, buffer + curr_len + 1,
223                            &buf_len, NULL, NULL, NULL, NULL);
224         if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
225             ret != ERROR_NO_MORE_ITEMS)
226             break;
227         else
228             remove_path(root, buffer);
229     }
230     HeapFree(GetProcessHeap(), 0, buffer);
231     RegCloseKey(branch_key);
232
233     return RegDeleteKey(root, section);
234 }
235
236
237 /* ========================================================================= */
238
239 /* This code exists for the following reasons:
240  *
241  * - It makes working with the registry easier
242  * - By storing a mini cache of the registry, we can more easily implement
243  *   cancel/revert and apply. The 'settings list' is an overlay on top of
244  *   the actual registry data that we can write out at will.
245  *
246  * Rather than model a tree in memory, we simply store each absolute (rooted
247  * at the config key) path.
248  *
249  */
250
251 struct setting
252 {
253     struct list entry;
254     HKEY root;    /* the key on which path is rooted */
255     char *path;   /* path in the registry rooted at root  */
256     char *name;   /* name of the registry value. if null, this means delete the key  */
257     char *value;  /* contents of the registry value. if null, this means delete the value  */
258     DWORD type;   /* type of registry value. REG_SZ or REG_DWORD for now */
259 };
260
261 struct list *settings;
262
263 static void free_setting(struct setting *setting)
264 {
265     assert( setting != NULL );
266     assert( setting->path );
267
268     WINE_TRACE("destroying %p: %s\n", setting, setting->path);
269     
270     HeapFree(GetProcessHeap(), 0, setting->path);
271     HeapFree(GetProcessHeap(), 0, setting->name);
272     HeapFree(GetProcessHeap(), 0, setting->value);
273
274     list_remove(&setting->entry);
275
276     HeapFree(GetProcessHeap(), 0, setting);
277 }
278
279 /**
280  * Returns the contents of the value at path. If not in the settings
281  * list, it will be fetched from the registry - failing that, the
282  * default will be used.
283  *
284  * If already in the list, the contents as given there will be
285  * returned. You are expected to HeapFree the result.
286  */
287 char *get_reg_key(HKEY root, const char *path, const char *name, const char *def)
288 {
289     struct list *cursor;
290     struct setting *s;
291     char *val;
292
293     WINE_TRACE("path=%s, name=%s, def=%s\n", path, name, def);
294
295     /* check if it's in the list */
296     LIST_FOR_EACH( cursor, settings )
297     {
298         s = LIST_ENTRY(cursor, struct setting, entry);
299
300         if (root != s->root) continue;
301         if (strcasecmp(path, s->path) != 0) continue;
302         if (strcasecmp(name, s->name) != 0) continue;
303
304         WINE_TRACE("found %s:%s in settings list, returning %s\n", path, name, s->value);
305         return s->value ? strdupA(s->value) : NULL;
306     }
307
308     /* no, so get from the registry */
309     val = get_config_key(root, path, name, def);
310
311     WINE_TRACE("returning %s\n", val);
312
313     return val;
314 }
315
316 /**
317  * Used to set a registry key.
318  *
319  * path is rooted at the config key, ie use "Version" or
320  * "AppDefaults\\fooapp.exe\\Version". You can use keypath()
321  * to get such a string.
322  *
323  * name is the value name, or NULL to delete the path.
324  *
325  * value is what to set the value to, or NULL to delete it.
326  *
327  * type is REG_SZ or REG_DWORD.
328  *
329  * These values will be copied when necessary.
330  */
331 static void set_reg_key_ex(HKEY root, const char *path, const char *name, const void *value, DWORD type)
332 {
333     struct list *cursor;
334     struct setting *s;
335
336     assert( path != NULL );
337
338     WINE_TRACE("path=%s, name=%s, value=%p\n", path, name, value);
339
340     /* firstly, see if we already set this setting  */
341     LIST_FOR_EACH( cursor, settings )
342     {
343         struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
344
345         if (root != s->root) continue;
346         if (strcasecmp(s->path, path) != 0) continue;
347         if ((s->name && name) && strcasecmp(s->name, name) != 0) continue;
348
349         /* are we attempting a double delete? */
350         if (!s->name && !name) return;
351
352         /* do we want to undelete this key? */
353         if (!s->name && name) s->name = strdupA(name);
354
355         /* yes, we have already set it, so just replace the content and return  */
356         HeapFree(GetProcessHeap(), 0, s->value);
357         s->type = type;
358         switch (type)
359         {
360             case REG_SZ:
361                 s->value = value ? strdupA(value) : NULL;
362                 break;
363             case REG_DWORD:
364                 s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
365                 memcpy( s->value, value, sizeof(DWORD) );
366                 break;
367         }
368
369         /* are we deleting this key? this won't remove any of the
370          * children from the overlay so if the user adds it again in
371          * that session it will appear to undelete the settings, but
372          * in reality only the settings actually modified by the user
373          * in that session will be restored. we might want to fix this
374          * corner case in future by actually deleting all the children
375          * here so that once it's gone, it's gone.
376          */
377         if (!name) s->name = NULL;
378
379         return;
380     }
381
382     /* otherwise add a new setting for it  */
383     s = HeapAlloc(GetProcessHeap(), 0, sizeof(struct setting));
384     s->root  = root;
385     s->path  = strdupA(path);
386     s->name  = name  ? strdupA(name)  : NULL;
387     s->type  = type;
388     switch (type)
389     {
390         case REG_SZ:
391             s->value = value ? strdupA(value) : NULL;
392             break;
393         case REG_DWORD:
394             s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
395             memcpy( s->value, value, sizeof(DWORD) );
396             break;
397     }
398
399     list_add_tail(settings, &s->entry);
400 }
401
402 void set_reg_key(HKEY root, const char *path, const char *name, const char *value)
403 {
404     set_reg_key_ex(root, path, name, value, REG_SZ);
405 }
406
407 void set_reg_key_dword(HKEY root, const char *path, const char *name, DWORD value)
408 {
409     set_reg_key_ex(root, path, name, &value, REG_DWORD);
410 }
411
412 /**
413  * enumerates the value names at the given path, taking into account
414  * the changes in the settings list.
415  *
416  * you are expected to HeapFree each element of the array, which is null
417  * terminated, as well as the array itself.
418  */
419 char **enumerate_values(HKEY root, char *path)
420 {
421     HKEY key;
422     DWORD res, i = 0;
423     char **values = NULL;
424     int valueslen = 0;
425     struct list *cursor;
426
427     res = RegOpenKey(root, path, &key);
428     if (res == ERROR_SUCCESS)
429     {
430         while (TRUE)
431         {
432             char name[1024];
433             DWORD namesize = sizeof(name);
434             BOOL removed = FALSE;
435
436             /* find out the needed size, allocate a buffer, read the value  */
437             if ((res = RegEnumValue(key, i, name, &namesize, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
438                 break;
439
440             WINE_TRACE("name=%s\n", name);
441
442             /* check if this value name has been removed in the settings list  */
443             LIST_FOR_EACH( cursor, settings )
444             {
445                 struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
446                 if (strcasecmp(s->path, path) != 0) continue;
447                 if (strcasecmp(s->name, name) != 0) continue;
448
449                 if (!s->value)
450                 {
451                     WINE_TRACE("this key has been removed, so skipping\n");
452                     removed = TRUE;
453                     break;
454                 }
455             }
456
457             if (removed)            /* this value was deleted by the user, so don't include it */
458             {
459                 HeapFree(GetProcessHeap(), 0, name);
460                 i++;
461                 continue;
462             }
463
464             /* grow the array if necessary, add buffer to it, iterate  */
465             if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(char*) * (valueslen + 1));
466             else values = HeapAlloc(GetProcessHeap(), 0, sizeof(char*));
467
468             values[valueslen++] = strdupA(name);
469             WINE_TRACE("valueslen is now %d\n", valueslen);
470             i++;
471         }
472     }
473     else
474     {
475         WINE_WARN("failed opening registry key %s, res=0x%lx\n", path, res);
476     }
477
478     WINE_TRACE("adding settings in list but not registry\n");
479
480     /* now we have to add the values that aren't in the registry but are in the settings list */
481     LIST_FOR_EACH( cursor, settings )
482     {
483         struct setting *setting = LIST_ENTRY(cursor, struct setting, entry);
484         BOOL found = FALSE;
485
486         if (strcasecmp(setting->path, path) != 0) continue;
487
488         if (!setting->value) continue;
489
490         for (i = 0; i < valueslen; i++)
491         {
492             if (strcasecmp(setting->name, values[i]) == 0)
493             {
494                 found = TRUE;
495                 break;
496             }
497         }
498
499         if (found) continue;
500
501         WINE_TRACE("%s in list but not registry\n", setting->name);
502
503         /* otherwise it's been set by the user but isn't in the registry */
504         if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(char*) * (valueslen + 1));
505         else values = HeapAlloc(GetProcessHeap(), 0, sizeof(char*));
506
507         values[valueslen++] = strdupA(setting->name);
508     }
509
510     WINE_TRACE("adding null terminator\n");
511     if (values)
512     {
513         values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(char*) * (valueslen + 1));
514         values[valueslen] = NULL;
515     }
516
517     RegCloseKey(key);
518
519     return values;
520 }
521
522 /**
523  * returns true if the given key/value pair exists in the registry or
524  * has been written to.
525  */
526 BOOL reg_key_exists(HKEY root, const char *path, const char *name)
527 {
528     char *val = get_reg_key(root, path, name, NULL);
529
530     if (val)
531     {
532         HeapFree(GetProcessHeap(), 0, val);
533         return TRUE;
534     }
535
536     return FALSE;
537 }
538
539 static void process_setting(struct setting *s)
540 {
541     if (s->value)
542     {
543         WINE_TRACE("Setting %s:%s to '%s'\n", s->path, s->name, s->value);
544         set_config_key(s->root, s->path, s->name, s->value, s->type);
545     }
546     else
547     {
548         /* NULL name means remove that path/section entirely */
549         if (s->path && s->name) remove_value(s->root, s->path, s->name);
550         else if (s->path && !s->name) remove_path(s->root, s->path);
551     }
552 }
553
554 void apply(void)
555 {
556     if (list_empty(settings)) return; /* we will be called for each page when the user clicks OK */
557
558     WINE_TRACE("()\n");
559
560     while (!list_empty(settings))
561     {
562         struct setting *s = (struct setting *) list_head(settings);
563         process_setting(s);
564         free_setting(s);
565     }
566 }
567
568 /* ================================== utility functions ============================ */
569
570 char *current_app = NULL; /* the app we are currently editing, or NULL if editing global */
571
572 /* returns a registry key path suitable for passing to addTransaction  */
573 char *keypath(const char *section)
574 {
575     static char *result = NULL;
576
577     HeapFree(GetProcessHeap(), 0, result);
578
579     if (current_app)
580     {
581         result = HeapAlloc(GetProcessHeap(), 0, strlen("AppDefaults\\") + strlen(current_app) + 2 /* \\ */ + strlen(section) + 1 /* terminator */);
582         sprintf(result, "AppDefaults\\%s", current_app);
583         if (section[0]) sprintf( result + strlen(result), "\\%s", section );
584     }
585     else
586     {
587         result = strdupA(section);
588     }
589
590     return result;
591 }
592
593 void PRINTERROR(void)
594 {
595         LPSTR msg;
596
597         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
598                        0, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
599                        (LPSTR)&msg, 0, NULL);
600
601         /* eliminate trailing newline, is this a Wine bug? */
602         *(strrchr(msg, '\r')) = '\0';
603         
604         WINE_TRACE("error: '%s'\n", msg);
605 }
606
607 int initialize(void) {
608     DWORD res = RegCreateKey(HKEY_CURRENT_USER, WINE_KEY_ROOT, &config_key);
609
610     if (res != ERROR_SUCCESS) {
611         WINE_ERR("RegOpenKey failed on wine config key (%ld)\n", res);
612         return 1;
613     }
614
615     /* we could probably just have the list as static data  */
616     settings = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
617     list_init(settings);
618
619     return 0;
620 }