/[winpt]/trunk/Src/wptRegistry.cpp
ViewVC logotype

Contents of /trunk/Src/wptRegistry.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5 - (show annotations)
Mon Mar 7 13:21:36 2005 UTC (19 years, 11 months ago) by twoaday
File size: 17068 byte(s)
2005-03-03  Timo Schulz  <twoaday@g10code.com>
                                                                                
        * wptCardDlg.cpp (card_changepin_dlg_proc): Add item to re-type the
        new PIN. Suggested by Achim.
        Support to show the unmasked PIN.
        Modified TAB-order.
        * wptPINDlg.cpp (pin_cb_dlg_proc): Show unmasked PIN.
 
        * Fixed wrong GPG --command-fd strings. Thanks to Achim.
 
2005-03-04  Timo Schulz  <twoaday@g10code.com>
 
        * GPG asks twice for the new PIN. Thanks to Achim.
        * wptCardDlg.cpp (card_changepin_dlg_proc): Reset the 'safety' pin also.        Only check the passphrase if the backup flag is enabled. Again thanks to        Achim.
 
2005-03-06  Timo Schulz  <twoaday@freakmail.de>
 
        * wptKeySignDlg.cpp (do_fill_seckeylist): Skip secret keys without
        a public key. Noted by Kurt Fitzner.
 


