1 /********************************************************************************
2 *                                                                               *
3 *                           R e g i s t r y   C l a s s                         *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2020 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (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                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXStream.h"
28 #include "FXObject.h"
29 #include "FXString.h"
30 #include "FXSystem.h"
31 #include "FXProcess.h"
32 #include "FXPath.h"
33 #include "FXStat.h"
34 #include "FXFile.h"
35 #include "FXDir.h"
36 #include "FXStringDictionary.h"
37 #include "FXRegistry.h"
38 
39 /*
40   Notes:
41 
42   - The FOX settings tree stores configuration data for FOX-based applications; it
43     is organized as follows:
44 
45     SettingsRoot/foxrc                  The "foxrc" file stores common configuration
46                                         settings for all FOX-based applications,
47                                         such as colors and file bindings.
48 
49     SettingsRoot/Vendor/Vendorrc        The Vendorrc file is common to all applications
50                                         from a particular organization, so that common
51                                         configurable attributes may be given consistent
52                                         values easily.
53 
54     SettingsRoot/Vendor/Applicationrc   The Applicationrc file stores configuration data
55                                         unique to a specific application only.
56 
57     SettingsRoot/Applicationrc          If no Vendor name is specified, the Applicationrc
58                                         file is located at the toplevel of the SettingsRoot.
59 
60   - A System-Wide SettingsRoot may be located in a read-only, system adminstrator designated
61     place, to be shared by all users on a particular installation.  For example, this may store
62     license-keys, references to online documentation, and other installation-time parameters.
63 
64   - A Per-User SettingsRoot is typically located in a user's home directory.
65     Configurations stored in these files will be merged with the System-Wide configurations.
66     Parameters given different values in the Per-User files will override those in the
67     System-Wide ones.
68 
69   - Rationale:
70 
71       1)    When installing an application, simply copy "seed" registry files to
72             the System-Wide SettingsRoot; having a subdirectory Vendor prevents
73             clobbering other people's registry files, even if their application
74             has the same name.
75 
76       2)    System-Wide registry files are, as a matter of principle, read-only.
77 
78       3)    System-Wide registry files are loaded first, and Per-User registry
79             files are loaded on top of that.
80 
81       4)    Registry files loaded later will take precedence over those loaded
82             earlier; i.e. key/value pairs in a later file will override a key/value
83             pair with the same key loaded earlier.
84 
85       5)    The exception to the rule is that a key/value pair will not be overridden
86             if the value of the key was changed since it had been loaded.
87 
88       6)    An application reads files in the order:
89 
90                 System-Wide:
91                         fox.rc,
92                         Vendor/Vendor.rc,
93                         Vendor/Application.rc
94 
95                 Per-User:
96                         fox.rc,
97                         Vendor/Vendor.rc,
98                         Vendor/Application.rc
99 
100       7)    When System-Wide settings files are loaded, entries are not marked as
101             changed, and thus not written into the Per-User settings file.  Only
102             values changed by the Application will be written into the Per-User file.
103 
104       8)    Settings loaded from the Per-User settings file *will* however be written
105             back to the Per-User settings file, regarless whether they are changed or
106             not (exception is when no single entry was changed at all!).
107 
108       9)    ONLY the Per-User, Application-specific settings files ARE WRITTEN!
109 
110      10)    Special applications, such as ControlPanel, may change other settings files,
111             however.
112 
113   - Locations of registry settings files:
114 
115       o System-Wide registry files are loaded from the directory defined by user-
116         specified environment variable $FOXDIR, if this variable was set.
117 
118       o System-Wide registry files are searched in SystemDirectories path-list.
119         The path-list may be changed by setSystemDirectories().
120 
121       o On UNIX systems, the XDG standard is followed; this means the location
122         of System-Wide settings should controlled by $XDG_CONFIG_DIRS.
123 
124       o If this environment variable isn't set, then SystemDirectories with only have
125         one single directory, the default value: "/etc/xdg".
126 
127       o Directories in SystemDirectories will be searched for a subdirectory "foxrc"
128         which is the root of the System-Wide settings tree for all FOX applications;
129         for example: "/etc/xdg/foxrc/Desktop".
130 
131       o Per-User registry files are located from a single toplevel directory in variable
132         UserDirectory.  The value of this variable may be changed by setUserDirectory().
133 
134       o On UNIX systems, the XDG standard is followed; this means the location of the
135         Per-User settings should controlled by $XDG_CONFIG_HOME.   If this environment
136         variable set, the root for the Per-User settings tree for all FOX applications
137         is $XDG_CONFIG_HOME/foxrc.
138 
139       o Otherwise, it will have the default value: "~/.config".
140 
141 
142   - The Freedesktop.org XDG standard is found at:
143 
144         http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
145 
146     Important for FOX is:
147 
148      $XDG_DATA_HOME     Per-user data files, defaults to "$HOME/.local/share".
149 
150      $XDG_CONFIG_HOME   Per-user configuration files, defaults to "$HOME/.config".
151 
152      $XDG_DATA_DIRS     Colon separated search path for per-user data files; default
153                         paths are "/usr/local/share/:/usr/share/"
154 
155      $XDG_CONFIG_DIRS   Colon separated search path for per-user configuration files;
156                         defaults to "/etc/xdg".
157 */
158 
159 #define MAXNAME   200
160 #define MAXVALUE  2000
161 
162 // Default locations and names
163 #if defined(WIN32)
164 #define FOXRC           "fox.ini"
165 #define SYSTEMDIRS      "\\Program Files;\\Windows"
166 #define USERDIR         "%USERPROFILE%\\fox"
167 #define FILEEXT         ".ini"
168 #else
169 #define FOXRC           "fox.rc"
170 #define SYSTEMDIRS      "/etc/xdg"
171 #define USERDIR         "~/.config"
172 #define FILEEXT         ".rc"
173 #endif
174 
175 using namespace FX;
176 
177 /*******************************************************************************/
178 
179 namespace FX {
180 
181 
182 // File extension for settings files
183 const FXchar FXRegistry::ext[]=FILEEXT;
184 
185 
186 // File name of common settings file
187 const FXchar FXRegistry::foxrc[]=FOXRC;
188 
189 
190 // Make registry object
FXRegistry(const FXString & akey,const FXString & vkey)191 FXRegistry::FXRegistry(const FXString& akey,const FXString& vkey):applicationkey(akey),vendorkey(vkey),systemdirs(SYSTEMDIRS),userdir(USERDIR){
192 #if defined(WIN32)
193   ascii=false;
194 #else
195   ascii=true;
196 #endif
197   }
198 
199 
200 #if defined(WIN32)
201 
202 // Read from Windows Registry
readFromRegistry(FXptr hroot,FXbool mrk)203 FXbool FXRegistry::readFromRegistry(FXptr hroot,FXbool mrk){
204   HKEY hsoftware;
205   FXbool ok=false;
206 
207   FXTRACE((100,"FXRegistry::readFromRegistry(%p,%d)\n",hroot,mrk));
208 
209   // Open Software registry section
210   if(RegOpenKeyExA((HKEY)hroot,"Software",0,KEY_READ,&hsoftware)==ERROR_SUCCESS){
211 
212     // Read Software\FOX
213     if(readFromRegistryGroup("FOX",hsoftware,false)) ok=true;
214 
215     // Have vendor key
216     if(!vendorkey.empty()){
217       HKEY hvendor;
218 
219       // Open vendor registry sub-section
220       if(RegOpenKeyExA(hsoftware,vendorkey.text(),0,KEY_READ,&hvendor)==ERROR_SUCCESS){
221 
222         // Read under "Software\Vendor\Vendor"
223         if(readFromRegistryGroup(vendorkey,hvendor,false)) ok=true;
224 
225         // Have application key
226         if(!applicationkey.empty()){
227 
228           // Read under "Software\Vendor\Application"
229           if(readFromRegistryGroup(applicationkey,hvendor,mrk)) ok=true;
230           }
231         RegCloseKey(hvendor);
232         }
233       }
234 
235     // No vendor key
236     else{
237 
238       // Have application key
239       if(!applicationkey.empty()){
240 
241         // Read under "Software\Application"
242         if(readFromRegistryGroup(applicationkey,hsoftware,mrk)) ok=true;
243         }
244       }
245     RegCloseKey(hsoftware);
246     }
247   return ok;
248   }
249 
250 
251 // Read from given group
readFromRegistryGroup(const FXString & group,FXptr hbase,FXbool mrk)252 FXbool FXRegistry::readFromRegistryGroup(const FXString& group,FXptr hbase,FXbool mrk){
253   FXchar section[MAXNAME],name[MAXNAME],value[MAXVALUE];
254   HKEY hgroup;
255 
256   // Open registry group
257   if(RegOpenKeyExA((HKEY)hbase,group.text(),0,KEY_READ,&hgroup)==ERROR_SUCCESS){
258     DWORD sectionsize=MAXNAME;
259     DWORD sectionindex=0;
260     FILETIME writetime;
261 
262     // Read sections
263     while(RegEnumKeyExA(hgroup,sectionindex,section,&sectionsize,NULL,NULL,NULL,&writetime)==ERROR_SUCCESS){
264 
265       // Open section
266       HKEY hsection;
267       if(RegOpenKeyExA(hgroup,section,0,KEY_READ,&hsection)==ERROR_SUCCESS){
268         DWORD namesize=MAXNAME;
269         DWORD valuesize=MAXVALUE;
270         DWORD index=0;
271         DWORD type;
272 
273         // Read key-value pairs
274         while(RegEnumValueA(hsection,index,name,&namesize,NULL,&type,(BYTE*)value,&valuesize)!=ERROR_NO_MORE_ITEMS){
275           FXASSERT(type==REG_SZ);
276           at(section).at(name,mrk)=value;
277           namesize=MAXNAME;
278           valuesize=MAXVALUE;
279           index++;
280           }
281 
282         // Close section
283         RegCloseKey(hsection);
284         }
285       sectionsize=MAXNAME;
286       sectionindex++;
287       }
288 
289     // Close group
290     RegCloseKey(hgroup);
291     return true;
292     }
293   return false;
294   }
295 
296 
297 // Update current user's settings
writeToRegistry(FXptr hroot)298 FXbool FXRegistry::writeToRegistry(FXptr hroot){
299   FXbool ok=false;
300 
301   FXTRACE((100,"FXRegistry::writeToRegistry(%p)\n",hroot));
302 
303   // Have application key
304   if(!applicationkey.empty()){
305     HKEY hsoftware;
306 
307     // Open software registry section
308     if(RegOpenKeyExA((HKEY)hroot,"Software",0,KEY_WRITE,&hsoftware)==ERROR_SUCCESS){
309 
310       // Have vendor key
311       if(!vendorkey.empty()){
312         HKEY hvendor;
313         DWORD disp;
314 
315         // Open vendor registry sub-section
316         if(RegCreateKeyExA(hsoftware,vendorkey.text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hvendor,&disp)==ERROR_SUCCESS){
317 
318           // Have application key
319           if(!applicationkey.empty()){
320 
321             // Write under "Software\Vendor\Application"
322             if(writeToRegistryGroup(applicationkey,hvendor)) ok=true;
323             }
324 
325           // Done with vendor key
326           RegCloseKey(hvendor);
327           }
328         }
329 
330       // No vendor key
331       else{
332 
333         // Have application key
334         if(!applicationkey.empty()){
335 
336           // Write under "Software\Application"
337           if(writeToRegistryGroup(applicationkey,hsoftware)) ok=true;
338           }
339         }
340 
341       // Done with software key
342       RegCloseKey(hsoftware);
343       }
344     }
345   return ok;
346   }
347 
348 
349 // Write to registry group
writeToRegistryGroup(const FXString & group,FXptr hbase)350 FXbool FXRegistry::writeToRegistryGroup(const FXString& group,FXptr hbase){
351   FXchar section[MAXNAME];
352   DWORD sectionsize,sectionindex,disp;
353   HKEY hgroup,hsection;
354   FXint s,e;
355   FILETIME writetime;
356 
357   // Open registry group
358   if(RegCreateKeyExA((HKEY)hbase,group.text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hgroup,&disp)==ERROR_SUCCESS){
359 
360     // First, purge all existing sections
361     while(1){
362       sectionindex=0;
363       sectionsize=MAXNAME;
364       if(RegEnumKeyExA(hgroup,sectionindex,section,&sectionsize,NULL,NULL,NULL,&writetime)!=ERROR_SUCCESS) break;
365       if(RegDeleteKeyA(hgroup,section)!=ERROR_SUCCESS) break;
366       }
367 
368     // Write sections
369     for(s=0; s<no(); ++s){
370 
371       // Section is non-empty
372       if(!empty(s)){
373         hsection=NULL;
374 
375         // Write keys in this section
376         for(e=0; e<data(s).no(); ++e){
377 
378           // Key is non-empty and marked
379           if(!data(s).empty(e) && data(s).mark(e)){
380 
381             // Create section in registry upon finding first key in it
382             if(hsection==NULL){
383               if(RegCreateKeyExA(hgroup,key(s).text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hsection,&disp)!=ERROR_SUCCESS) goto x;
384               }
385 
386             // Write key-value pair
387             if(RegSetValueExA(hsection,data(s).key(e).text(),0,REG_SZ,(BYTE*)data(s).data(e).text(),data(s).data(e).length()+1)!=ERROR_SUCCESS) break;
388             }
389           }
390 
391         // Close section
392         if(hsection) RegCloseKey(hsection);
393         }
394 
395       // Process next registry section
396 x:    continue;
397       }
398 
399     // Close group
400     RegCloseKey(hgroup);
401     return true;
402     }
403   return false;
404   }
405 
406 #endif
407 
408 
409 /*******************************************************************************/
410 
411 // Read registry
read()412 FXbool FXRegistry::read(){
413   FXbool ok=false;
414   FXString path;
415   if(ascii){
416 
417     // Read system-wide settings from systemdirs
418     if(!systemdirs.empty()){
419 
420       // Find common settings
421       path=FXPath::search(systemdirs,FOXRC);
422       if(!path.empty()){
423         if(parseFile(path,false)) ok=true;
424         }
425 
426       // Have vendor subdirectory
427       if(!vendorkey.empty()){
428 
429         // Find vendor subdirectory
430         path=FXPath::search(systemdirs,vendorkey);
431         if(!path.empty()){
432 
433           // Try read vendor settings
434           if(parseFile(path+PATHSEPSTRING+vendorkey+ext,false)) ok=true;
435 
436           // Try read application settings
437           if(!applicationkey.empty()){
438             if(parseFile(path+PATHSEPSTRING+applicationkey+ext,false)) ok=true;
439             }
440           }
441         }
442 
443       // Have application settings only
444       else if(!applicationkey.empty()){
445 
446         // Find applications settings
447         path=FXPath::search(systemdirs,applicationkey+ext);
448 
449         // Try read application settings
450         if(!path.empty()){
451           if(parseFile(path,false)) ok=true;
452           }
453         }
454       }
455 
456     // Read per-user settings from userdir
457     if(!userdir.empty()){
458 
459       // Path to settings data
460       path=FXPath::absolute(FXPath::expand(userdir));
461 
462       // Try read common settings
463       if(parseFile(path+PATHSEPSTRING FOXRC,false)) ok=true;
464 
465       // Try read vendor settings
466       if(!vendorkey.empty()){
467         path.append(PATHSEPSTRING+vendorkey);
468         if(parseFile(path+PATHSEPSTRING+vendorkey+ext,false)) ok=true;
469         }
470 
471       // Try read application settings
472       if(!applicationkey.empty()){
473         if(parseFile(path+PATHSEPSTRING+applicationkey+ext,true)) ok=true;
474         }
475       }
476     }
477 #if defined(WIN32)
478   else{
479     // Try read system-wide registry settings from HKEY_LOCAL_MACHINE
480     if(readFromRegistry(HKEY_LOCAL_MACHINE,false)) ok=true;
481 
482     // Try read per-user registry settings from HKEY_CURRENT_USER
483     if(readFromRegistry(HKEY_CURRENT_USER,true)) ok=true;
484     }
485 #endif
486   return ok;
487   }
488 
489 /*******************************************************************************/
490 
491 // Write registry
write()492 FXbool FXRegistry::write(){
493   FXbool ok=false;
494   FXString path;
495   if(isModified()){
496     if(ascii){
497 
498       // Write per-user settings to userdir
499       if(!userdir.empty()){
500 
501         // Have application key
502         if(!applicationkey.empty()){
503 
504           // Path to settings data
505           path=FXPath::absolute(FXPath::expand(userdir));
506 
507           // Have vendor key
508           if(!vendorkey.empty()){
509             path.append(PATHSEPSTRING+vendorkey);
510             }
511 
512           // Ensure parent directories exist
513           if(FXDir::createDirectories(path)){
514             FXString realfile;
515             FXString tempfile;
516 
517             // Final registry filename
518             realfile=path+PATHSEPSTRING+applicationkey+ext;
519 
520             // Temporary registry filename
521             tempfile=path+PATHSEPSTRING+applicationkey+"_"+FXString::value(FXProcess::current())+ext;
522 
523             // Unparse settings into temp file first
524             if(unparseFile(tempfile)){
525 
526               // Rename ATOMICALLY to proper name
527               if(FXFile::move(tempfile,realfile,true)){
528                 setModified(false);
529                 ok=true;
530                 }
531               }
532             }
533           }
534         }
535       }
536 #if defined(WIN32)
537     else{
538       // Write per-user registry settings to HKEY_CURRENT_USER
539       if(writeToRegistry(HKEY_CURRENT_USER)) ok=true;
540       }
541 #endif
542     }
543   return ok;
544   }
545 
546 
547 // Destructor
~FXRegistry()548 FXRegistry::~FXRegistry(){
549   }
550 
551 }
552