1 /********************************************************************************
2 *                                                                               *
3 *                           R e g i s t r y   C l a s s                         *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
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 * $Id: FXRegistry.cpp,v 1.61 2006/01/22 17:58:39 fox Exp $                      *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXObject.h"
30 #include "FXString.h"
31 #include "FXSystem.h"
32 #include "FXPath.h"
33 #include "FXStat.h"
34 #include "FXFile.h"
35 #include "FXDir.h"
36 #include "FXStringDict.h"
37 #include "FXRegistry.h"
38 
39 /*
40   Notes:
41 
42   - Default directories for registry files where search for the FOX settings
43     database is tried:
44 
45      1 $FOXDIR
46 
47      2 /etc/foxrc /usr/lib/foxrc /usr/local/lib/foxrc
48 
49      3 $PATH/foxrc
50 
51      4 ~/.foxrc
52 
53   - The latter one is writable, and also the one that overrides all other
54     values.
55 
56   - Search $PATH for registry directory [$PATH/VendorKey/AppKey] or
57     [$PATH/AppKey]
58 
59   - Compile-time #define for registry directories [for global settings].
60 
61   - Registry is organized as follows:
62 
63     DESKTOP             Registry file common to all FOX applications, from all vendors
64     Vendor/Vendor       Registry file common to all applications from Vendor
65     Vendor/Application  Registry file for Application from Vendor
66     Application         Registry file for Application, if no Vendor specified
67 
68     Rationale:
69 
70       1)    When installing an application, simply copy ``seed'' registry files
71             in the appropriate places; having a subdirectory Vendor prevents clobbering
72             other people's registry files, even if their application has the same name.
73 
74       2)    System-wide registry files are, as a matter of principle, read-only.
75 
76       3)    System registry files are loaded first, then per-user registry files are
77             loaded on top of that.
78 
79       4)    Registry files loaded later will take precedence over those loaded earlier;
80             i.e. key/value pairs in a later file will override a key/value pair with the
81             same key loaded earlier.
82 
83       5)    The exception to the rule is that a key/value pair will not be overridden if
84             the value of the key has changed since loading it. In other words, changes
85             will persist.
86 
87       6)    An application reads files in the order of system: (DESKTOP, Vendor/Vendor,
88             Vendor/App), then user: (DESKTOP, Vendor/Vendor, Vendor/App).
89 
90       7)    All changed entries are written to the per-user directory.  An application
91             starts by reading the registry files, runs for a while, then at the end writes
92             the settings back out to the per-user registry files.
93             It will ONLY write those entries which (a) have been changed, or (b) were
94             previously read from the same per-user registry files.
95 
96   - Need ability to read/write DESKTOP, Vendor/Vendor, Vendor/Application registry,
97     for both user as well as system-wide settings. This is primarily important for
98     installation programs.
99 
100 */
101 
102 #define MAXNAME   200
103 #define MAXVALUE  2000
104 
105 #ifndef REGISTRYPATH
106 #ifndef WIN32
107 #define REGISTRYPATH   "/etc:/usr/lib:/usr/local/lib"
108 #else
109 #define REGISTRYPATH   "\\WINDOWS\\foxrc"
110 #endif
111 #endif
112 
113 #define DESKTOP        "Desktop"
114 
115 using namespace FX;
116 
117 /*******************************************************************************/
118 
119 namespace FX {
120 
121 // Object implementation
122 FXIMPLEMENT(FXRegistry,FXSettings,NULL,0)
123 
124 // Make registry object
FXRegistry(const FXString & akey,const FXString & vkey)125 FXRegistry::FXRegistry(const FXString& akey,const FXString& vkey):applicationkey(akey),vendorkey(vkey){
126 #ifndef WIN32
127   ascii=true;
128 #else
129   ascii=false;
130 #endif
131   }
132 
133 
134 // Read registry
read()135 bool FXRegistry::read(){
136   FXString dirname;
137   register bool ok=false;
138 
139 #ifdef WIN32      // Either file based or system registry for WIN32
140 
141   if(ascii){
142 
143     FXTRACE((100,"Reading from file based settings database.\n"));
144 
145     // Try get location from FOXDIR environment variable
146     dirname=getenv("FOXDIR");
147     if(!dirname.empty()){
148       FXTRACE((100,"Found registry %s in $FOXDIR.\n",dirname.text()));
149       ok=readFromDir(dirname,false);
150       }
151 
152     // Try search along REGISTRYPATH if not specified explicitly
153     if(!ok){
154       dirname=FXPath::search(REGISTRYPATH,"foxrc");
155       if(!dirname.empty()){
156         FXTRACE((100,"Found registry %s in REGISTRYPATH.\n",dirname.text()));
157         ok=readFromDir(dirname,false);
158         }
159       }
160 
161     // Try search along PATH if still not found
162     if(!ok){
163       dirname=FXPath::search(FXSystem::getExecPath(),"foxrc");
164       if(!dirname.empty()){
165         FXTRACE((100,"Found registry %s in $PATH.\n",dirname.text()));
166         ok=readFromDir(dirname,false);
167         }
168       }
169 
170     // Get path to per-user settings directory
171     dirname=FXSystem::getEnvironment("USERPROFILE")+PATHSEPSTRING "foxrc";
172 
173     // Then read per-user settings; overriding system-wide ones
174     if(readFromDir(dirname,true)) ok=true;
175     }
176 
177   else{
178 
179     FXTRACE((100,"Reading from registry HKEY_LOCAL_MACHINE.\n"));
180 
181     // Load system-wide resources first
182     if(readFromRegistry(HKEY_LOCAL_MACHINE,false)) ok=true;
183 
184     FXTRACE((100,"Reading from registry HKEY_CURRENT_USER.\n"));
185 
186     // Now any modified resources for current user
187     if(readFromRegistry(HKEY_CURRENT_USER,true)) ok=true;
188     }
189 
190 #else             // File based registry for UNIX
191 
192   // Try get location from FOXDIR environment variable
193   dirname=getenv("FOXDIR");
194   if(!dirname.empty()){
195     FXTRACE((100,"Found registry %s in $FOXDIR.\n",dirname.text()));
196     ok=readFromDir(dirname,false);
197     }
198 
199   // Try search along REGISTRYPATH if not specified explicitly
200   if(!ok){
201     dirname=FXPath::search(REGISTRYPATH,"foxrc");
202     if(!dirname.empty()){
203       FXTRACE((100,"Found registry %s in REGISTRYPATH.\n",dirname.text()));
204       ok=readFromDir(dirname,false);
205       }
206     }
207 
208   // Try search along PATH if still not found
209   if(!ok){
210     dirname=FXPath::search(FXSystem::getExecPath(),"foxrc");
211     if(!dirname.empty()){
212       FXTRACE((100,"Found registry %s in $PATH.\n",dirname.text()));
213       ok=readFromDir(dirname,false);
214       }
215     }
216 
217   // Get path to per-user settings directory
218   dirname=FXSystem::getHomeDirectory()+PATHSEPSTRING ".foxrc";
219 
220   // Then read per-user settings; overriding system-wide ones
221   if(readFromDir(dirname,true)) ok=true;
222 
223 #endif
224 
225   return ok;
226   }
227 
228 
229 // Try read registry from directory
readFromDir(const FXString & dirname,bool mark)230 bool FXRegistry::readFromDir(const FXString& dirname,bool mark){
231   bool ok=false;
232 
233   // Directory is empty?
234   if(!dirname.empty()){
235 
236     // First try to load desktop registry
237 #ifndef WIN32
238     if(parseFile(dirname+PATHSEPSTRING DESKTOP,false)) ok=true;
239 #else
240     if(parseFile(dirname+PATHSEPSTRING DESKTOP ".ini",false)) ok=true;
241 #endif
242 
243     // Have vendor key
244     if(!vendorkey.empty()){
245 #ifndef WIN32
246       if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+vendorkey,false)) ok=true;
247 #else
248       if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+vendorkey+".ini",false)) ok=true;
249 #endif
250       // Have application key
251       if(!applicationkey.empty()){
252 #ifndef WIN32
253         if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+applicationkey,mark)) ok=true;
254 #else
255         if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+applicationkey+".ini",mark)) ok=true;
256 #endif
257         }
258       }
259 
260     // No vendor key
261     else{
262 
263       // Have application key
264       if(!applicationkey.empty()){
265 #ifndef WIN32
266         if(parseFile(dirname+PATHSEPSTRING+applicationkey,mark)) ok=true;
267 #else
268         if(parseFile(dirname+PATHSEPSTRING+applicationkey+".ini",mark)) ok=true;
269 #endif
270         }
271       }
272     }
273   return ok;
274   }
275 
276 
277 #ifdef WIN32
278 
279 // Read from Windows Registry
readFromRegistry(void * hRootKey,bool mark)280 bool FXRegistry::readFromRegistry(void* hRootKey,bool mark){
281   HKEY hSoftKey,hOrgKey;
282   bool ok=false;
283 
284   // Open Software registry section
285   if(RegOpenKeyExA((HKEY)hRootKey,"Software",0,KEY_READ,&hSoftKey)==ERROR_SUCCESS){
286 
287     // Read Software\Desktop
288     if(readFromRegistryGroup(hSoftKey,DESKTOP)) ok=true;
289 
290     // Have vendor key
291     if(!vendorkey.empty()){
292 
293       // Open Vendor registry sub-section
294       if(RegOpenKeyExA(hSoftKey,vendorkey.text(),0,KEY_READ,&hOrgKey)==ERROR_SUCCESS){
295 
296         // Read Software\Vendor\Vendor
297         if(readFromRegistryGroup(hOrgKey,vendorkey.text())) ok=true;
298 
299         // Have application key
300         if(!applicationkey.empty()){
301 
302           // Read Software\Vendor\Application
303           if(readFromRegistryGroup(hOrgKey,applicationkey.text(),mark)) ok=true;
304           }
305         RegCloseKey(hOrgKey);
306         }
307       }
308 
309     // No vendor key
310     else{
311 
312       // Have application key
313       if(!applicationkey.empty()){
314 
315         // Read Software\Application
316         if(readFromRegistryGroup(hSoftKey,applicationkey.text(),mark)) ok=true;
317         }
318       }
319     RegCloseKey(hSoftKey);
320     }
321   return ok;
322   }
323 
324 
325 // Read from given group
readFromRegistryGroup(void * org,const char * groupname,bool mark)326 bool FXRegistry::readFromRegistryGroup(void* org,const char* groupname,bool mark){
327   FXchar section[MAXNAME],name[MAXNAME],value[MAXVALUE];
328   DWORD sectionsize,sectionindex,namesize,valuesize,index,type;
329   HKEY groupkey,sectionkey;
330   FILETIME writetime;
331   FXStringDict *group;
332   if(RegOpenKeyExA((HKEY)org,groupname,0,KEY_READ,&groupkey)==ERROR_SUCCESS){
333     sectionindex=0;
334     sectionsize=MAXNAME;
335     FXTRACE((100,"Reading registry group %s\n",groupname));
336     while(RegEnumKeyExA(groupkey,sectionindex,section,&sectionsize,NULL,NULL,NULL,&writetime)==ERROR_SUCCESS){
337       group=insert(section);
338       FXTRACE((100,"[%s]\n",section));
339       if(RegOpenKeyExA(groupkey,section,0,KEY_READ,&sectionkey)==ERROR_SUCCESS){
340         index=0;
341         namesize=MAXNAME;
342         valuesize=MAXVALUE;
343         while(RegEnumValueA(sectionkey,index,name,&namesize,NULL,&type,(BYTE*)value,&valuesize)!=ERROR_NO_MORE_ITEMS){
344           FXASSERT(type==REG_SZ);
345           FXTRACE((100,"%s=%s\n",name,value));
346           group->replace(name,value,mark);
347           namesize=MAXNAME;
348           valuesize=MAXVALUE;
349           index++;
350           }
351         RegCloseKey(sectionkey);
352         }
353       sectionsize=MAXNAME;
354       sectionindex++;
355       }
356     RegCloseKey(groupkey);
357     return true;
358     }
359   return false;
360   }
361 
362 
363 #endif
364 
365 
366 
367 // Write registry
write()368 bool FXRegistry::write(){
369   FXString pathname,tempname;
370 
371   // Settings have not changed
372   if(!isModified()) return true;
373 
374   // We can not save if no application key given
375   if(!applicationkey.empty()){
376 
377 #ifdef WIN32      // Either file based or system registry for WIN32
378 
379     if(ascii){
380 
381       FXTRACE((100,"Writing to file based settings database.\n"));
382 
383       // Changes written only in the per-user registry
384       pathname=FXSystem::getEnvironment("USERPROFILE")+PATHSEPSTRING "foxrc";
385 
386       // If this directory does not exist, make it
387       if(!FXStat::exists(pathname)){
388         if(!FXDir::create(pathname)){
389           fxwarning("%s: unable to create directory.\n",pathname.text());
390           return false;
391           }
392         }
393       else{
394         if(!FXStat::isDirectory(pathname)){
395           fxwarning("%s: is not a directory.\n",pathname.text());
396           return false;
397           }
398         }
399 
400       // Add vendor subdirectory
401       if(!vendorkey.empty()){
402         pathname.append(PATHSEPSTRING+vendorkey);
403         if(!FXStat::exists(pathname)){
404           if(!FXDir::create(pathname)){
405             fxwarning("%s: unable to create directory.\n",pathname.text());
406             return false;
407             }
408           }
409         else{
410           if(!FXStat::isDirectory(pathname)){
411             fxwarning("%s: is not a directory.\n",pathname.text());
412             return false;
413             }
414           }
415         }
416 
417       // Add application key
418       pathname.append(PATHSEPSTRING+applicationkey+".ini");
419 
420       // Construct temp name
421       tempname.format("%s_%d",pathname.text(),fxgetpid());
422 
423       // Unparse settings into temp file first
424       if(unparseFile(tempname)){
425 
426         // Rename ATOMICALLY to proper name
427         if(!FXFile::rename(tempname,pathname)){
428           fxwarning("Unable to save registry.\n");
429           return false;
430           }
431 
432         modified=false;
433         return true;
434         }
435       }
436 
437     else{
438 
439       FXTRACE((100,"Writing to registry HKEY_CURRENT_USER.\n"));
440 
441       // Write back modified resources for current user
442       if(writeToRegistry(HKEY_CURRENT_USER)) return true;
443       }
444 
445 #else             // File based registry for X11
446 
447     // Changes written only in the per-user registry
448     pathname=FXSystem::getHomeDirectory()+PATHSEPSTRING ".foxrc";
449 
450     // If this directory does not exist, make it
451     if(!FXStat::exists(pathname)){
452       if(!FXDir::create(pathname)){
453         fxwarning("%s: unable to create directory.\n",pathname.text());
454         return false;
455         }
456       }
457     else{
458       if(!FXStat::isDirectory(pathname)){
459         fxwarning("%s: is not a directory.\n",pathname.text());
460         return false;
461         }
462       }
463 
464     // Add vendor subdirectory
465     if(!vendorkey.empty()){
466       pathname.append(PATHSEPSTRING+vendorkey);
467       if(!FXStat::exists(pathname)){
468         if(!FXDir::create(pathname)){
469           fxwarning("%s: unable to create directory.\n",pathname.text());
470           return false;
471           }
472         }
473       else{
474         if(!FXStat::isDirectory(pathname)){
475           fxwarning("%s: is not a directory.\n",pathname.text());
476           return false;
477           }
478         }
479       }
480 
481     // Add application key
482     pathname.append(PATHSEPSTRING+applicationkey);
483 
484     // Construct temp name
485     tempname.format("%s_%d",pathname.text(),fxgetpid());
486 
487     // Unparse settings into temp file first
488     if(unparseFile(tempname)){
489 
490       // Rename ATOMICALLY to proper name
491       if(!FXFile::rename(tempname,pathname)){
492         fxwarning("Unable to save registry.\n");
493         return false;
494         }
495       setModified(false);
496       return true;
497       }
498 
499 #endif
500 
501     }
502   return false;
503   }
504 
505 
506 
507 #ifdef WIN32
508 
509 // Update current user's settings
writeToRegistry(void * hRootKey)510 bool FXRegistry::writeToRegistry(void* hRootKey){
511   HKEY hSoftKey,hOrgKey;
512   DWORD disp;
513   bool ok=false;
514 
515   // Open Software registry section
516   if(RegOpenKeyExA((HKEY)hRootKey,"Software",0,KEY_WRITE,&hSoftKey)==ERROR_SUCCESS){
517 
518     // Have vendor key
519     if(!vendorkey.empty()){
520 
521       // Open Vendor registry sub-section
522       if(RegCreateKeyExA(hSoftKey,vendorkey.text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hOrgKey,&disp)==ERROR_SUCCESS){
523 
524         // Have application key
525         if(!applicationkey.empty()){
526 
527           // Write Software\Vendor\Application
528           if(writeToRegistryGroup(hOrgKey,applicationkey.text())) ok=true;
529           }
530         RegCloseKey(hOrgKey);
531         }
532       }
533 
534     // No vendor key
535     else{
536 
537       // Have application key
538       if(!applicationkey.empty()){
539 
540         // Write Software\Application
541         if(writeToRegistryGroup(hSoftKey,applicationkey.text())) ok=true;
542         }
543       }
544 
545     // Done with Software key
546     RegCloseKey(hSoftKey);
547     }
548   return ok;
549   }
550 
551 
552 // Write to registry group
writeToRegistryGroup(void * org,const char * groupname)553 bool FXRegistry::writeToRegistryGroup(void* org,const char* groupname){
554   FXchar section[MAXNAME];
555   DWORD sectionsize,sectionindex,disp;
556   HKEY groupkey,sectionkey;
557   FXint s,e;
558   FILETIME writetime;
559   FXStringDict *group;
560   if(RegCreateKeyExA((HKEY)org,groupname,0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&groupkey,&disp)==ERROR_SUCCESS){
561 
562     // First, purge all existing sections
563     while(1){
564       sectionindex=0;
565       sectionsize=MAXNAME;
566       if(RegEnumKeyExA(groupkey,sectionindex,section,&sectionsize,NULL,NULL,NULL,&writetime)!=ERROR_SUCCESS) break;
567       if(RegDeleteKeyA(groupkey,section)!=ERROR_SUCCESS) break;
568       }
569 
570     // Dump the registry, writing only marked entries
571     s=first();
572     while(s<size()){
573       sectionkey=NULL;
574       group=data(s);
575       FXASSERT(group);
576       for(e=group->first(); e<group->size(); e=group->next(e)){
577         if(group->mark(e)){
578           if(sectionkey==NULL){
579             FXASSERT(key(s));
580             if(RegCreateKeyExA(groupkey,key(s),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&sectionkey,&disp)!=ERROR_SUCCESS) goto x;
581             }
582           FXASSERT(group->key(e));
583           FXASSERT(group->data(e));
584           if(RegSetValueExA(sectionkey,group->key(e),0,REG_SZ,(BYTE*)group->data(e),strlen(group->data(e))+1)!=ERROR_SUCCESS) break;
585           }
586         }
587 
588       // Close this section's key (if it exists)
589       if(sectionkey) RegCloseKey(sectionkey);
590 
591       // Process next registry section
592 x:    s=next(s);
593       }
594     RegCloseKey(groupkey);
595     return true;
596     }
597   return false;
598   }
599 
600 
601 #endif
602 
603 }
604