Merge branch 'dev' of feistymeow.org:feisty_meow into dev
[feisty_meow.git] / nucleus / library / application / registry_config.cpp
1 /*
2 *  Name   : registry_configurator                                             *
3 *  Author : Chris Koeritz                                                     *
4 **
5 * Copyright (c) 2004-$now By Author.  This program is free software; you can  *
6 * redistribute it and/or modify it under the terms of the GNU General Public  *
7 * License as published by the Free Software Foundation; either version 2 of   *
8 * the License or (at your option) any later version.  This is online at:      *
9 *     http://www.fsf.org/copyleft/gpl.html                                    *
10 * Please send any updates to: fred@gruntose.com                               *
11 */
12
13 #include "registry_config.h"
14
15 #include <basis/astring.h>
16 #include <basis/functions.h>
17 #include <basis/utf_conversion.h>
18 #include <structures/static_memory_gremlin.h>
19 #include <structures/string_array.h>
20 #include <structures/string_table.h>
21
22 using namespace basis;
23 using namespace filesystem;
24 using namespace structures;
25
26 #ifdef _MSC_VER
27
28   // this implementation only works on windows currently.
29 //hmmm: i suppose we could fake it with an ini file.
30
31   #include <shlwapi.h>
32 #endif
33
34 #undef LOG
35 #ifdef DEBUG_REGISTRY_CONFIGURATOR
36   #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
37 #else
38   #define LOG(s) {}
39 #endif
40
41 //#define DEBUG_REGISTRY_CONFIGURATOR
42   // uncomment for noisy version.
43
44 namespace configuration {
45
46 const int MAXIMUM_ENTRY_SIZE = 256 * KILOBYTE;
47   // arbitrary maximum for entries we'll read.
48
49 const int MAXIMUM_NAME_SIZE = 16384;
50   // the longest that value names can be in the registry.
51
52 // a default we hope never to see in the registry.
53 //SAFE_STATIC_CONST(astring, registry_configurator::reg_str_fake_default,
54  //   ("bogus_never_should_see"));
55 const astring &registry_configurator::reg_str_fake_default()
56 {
57   static astring _hidden = "bogus_never_should_see";
58   return _hidden;
59 }
60
61 registry_configurator::registry_configurator(registry_hives hive,
62       treatment_of_defaults behavior)
63 : configurator(behavior),
64   _hive(hive)
65 {}
66
67 registry_configurator::~registry_configurator()
68 {}
69
70 #ifndef __WIN32__
71 // fake the platform dependent names.
72 void *HKEY_CLASSES_ROOT = NULL;
73 void *HKEY_CURRENT_USER = NULL;
74 void *HKEY_LOCAL_MACHINE = NULL;
75 void *HKEY_USERS = NULL;
76 void *HKEY_CURRENT_CONFIG = NULL;
77 #endif
78
79 void *registry_configurator::translate_hive(registry_hives hive)
80 {
81   switch (hive) {
82     case hkey_classes_root: return HKEY_CLASSES_ROOT;
83     case hkey_current_user: return HKEY_CURRENT_USER;
84     case hkey_local_machine: return HKEY_LOCAL_MACHINE;
85     case hkey_users: return HKEY_USERS;
86     case hkey_current_config: return HKEY_CURRENT_CONFIG;
87     default: return 0;
88   }
89 }
90
91 astring registry_configurator::fix_section(const astring &section)
92 {
93   astring to_return = section;
94   for (int i = 0; i < to_return.length(); i++) {
95     if (to_return[i] == '/')
96       to_return[i] = '\\';
97   }
98   return to_return;
99 }
100
101 bool registry_configurator::put(const astring &section_in, const astring &entry,
102     const astring &to_store)
103 {
104   FUNCDEF("put");
105   astring section = fix_section(section_in);
106   if (!to_store.length()) return delete_entry(section, entry);
107   else if (!section.length()) return false;
108
109 #ifdef _MSC_VER
110   HKEY key;
111   long ret = RegOpenKeyEx((HKEY)translate_hive(_hive),
112       to_unicode_temp(section), 0, KEY_WRITE, &key);
113   if (ret != ERROR_SUCCESS) {
114     LOG("failed to open the key, trying to create it.");
115     DWORD dispose;  // the disposition of the call (created or existing).
116     ret = RegCreateKeyEx((HKEY)translate_hive(_hive),
117         to_unicode_temp(section), 0, NULL_POINTER, REG_OPTION_NON_VOLATILE,
118         KEY_ALL_ACCESS, NULL_POINTER, &key, &dispose);
119     if (ret != ERROR_SUCCESS) {
120       LOG("failed to create the key!!");
121       return false;
122     }
123   }
124
125   bool to_return = true;
126   ret = RegSetValueEx(key, to_unicode_temp(entry), 0, REG_SZ,
127       (byte *)to_store.s(), to_store.length() + 1);
128   if (ret != ERROR_SUCCESS) {
129     LOG(astring("failed to write the entry!"));
130     to_return = false;
131   }
132
133   RegCloseKey(key);
134   return to_return;
135 #else
136   return false;
137 #endif
138 }
139
140 bool registry_configurator::get(const astring &section_in, const astring &entry,
141     astring &found)
142 {
143   FUNCDEF("get");
144   found = "";
145   if (!section_in) return false;
146   if (!entry) {}  // not a problem.
147   astring section = fix_section(section_in);
148 #ifdef _MSC_VER
149   HKEY key;
150   long ret = RegOpenKeyEx((HKEY)translate_hive(_hive),
151       to_unicode_temp(section), 0, KEY_QUERY_VALUE, &key);
152   if (ret != ERROR_SUCCESS) {
153     LOG("failed to open the key!");
154     return false;
155   }
156
157   DWORD type_seen;
158   byte *data_seen = new byte[MAXIMUM_ENTRY_SIZE];
159   DWORD length = MAXIMUM_ENTRY_SIZE - 1;
160   ret = RegQueryValueEx(key, to_unicode_temp(entry), 0, &type_seen, data_seen,
161       &length);
162   if (ret != ERROR_SUCCESS) {
163     LOG(astring("failed to read the entry!"));
164     return false;
165   }
166
167   if (type_seen != REG_SZ) {
168     LOG(astring("entry found was not of string type!"));
169     RegCloseKey(key);
170     return false;
171   }
172
173   data_seen[MAXIMUM_ENTRY_SIZE - 1] = '\0';
174     // force last character to be null if data happened to be too big.
175   found = astring((char *)data_seen);
176
177   delete [] data_seen;
178
179   RegCloseKey(key);
180   return true;
181 #else
182   return false;
183 #endif
184 }
185
186 bool registry_configurator::get_section(const astring &section_in,
187     string_table &info)
188 {
189   FUNCDEF("get_section");
190   info.reset();
191   if (!section_in.length()) return false;
192   astring section = fix_section(section_in);
193 #ifdef _MSC_VER
194   HKEY key;
195   long ret = RegOpenKeyEx((HKEY)translate_hive(_hive),
196       to_unicode_temp(section), 0, KEY_QUERY_VALUE, &key);
197   if (ret != ERROR_SUCCESS) {
198     LOG("failed to open the key!");
199     return false;
200   }
201
202   DWORD type_seen;
203   byte *data_seen = new byte[MAXIMUM_ENTRY_SIZE];
204   flexichar *name_seen = new flexichar[MAXIMUM_NAME_SIZE];
205   DWORD name_length;
206   for (DWORD index = 0; true; index++) {
207     DWORD length = MAXIMUM_ENTRY_SIZE - 1;
208     name_length = MAXIMUM_NAME_SIZE - 1;
209     LONG ret = RegEnumValue(key, index, name_seen, &name_length, 0,
210         &type_seen, data_seen, &length);
211     if (ret != ERROR_SUCCESS) break;  // no entry at that index.
212     if (type_seen == REG_SZ) {
213       // found an entry successfully and it's the right type.
214       astring name = from_unicode_temp(name_seen);
215       astring content = from_unicode_temp((flexichar *)data_seen);
216       info.add(name, content);
217     }
218   }
219
220   delete [] data_seen;
221   delete [] name_seen;
222
223   RegCloseKey(key);
224
225   return true;
226 #else
227   return false;
228 #endif
229 }
230
231 bool registry_configurator::section_exists(const astring &section_in)
232 {
233   FUNCDEF("section_exists");
234   if (!section_in.length()) return false;
235   astring section = fix_section(section_in);
236 #ifdef _MSC_VER
237   HKEY key;
238   long ret = RegOpenKeyEx((HKEY)translate_hive(_hive),
239       to_unicode_temp(section), 0, KEY_QUERY_VALUE, &key);
240   if (ret != ERROR_SUCCESS) {
241     LOG("failed to open the key!");
242     return false;
243   }
244   RegCloseKey(key);
245   return true;
246 #else
247   return false;
248 #endif
249 }
250
251 bool registry_configurator::delete_section(const astring &section_in)
252 {
253   FUNCDEF("delete_section");
254   if (!section_in.length()) return false;
255   astring section = fix_section(section_in);
256 //if the key doesn't exist, should that be a failure?
257 #ifdef _MSC_VER
258   long ret = SHDeleteKey((HKEY)translate_hive(_hive),
259       to_unicode_temp(section));
260   if (ret != ERROR_SUCCESS) {
261     LOG("failed to delete the key!");
262     return false;
263   }
264   return true;
265 #else
266   return false;
267 #endif
268 }
269
270 bool registry_configurator::delete_entry(const astring &section_in,
271     const astring &entry)
272 {
273   FUNCDEF("delete_entry");
274   if (!section_in.length()) return false;
275   astring section = fix_section(section_in);
276   if (!entry) {}  // no problem.
277
278 #ifdef _MSC_VER
279   HKEY key;
280   long ret = RegOpenKeyEx((HKEY)translate_hive(_hive),
281       to_unicode_temp(section), 0, KEY_SET_VALUE, &key);
282   if (ret != ERROR_SUCCESS) {
283     LOG("failed to open the key!");
284     return false;
285   }
286
287   bool to_return = true;
288   ret = RegDeleteValue(key, to_unicode_temp(entry));
289   if (ret != ERROR_SUCCESS) {
290     LOG(astring("failed to delete the entry!"));
291     to_return = false;
292   }
293
294   RegCloseKey(key);
295   return to_return;
296 #else
297   return false;
298 #endif
299 }
300
301 bool registry_configurator::put_section(const astring &section_in,
302     const string_table &info)
303 {
304   if (!section_in) return false;
305   astring section = fix_section(section_in);
306   bool failures = false;
307   for (int i = 0; i < info.symbols(); i++) {
308     bool worked = put(section, info.name(i), info[i]);
309     if (!worked) failures = true;
310   }
311   return !failures;
312 }
313
314 } // namespace
315