1 /* wptRegistry.cpp - W32 Registry access
2 * Copyright (C) 2000-2005 Timo Schulz
3 *
4 * This file is part of WinPT.
5 *
6 * WinPT is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * WinPT is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with WinPT; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <windows.h>
22 #include <stdio.h>
23
24 #include "wptErrors.h"
25 #include "wptW32API.h"
26 #include "wptGPG.h"
27 #include "wptRegistry.h"
28 #include "wptKeyserver.h"
29 #include "wptTypes.h"
30 #include "wptNLS.h"
31 #include "wptVersion.h"
32
33 #define rc_ok(rc) ((rc) == ERROR_SUCCESS)
34
35 struct reg_hotkey_s reg_hotkeys[] = {
36 {"ClipEncrypt", NULL, 0},
37 {"ClipDecrypt", NULL, 0},
38 {"ClipSign", NULL, 0},
39 {"ClipSignEnc", NULL, 0},
40 {"CwsEncrypt", NULL, 0},
41 {"CwsDecrypt", NULL, 0},
42 {"CwsSign", NULL, 0},
43 {"CwsSignEnc", NULL, 0},
44 {0}
45 };
46
47 winpt_reg_prefs_s reg_prefs;
48
49 #define WINPT_REG "Software\\WinPT"
50
51
52 void
53 free_reg_prefs (void)
54 {
55 free_if_alloc (reg_prefs.backup.path);
56 free_if_alloc (reg_prefs.kserv_conf);
57 memset (&reg_prefs, 0, sizeof reg_prefs);
58 } /* free_reg_prefs */
59
60
61 /*
62 * Register the given WinPT filetype.
63 */
64 static int
65 regist_single_filetype (gpg_filetype *gfile)
66 {
67 char icon[256], prog[256];
68
69 memset (&icon, 0, sizeof (icon));
70 GetModuleFileName (glob_hinst, prog, sizeof (prog)-1);
71 _snprintf (icon, sizeof (icon) -1, "%s,%d", prog, gfile->nicon);
72 return create_file_type (prog, gfile->ext, gfile->descr, icon);
73 } /* regist_single_filetype */
74
75
76 /*
77 * Install the GPG related into the W32 resgistry, if the entry already
78 * exists< the function returns immediately.
79 */
80 int
81 regist_inst_gnupg( int create_mokey )
82 {
83 int rc;
84 HKEY reg;
85
86 rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", 0, KEY_READ, &reg );
87 if( rc_ok( rc ) ) {
88 RegCloseKey( reg );
89 return 0;
90 }
91 rc = RegCreateKey( HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", &reg );
92 if( !rc_ok( rc ) )
93 return WPTERR_REGISTRY;
94 RegCloseKey( reg );
95 if( create_mokey ) {
96 rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Control Panel\\MingW32\\NLS", 0, KEY_READ, &reg );
97 if( rc_ok( rc ) ) {
98 RegCloseKey( reg );
99 return 0;
100 }
101 rc = RegCreateKey( HKEY_CURRENT_USER, "Control Panel\\MingW32\\NLS", &reg );
102 if( !rc_ok( rc ) )
103 return WPTERR_REGISTRY;
104 RegCloseKey( reg );
105 }
106
107 return 0;
108 } /* regist_inst_gpg */
109
110
111 /*
112 * Install WinPT into the W32 registry, if the entry already
113 * exists the function returns immediately.
114 */
115 int
116 regist_inst_winpt( int with_ext, int * created )
117 {
118 HKEY reg;
119 char * p = NULL;
120 int rc, i, id, n = 0;
121
122 if( created )
123 *created = 0;
124
125 p = get_reg_entry( HKEY_CURRENT_USER, WINPT_REG, "Extensions" );
126 if( p && *p == '1' )
127 with_ext = 0;
128 free_if_alloc( p );
129
130 if( with_ext ) {
131 id = msg_box( NULL, _("WinPT can register some GPG file types for you so they can "
132 "be processed with a double click in the explorer.\n"
133 "Do you want to continue?"), _("WinPT"), MB_YESNO|MB_INFO );
134 if( id == IDNO ) {
135 set_reg_entry( HKEY_CURRENT_USER, WINPT_REG, "Extensions", "1" );
136 goto start;
137 }
138 for( i = 0; gpg_filetypes[i].ext; i++ ) {
139 rc = RegOpenKeyEx( HKEY_CLASSES_ROOT, gpg_filetypes[i].ext, 0, KEY_READ, &reg );
140 if( rc_ok( rc ) ) {
141 RegCloseKey( reg );
142 id = log_box( _("WinPT WARNING"), MB_YESNO|MB_INFO,
143 _("It seems there was already a '%s' file type registered by another application.\n"
144 "Do you want to overwrite it?"), gpg_filetypes[i].ext );
145 if( id == IDNO )
146 continue;
147 }
148 regist_single_filetype( &gpg_filetypes[i] );
149 n++;
150 }
151 }
152
153 start:
154 rc = RegOpenKeyEx( HKEY_CURRENT_USER, WINPT_REG, 0, KEY_READ, &reg );
155 if( rc_ok( rc ) ) {
156 RegCloseKey( reg );
157 rc = RegOpenKeyEx( HKEY_CURRENT_USER, WINPT_REG"\\Keyserver", 0, KEY_READ, &reg );
158 if( !rc_ok( rc ) ) {
159 RegCreateKey( HKEY_CURRENT_USER, WINPT_REG"\\Keyserver", &reg );
160 RegCloseKey( reg );
161 }
162 p = get_reg_entry_keyserver ("Default");
163 if (!p) {
164 char buf[16];
165 sprintf (buf, "%d", HKP_PORT);
166 set_reg_entry_keyserver ("Default_Port", buf);
167 set_reg_entry_keyserver ("Default", DEF_HKP_KEYSERVER);
168 }
169 free_if_alloc( p );
170 if( n )
171 set_reg_entry( HKEY_CURRENT_USER, WINPT_REG, "Extensions", "1" );
172 return 0;
173 }
174 rc = RegCreateKey( HKEY_CURRENT_USER, WINPT_REG, &reg );
175 if( !rc_ok( rc ) )
176 return WPTERR_REGISTRY;
177 if( created )
178 *created = 1;
179 RegCloseKey( reg );
180 if( n )
181 set_reg_entry( HKEY_CURRENT_USER, WINPT_REG, "Extensions", "1" );
182 return 0;
183 } /* regist_inst_winpt */
184
185
186 /*
187 * Create a new filetype in the W32 registry.
188 * We should really care of errors! Otherwise we can damage the registry!
189 */
190 int
191 create_file_type( const char *exefile, const char *ext, const char *extname, char *iconfile )
192 {
193 int rc;
194 HKEY reg = NULL;
195 char deficon[256], defexec[256], p_exefile[256];
196
197
198 rc = RegCreateKey( HKEY_CLASSES_ROOT, ext, &reg );
199 if( rc_ok( rc ) )
200 rc = RegSetValueEx( reg, NULL, 0, REG_SZ, (byte *)extname, strlen( extname ) );
201 if( rc_ok( rc ) )
202 rc = RegCloseKey( reg );
203 if( rc_ok( rc ) )
204 rc = RegCreateKey( HKEY_CLASSES_ROOT, extname, &reg );
205 if( rc_ok( rc ) )
206 rc = RegSetValueEx( reg, NULL, 0, REG_SZ, (byte *) extname, strlen( extname ) );
207 if( rc_ok( rc ) )
208 rc = RegCloseKey( reg );
209 if( !rc_ok( rc ) ) {
210 rc = WPTERR_REGISTRY;
211 goto leave;
212 }
213
214 memset( &deficon, 0, sizeof deficon );
215 _snprintf( deficon, sizeof deficon - 1, "%s\\DefaultIcon", extname );
216 memset( &defexec, 0, sizeof defexec );
217 _snprintf( defexec, sizeof defexec - 1, "%s\\shell\\open\\command", extname );
218 memset( &p_exefile, 0, sizeof p_exefile );
219 _snprintf( p_exefile, sizeof p_exefile - 1, "%s %%1", exefile );
220
221 rc = RegCreateKey( HKEY_CLASSES_ROOT, deficon, &reg );
222 if( rc_ok( rc ) )
223 rc = RegSetValueEx(reg, NULL, 0, REG_SZ, (byte *)iconfile, strlen( iconfile ) );
224 if( rc_ok( rc ) )
225 rc = RegCloseKey( reg );
226 if( rc_ok( rc ) )
227 rc = RegCreateKey( HKEY_CLASSES_ROOT, defexec, &reg );
228 if( rc_ok( rc ) )
229 rc = RegSetValueEx( reg, NULL, 0, REG_SZ, (byte *)p_exefile, strlen( exefile ) );
230 if( rc_ok( rc ) )
231 rc = RegCloseKey( reg );
232 if( !rc_ok( rc ) ) {
233 rc = WPTERR_REGISTRY;
234 goto leave;
235 }
236
237 leave:
238 if( reg )
239 RegCloseKey( reg );
240 return rc;
241 } /* create_file_type */
242
243
244 static char *
245 expand_path( const char *path )
246 {
247 DWORD len;
248 char *p;
249
250 len = ExpandEnvironmentStrings( path, NULL, 0 );
251 if( !len )
252 return NULL;
253 len += 1;
254 p = new char[len];
255 if( !p )
256 return NULL;
257 len = ExpandEnvironmentStrings( path, p, len );
258 if( !len ) {
259 free_if_alloc( p );
260 return NULL;
261 }
262 return p;
263 } /* expand_path */
264
265
266 char *
267 get_reg_entry( HKEY root_key, const char * dir, const char * key )
268 {
269 int rc;
270 char text[384] = {0};
271 DWORD nbytes, type, n1 = 0;
272 HKEY reg_key = NULL;
273 char * p = NULL, * tmp = NULL;
274
275 rc = RegOpenKeyEx( root_key, dir, 0, KEY_QUERY_VALUE, &reg_key );
276 if( !rc_ok( rc ) )
277 goto leave;
278 nbytes = sizeof text - 1;
279 type = REG_SZ;
280
281 rc = RegQueryValueEx (reg_key, key, 0, &type, (BYTE *)&text, &nbytes);
282 if (!rc_ok (rc) || !nbytes)
283 goto leave;
284
285 if (type == REG_EXPAND_SZ && strchr (text, '%'))
286 p = expand_path (text);
287 else {
288 p = new char[nbytes + 1];
289 if (!p)
290 BUG (0);
291 memcpy (p, text, nbytes);
292 p[nbytes] = '\0';
293 }
294
295 leave:
296 if (reg_key)
297 RegCloseKey (reg_key);
298 return p;
299 } /* get_reg_entry */
300
301
302 int
303 set_reg_entry( HKEY root_key, const char * dir, const char * key,
304 const char * value )
305 {
306 int rc = 0;
307 HKEY reg_key;
308
309 rc = RegOpenKeyEx( root_key, dir, 0, KEY_WRITE, &reg_key );
310 if( !rc_ok( rc ) )
311 return WPTERR_REGISTRY;
312 rc = RegSetValueEx( reg_key, key, 0, REG_SZ, (BYTE *)value, strlen( value ) );
313 if( !rc_ok( rc ) )
314 rc = WPTERR_REGISTRY;
315 RegCloseKey( reg_key );
316 return rc;
317 } /* set_reg_entry */
318
319
320 int
321 set_reg_key( HKEY root_key, const char * dir, const char * key,
322 const char * value )
323 {
324 int rc = 0;
325 HKEY reg_key;
326
327 rc = RegOpenKeyEx( root_key, dir, 0, KEY_WRITE, &reg_key );
328 if( !rc_ok( rc ) )
329 return WPTERR_REGISTRY;
330
331 rc = RegSetValueEx( reg_key, key, 0, REG_SZ, (BYTE *)value, strlen( value ) );
332 if( !rc_ok( rc ) ) {
333 if ( RegCreateKey( root_key, key, &reg_key ) != ERROR_SUCCESS ) {
334 rc = WPTERR_REGISTRY;
335 goto leave;
336 }
337 rc = RegSetValueEx( reg_key, key, 0, REG_SZ, (BYTE *)value, strlen( value ) );
338 if ( !rc_ok( rc ) )
339 rc = WPTERR_REGISTRY;
340 }
341
342 leave:
343 RegCloseKey( reg_key );
344 return rc;
345 } /* set_reg_key */
346
347
348 int
349 set_reg_entry_gpg( const char * key, const char * value )
350 {
351 return set_reg_entry( HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", key, value );
352 } /* set_reg_entry_gpg */
353
354
355 int
356 set_reg_entry_mo( const char * value )
357 {
358 return set_reg_entry( HKEY_CURRENT_USER, "Control Panel\\Mingw32\\NLS", "MODir", value );
359 } /* set_reg_entry_mo */
360
361
362 char *
363 get_reg_entry_gpg( const char *key )
364 {
365 return get_reg_entry( HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", key );
366 } /* get_reg_entry_gpg */
367
368
369 char *
370 get_reg_entry_mo( void )
371 {
372 return get_reg_entry( HKEY_CURRENT_USER, "Control Panel\\Mingw32\\NLS", "MODir" );
373 } /* get_reg_entry_mo */
374
375
376 static const char * cfg [] = {
377 NULL,
378 "CacheTime",
379 "WordWrap",
380 "FastMode",
381 "Viewer",
382 "KeylistMode",
383 "WipeMode",
384 "AlwaysTrust",
385 "AutoBackup",
386 "BackupMode",
387 "DisableHotkeys",
388 "NoCompressMultiMedia",
389 "Expert",
390 "FMProgressBar",
391 };
392
393
394 int
395 set_reg_winpt_single (int id, int val)
396 {
397 char buf[64];
398 int rc;
399
400 sprintf (buf, "%d", val);
401 rc = set_reg_entry (HKEY_CURRENT_USER, WINPT_REG, cfg[id], buf);
402 return rc;
403 }
404
405
406 int
407 get_reg_winpt_single (int id)
408 {
409 char * buf = NULL;
410 int val = 0;
411
412 buf = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG, cfg[id]);
413 if (buf && *buf != ' ')
414 val = 1;
415 else if (!buf)
416 val = -1;
417 free_if_alloc (buf);
418 return val;
419 }
420
421
422 int
423 set_reg_winpt_prefs (winpt_reg_prefs_s * opt)
424 {
425 char buf[128];
426 int rc = 0, i;
427
428 for (i=1; i < DIM (cfg); i++) {
429 switch (i) {
430 case CFG_CACHETIME:
431 sprintf (buf, "%d", opt->cache_time);
432 break;
433 case CFG_WORDWRAP:
434 sprintf (buf, "%d", opt->word_wrap);
435 break;
436 case CFG_WIPEMODE:
437 sprintf (buf, "%d", opt->wipe_mode);
438 break;
439 case CFG_FASTMODE:
440 sprintf (buf, "%d", opt->use_tmpfiles);
441 break;
442 case CFG_NOZIP_MMEDIA:
443 sprintf (buf, "%d", opt->no_zip_mmedia);
444 break;
445 case CFG_VIEWER:
446 sprintf (buf, "%d", opt->use_viewer);
447 break;
448 case CFG_KEYLISTMODE:
449 sprintf (buf, "%d", opt->keylist_mode);
450 break;
451 case CFG_ALWAYSTRUST:
452 sprintf (buf, "%d", opt->always_trust);
453 break;
454 case CFG_AUTOBACKUP:
455 sprintf (buf, "%d", opt->auto_backup);
456 break;
457 case CFG_AUTOBAKMODE:
458 sprintf (buf, "%d", opt->backup.mode);
459 break;
460 case CFG_DISHOTKEYS:
461 sprintf (buf, "%d", opt->no_hotkeys);
462 break;
463 case CFG_EXPERT:
464 sprintf (buf, "%d", opt->expert);
465 break;
466
467 case CFG_FM_PROGRESS:
468 sprintf (buf, "%d", opt->fm.progress);
469 break;
470 }
471 rc = set_reg_entry (HKEY_CURRENT_USER, WINPT_REG, cfg[i], buf);
472 if (rc)
473 goto leave;
474 }
475
476 if (opt->backup.path) {
477 rc = set_reg_entry (HKEY_CURRENT_USER, WINPT_REG, "BackupPath",
478 opt->backup.path);
479 if (rc)
480 goto leave;
481 }
482 if (opt->kserv_conf) {
483 rc = set_reg_entry (HKEY_CURRENT_USER, WINPT_REG, "KeyserverConfig",
484 opt->kserv_conf);
485 if (rc)
486 goto leave;
487 }
488
489 for (i=0; reg_hotkeys[i].reg_entry; i++) {
490 strcpy (buf, " ");
491 if (reg_hotkeys[i].enabled)
492 strcpy (buf, reg_hotkeys[i].key);
493 rc = set_reg_key (HKEY_CURRENT_USER, WINPT_REG,
494 reg_hotkeys[i].reg_entry, buf);
495 if (rc)
496 break;
497 }
498
499 leave:
500 if (rc) {
501 msg_box (NULL, _("Could not write to Registry."), _("Preferences"), MB_ERR);
502 return rc;
503 }
504 return 0;
505 } /* set_reg_winpt_prefs */
506
507
508 int
509 set_reg_winpt_flag (const char * name, int val)
510 {
511 return set_reg_entry (HKEY_CURRENT_USER, WINPT_REG, name, val? "1" : "0");
512 } /* set_reg_winpt_flag */
513
514
515 int
516 get_reg_winpt_flag (const char * name)
517 {
518 char * buf;
519 int flag = 0;
520
521 buf = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG, name);
522 if (buf && buf[0] == '1')
523 flag = 1;
524 else if (!buf || buf && buf[0] != '0')
525 flag = -1;
526 free_if_alloc (buf);
527 return flag;
528 } /* get_reg_winpt_flag */
529
530
531 int
532 get_reg_winpt_prefs (winpt_reg_prefs_s * opt)
533 {
534 char * val = NULL;
535 int i;
536
537 for (i=1; i < DIM (cfg); i++) {
538 val = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG, cfg[i]);
539 if (!val || *val == ' ') {
540 free_if_alloc (val);
541 continue;
542 }
543 switch (i) {
544 case CFG_CACHETIME:
545 opt->cache_time = atol (val);
546 /* We do NOT support passphrase caching for more than an hour.
547 * Perhaps we should allow it, but for now we silently drop this.
548 */
549 if (opt->cache_time > 3600)
550 opt->cache_time = 3600;
551 break;
552 case CFG_WORDWRAP:
553 opt->word_wrap = atol (val);
554 break;
555 case CFG_FASTMODE:
556 opt->use_tmpfiles = atol (val);
557 break;
558 case CFG_NOZIP_MMEDIA:
559 opt->no_zip_mmedia = atol (val);
560 break;
561 case CFG_VIEWER:
562 opt->use_viewer = atol (val);
563 break;
564 case CFG_KEYLISTMODE:
565 opt->keylist_mode = atol (val);
566 break;
567 case CFG_WIPEMODE:
568 opt->wipe_mode = atol (val);
569 break;
570 case CFG_DISHOTKEYS:
571 opt->no_hotkeys = atol (val);
572 break;
573 case CFG_ALWAYSTRUST:
574 opt->always_trust = atol (val);
575 break;
576 case CFG_AUTOBACKUP:
577 opt->auto_backup = atol (val);
578 break;
579 case CFG_AUTOBAKMODE:
580 opt->backup.mode = atol (val);
581 break;
582 case CFG_EXPERT:
583 opt->expert = atol (val);
584 break;
585
586 case CFG_FM_PROGRESS:
587 opt->fm.progress = atol (val);
588 break;
589 }
590 free_if_alloc (val);
591 }
592
593 val = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG, "BackupPath");
594 if (val && val[0] != ' ')
595 opt->backup.path = m_strdup (val);
596 free_if_alloc (val);
597
598 val = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG, "KeyserverConfig");
599 if (val && val[0] != ' ')
600 opt->kserv_conf = m_strdup (val);
601 free_if_alloc (val);
602
603 for (i=0; reg_hotkeys[i].reg_entry; i++) {
604 val = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG, reg_hotkeys[i].reg_entry);
605 if (val && val[0] != ' ') {
606 reg_hotkeys[i].key[0] = *val;
607 reg_hotkeys[i].enabled = 1;
608 }
609 else
610 reg_hotkeys[i].enabled = 0;
611 free_if_alloc (val);
612 }
613 return 0;
614 } /* get_reg_winpt_prefs */
615
616
617 char *
618 get_reg_entry_keyserver (const char *name)
619 {
620 char * p = get_reg_entry (HKEY_CURRENT_USER, WINPT_REG"\\Keyserver", name);
621 if (p && !strcmp (p, "")) {
622 free_if_alloc (p);
623 return NULL;
624 }
625 return p;
626 } /* get_reg_entry_keyserver */
627
628
629 void
630 get_reg_proxy_prefs (char ** host, int * port, char ** user, char ** pass)
631 {
632 if (host)
633 *host = get_reg_entry_keyserver ("Host");
634 if (user)
635 *user = get_reg_entry_keyserver ("User");
636 if (pass)
637 *pass = get_reg_entry_keyserver ("Pass");
638 if (port) {
639 char * p = get_reg_entry_keyserver ("Port");
640 if (p) {
641 *port = atol (p);
642 free_if_alloc (p);
643 }
644 }
645 } /* get_reg_proxy_prefs */
646
647
648 int
649 set_reg_entry_keyserver( const char * name, const char * val )
650 {
651 return set_reg_entry( HKEY_CURRENT_USER, WINPT_REG"\\Keyserver", name, val );
652 } /* set_reg_entry_keyserver */
653
654
655 int
656 set_reg_proxy_prefs( const char * host, int port, const char * user, const char * pass )
657 {
658 int rc;
659
660 rc = set_reg_entry_keyserver( "Host", host? host : "" );
661 if( !rc ) {
662 char buf[32];
663 sprintf( buf, "%d", port );
664 rc = set_reg_entry_keyserver( "Port", buf );
665 }
666 if( !rc )
667 rc = set_reg_entry_keyserver( "User", user? user : "" );
668 if( !rc )
669 rc = set_reg_entry_keyserver( "Pass", pass? pass : "" );
670 return rc;
671 } /* set_reg_proxy_prefs */

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26