/[pkg-mixmaster]/trunk/Mix/Src/pgpdb.c
ViewVC logotype

Diff of /trunk/Mix/Src/pgpdb.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 91 by rabbi, Wed Jul 10 01:58:49 2002 UTC revision 332 by weaselp, Wed Oct 9 20:53:32 2002 UTC
# Line 6  Line 6 
6     details.     details.
7    
8     OpenPGP key database     OpenPGP key database
9     $Id: pgpdb.c,v 1.2 2002/07/10 01:58:49 rabbi Exp $ */     $Id: pgpdb.c,v 1.21 2002/10/09 20:53:31 weaselp Exp $ */
10    
11    
12  #include "mix3.h"  #include "mix3.h"
# Line 20  Line 20 
20  static int pgp_readkeyring(BUFFER *keys, char *filename)  static int pgp_readkeyring(BUFFER *keys, char *filename)
21  {  {
22    FILE *keyfile;    FILE *keyfile;
23    BUFFER *armored;    BUFFER *armored, *line, *tmp;
24    int err = -1;    int err = -1;
25    
26    if ((keyfile = mix_openfile(filename, "rb")) == NULL)    if ((keyfile = mix_openfile(filename, "rb")) == NULL)
# Line 33  static int pgp_readkeyring(BUFFER *keys, Line 33  static int pgp_readkeyring(BUFFER *keys,
33      err = 0;      err = 0;
34      buf_move(keys, armored);      buf_move(keys, armored);
35    } else {    } else {
36      err = pgp_dearmor(armored, keys);      line = buf_new();
37      if (err == 0)      tmp = buf_new();
38        err = ARMORED;  
39        while (1) {
40          do
41            if (buf_getline(armored, line) == -1) {
42              goto end_greedy_dearmor;
43            }
44          while (!bufleft(line, begin_pgp)) ;
45          buf_clear(tmp);
46          buf_cat(tmp, line);
47          buf_appends(tmp, "\n");
48          do {
49            if (buf_getline(armored, line) == -1) {
50              goto end_greedy_dearmor;
51            }
52            buf_cat(tmp, line);
53            buf_appends(tmp, "\n");
54          } while (!bufleft(line, end_pgp)) ;
55    
56          if (pgp_dearmor(tmp, tmp) == 0) {
57            err = ARMORED;
58            buf_cat(keys, tmp);
59          }
60        }
61    end_greedy_dearmor:
62        buf_free(line);
63        buf_free(tmp);
64    
65    }    }
66    buf_free(armored);    buf_free(armored);
67    return (err);    return (err);
68  }  }
69    
70  KEYRING *pgpdb_open(char *keyring, BUFFER *encryptkey, int writer)  KEYRING *pgpdb_open(char *keyring, BUFFER *encryptkey, int writer, int type)
71  {  {
72    KEYRING *keydb;    KEYRING *keydb;
73    
74    keydb = pgpdb_new(keyring, -1, encryptkey);    assert(! ((writer) && (type == PGP_TYPE_UNDEFINED)));
75      keydb = pgpdb_new(keyring, -1, encryptkey, type);
76    #ifndef NDEBUG
77      keydb->writer = writer;
78    #endif
79    if (writer)    if (writer)
80      keydb->lock = lockfile(keyring);      keydb->lock = lockfile(keyring);
81    keydb->filetype = pgp_readkeyring(keydb->db, keyring);    keydb->filetype = pgp_readkeyring(keydb->db, keyring);
# Line 54  KEYRING *pgpdb_open(char *keyring, BUFFE Line 84  KEYRING *pgpdb_open(char *keyring, BUFFE
84      pgpdb_close(keydb);      pgpdb_close(keydb);
85      return (NULL);      return (NULL);
86    }    }
87  #endif  #endif /* if 0 */
88    if (encryptkey && encryptkey->length && pgp_isconventional(keydb->db) &&    if (encryptkey && encryptkey->length && pgp_isconventional(keydb->db) &&
89        pgp_decrypt(keydb->db, encryptkey, NULL, NULL, NULL) < 0) {        pgp_decrypt(keydb->db, encryptkey, NULL, NULL, NULL) < 0) {
90      user_delpass();      user_delpass();
# Line 63  KEYRING *pgpdb_open(char *keyring, BUFFE Line 93  KEYRING *pgpdb_open(char *keyring, BUFFE
93    return (keydb);    return (keydb);
94  }  }
95    
96  KEYRING *pgpdb_new(char *keyring, int filetype, BUFFER *encryptkey)  KEYRING *pgpdb_new(char *keyring, int filetype, BUFFER *encryptkey, int type)
97  {  {
98    KEYRING *keydb;    KEYRING *keydb;
99    
# Line 74  KEYRING *pgpdb_new(char *keyring, int fi Line 104  KEYRING *pgpdb_new(char *keyring, int fi
104    keydb->db = buf_new();    keydb->db = buf_new();
105    keydb->modified = 0;    keydb->modified = 0;
106    keydb->lock = NULL;    keydb->lock = NULL;
107      keydb->type = type;
108    strncpy(keydb->filename, keyring, sizeof(keydb->filename));    strncpy(keydb->filename, keyring, sizeof(keydb->filename));
109    keydb->filetype = filetype;    keydb->filetype = filetype;
110    if (encryptkey == NULL)    if (encryptkey == NULL)
# Line 91  int pgpdb_close(KEYRING *keydb) Line 122  int pgpdb_close(KEYRING *keydb)
122    
123    if (keydb->modified) {    if (keydb->modified) {
124      FILE *f;      FILE *f;
125    #ifndef ndebug
126        assert(keydb->writer);
127    #endif
128      if (keydb->encryptkey && keydb->encryptkey->length)      if (keydb->encryptkey && keydb->encryptkey->length)
129        pgp_encrypt(PGP_NCONVENTIONAL | PGP_NOARMOR, keydb->db,        pgp_encrypt(PGP_NCONVENTIONAL | PGP_NOARMOR, keydb->db,
130                    keydb->encryptkey, NULL, NULL, NULL, NULL);                    keydb->encryptkey, NULL, NULL, NULL, NULL);
131        assert(keydb->type == PGP_TYPE_PRIVATE || keydb->type == PGP_TYPE_PUBLIC);
132      if (keydb->filetype == ARMORED)      if (keydb->filetype == ARMORED)
133        pgp_armor(keydb->db, 2);        pgp_armor(keydb->db, keydb->type == PGP_TYPE_PUBLIC ? PGP_ARMOR_KEY : PGP_ARMOR_SECKEY);
134      if (keydb->filetype == -1 || (f = mix_openfile(keydb->filename,      if (keydb->filetype == -1 || (f = mix_openfile(keydb->filename,
135                                                     keydb->filetype ==                                                     keydb->filetype ==
136                                                     ARMORED ? "w" : "wb"))                                                     ARMORED ? "w" : "wb"))
# Line 128  int pgpdb_getnext(KEYRING *keydb, BUFFER Line 162  int pgpdb_getnext(KEYRING *keydb, BUFFER
162    p = buf_new();    p = buf_new();
163    i = buf_new();    i = buf_new();
164    thisid = buf_new();    thisid = buf_new();
165    
166    if (key == NULL) {    if (key == NULL) {
167      tempbuf = 1;      tempbuf = 1;
168      key = buf_new();      key = buf_new();
# Line 170  int pgpdb_getnext(KEYRING *keydb, BUFFER Line 204  int pgpdb_getnext(KEYRING *keydb, BUFFER
204        case PGP_USERID:        case PGP_USERID:
205  #ifdef DEBUG  #ifdef DEBUG
206          printf("%s\n", p->data);          printf("%s\n", p->data);
207  #endif  #endif /* DEBUG */
208          if (userid && userid->length > 0 && bufifind(p, userid->data))          if (userid && userid->length > 0 && bufifind(p, userid->data))
209            found = 1;            found = 1;
210          break;          break;
# Line 192  int pgpdb_getnext(KEYRING *keydb, BUFFER Line 226  int pgpdb_getnext(KEYRING *keydb, BUFFER
226  int pgpdb_append(KEYRING *keydb, BUFFER *p)  int pgpdb_append(KEYRING *keydb, BUFFER *p)
227  {  {
228    assert(keydb->lock);    assert(keydb->lock);
229    #ifndef ndebug
230      assert(keydb->writer);
231    #endif
232    buf_cat(keydb->db, p);    buf_cat(keydb->db, p);
233    keydb->modified = 1;    keydb->modified = 1;
234    return (0);    return (0);
# Line 199  int pgpdb_append(KEYRING *keydb, BUFFER Line 236  int pgpdb_append(KEYRING *keydb, BUFFER
236    
237  #define pgp_preferredalgo PGP_ES_RSA  #define pgp_preferredalgo PGP_ES_RSA
238    
239  int pgpdb_getkey(int mode, int algo, int *sym, BUFFER *key, BUFFER *userid,  int pgpdb_getkey(int mode, int algo, int *sym, int *mdc, long *expires, BUFFER *key, BUFFER *userid,
240                   BUFFER *founduid, BUFFER *keyid, char *keyring, BUFFER *pass)                   BUFFER *founduid, BUFFER *keyid, char *keyring, BUFFER *pass)
241    /* FIXME: This could be changed to return the key with the latest expiration date if
242     *        a key is not unique */
243  {  {
244    KEYRING *r;    KEYRING *r;
245    BUFFER *id, *thisid, *thiskey;    BUFFER *id, *thisid, *thiskey;
# Line 211  int pgpdb_getkey(int mode, int algo, int Line 250  int pgpdb_getkey(int mode, int algo, int
250    thisid = buf_new();    thisid = buf_new();
251    thiskey = buf_new();    thiskey = buf_new();
252    if (keyring)    if (keyring)
253      r = pgpdb_open(keyring, pass, 0);      r = pgpdb_open(keyring, pass, 0, PGP_TYPE_UNDEFINED);
254    else    else
255      switch (mode) {      switch (mode) {
256      case PK_DECRYPT:      case PK_DECRYPT:
257      case PK_SIGN:      case PK_SIGN:
258        r = pgpdb_open(PGPREMSECRING, NULL, 0);        r = pgpdb_open(PGPREMSECRING, NULL, 0, PGP_TYPE_PRIVATE);
259        break;        break;
260      case PK_ENCRYPT:      case PK_ENCRYPT:
261      case PK_VERIFY:      case PK_VERIFY:
262        r = pgpdb_open(PGPREMPUBRING, NULL, 0);        r = pgpdb_open(PGPREMPUBRING, NULL, 0, PGP_TYPE_PUBLIC);
263        if (r != NULL && r->filetype == -1) {        if (r != NULL && r->filetype == -1) {
264          pgpdb_close(r);          pgpdb_close(r);
265          r = pgpdb_open(PGPREMPUBASC, NULL, 0);          r = pgpdb_open(PGPREMPUBASC, NULL, 0, PGP_TYPE_PUBLIC);
266        }        }
267        break;        break;
268      default:      default:
# Line 238  int pgpdb_getkey(int mode, int algo, int Line 277  int pgpdb_getkey(int mode, int algo, int
277        break;        break;
278      if (keyid) /* pgp_getkey has to chose subkey with given keyid */      if (keyid) /* pgp_getkey has to chose subkey with given keyid */
279        buf_set(thisid, keyid);        buf_set(thisid, keyid);
280      thisalgo = pgp_getkey(mode, algo, sym, thiskey, thiskey, thisid, founduid,      thisalgo = pgp_getkey(mode, algo, sym, mdc, expires, thiskey, thiskey, thisid, founduid,
281                            pass);                            pass);
282      if (thisalgo == PGP_PASS)      if (thisalgo == PGP_PASS)
283        needpass = 1;        needpass = 1;
# Line 267  end: Line 306  end:
306          errlog(NOTICE, "Key %b not found!\n", userid);          errlog(NOTICE, "Key %b not found!\n", userid);
307        else if (keyid && keyid->length > 7)        else if (keyid && keyid->length > 7)
308          errlog(NOTICE, "Key %02X%02X%02X%02X not found!\n", keyid->data[4],          errlog(NOTICE, "Key %02X%02X%02X%02X not found!\n", keyid->data[4],
309                 keyid->data[5], keyid->data[6], keyid->data[7]);                 keyid->data[5], keyid->data[6], keyid->data[7]);
310      }      }
311    }    }
312    if (found > 1) {    if (found > 1) {
# Line 291  end: Line 330  end:
330  int pgp_keymgt(int force)  int pgp_keymgt(int force)
331  {  {
332    FILE *f = NULL;    FILE *f = NULL;
333    BUFFER *key, *userid, *out, *outkey, *outtxt, *pass;    BUFFER *key, *keybak, *userid, *out, *outkey, *outtxt, *pass, *secout;
334    KEYRING *keys;    KEYRING *keys;
335    int err = 0, type = 0;    int err = 0, res, recreate_pubring = 0, dsa_ok = 0;
336    #ifdef USE_RSA
337    #ifdef USE_IDEA
338      int rsa_ok = 0;
339    #endif /* USE_IDEA */
340    #endif /* USE_RSA */
341      long expires;
342      LOCK *seclock;
343    
344    key = buf_new();    key = buf_new();
345    out = buf_new();    out = buf_new();
346      keybak = buf_new();
347      secout = buf_new();
348    
349    userid = buf_new();    userid = buf_new();
350    buf_sets(userid, REMAILERNAME);    buf_sets(userid, REMAILERNAME);
351    pass = buf_new();    pass = buf_new();
352    buf_sets(pass, PASS_PHRASE);    buf_sets(pass, PASSPHRASE);
353    outtxt = buf_new();    outtxt = buf_new();
354    outkey = buf_new();    outkey = buf_new();
355    
356  #ifdef USE_RSA  #ifdef USE_RSA
357    if (force == 2 || (pgpdb_getkey(PK_DECRYPT, PGP_ES_RSA, NULL, NULL, NULL,    /* We only want to build RSA keys if we also can do IDEA
358                                    NULL, NULL, NULL, pass) < 0))     * This is to not lose any mail should users try our RSA key
359       * with IDEA.
360       */
361    #ifdef USE_IDEA
362      /* FIXME: pgpdb_getky returns the expiration date from the last key in the keyring
363       *        which probably works most of the time if the keys are in the correct order
364       *        it doesn't return the latest expiration date (or 0) if the key in question
365       *        is before another matching key in the keyring tho
366       */
367      res = pgpdb_getkey(PK_DECRYPT, PGP_ES_RSA, NULL, NULL, &expires, NULL, NULL,
368                                      NULL, NULL, NULL, pass);
369      if (force == 2 || res < 0 || (expires > 0 && expires - KEYOVERLAPPERIOD < time(NULL))) {
370        rsa_ok = -1;
371      pgp_keygen(PGP_ES_RSA, 0, userid, pass, PGPKEY, PGPREMSECRING, 0);      pgp_keygen(PGP_ES_RSA, 0, userid, pass, PGPKEY, PGPREMSECRING, 0);
372      };
373    
374    if (force == 0 && (pgpdb_getkey(PK_ENCRYPT, PGP_ES_RSA, NULL, NULL, NULL,    if (force == 0 && (pgpdb_getkey(PK_ENCRYPT, PGP_ES_RSA, NULL, NULL, NULL, NULL, NULL,
375                                    NULL, NULL, PGPKEY, NULL) < 0))                                    NULL, NULL, PGPKEY, NULL) < 0) && rsa_ok == 0)
376      goto end;      rsa_ok = 1;
377  #endif  #endif /* USE_IDEA */
378    if (force == 2 || (pgpdb_getkey(PK_DECRYPT, PGP_E_ELG, NULL, NULL, NULL,  #endif /* USE_RSA */
379                                    NULL, NULL, NULL, pass) < 0))    /* FIXME: pgpdb_getky returns the expiration date from the last key in the keyring
380       *        which probably works most of the time if the keys are in the correct order
381       *        it doesn't return the latest expiration date (or 0) if the key in question
382       *        is before another matching key in the keyring tho
383       */
384      res = pgpdb_getkey(PK_DECRYPT, PGP_E_ELG, NULL, NULL, &expires, NULL, NULL,
385                                      NULL, NULL, NULL, pass);
386      if (force == 2 || res < 0 || (expires > 0 && expires - KEYOVERLAPPERIOD < time(NULL))) {
387        dsa_ok = -1;
388      pgp_keygen(PGP_E_ELG, 0, userid, pass, PGPKEY, PGPREMSECRING, 0);      pgp_keygen(PGP_E_ELG, 0, userid, pass, PGPKEY, PGPREMSECRING, 0);
389      }
390    
391      if (force == 0 && (pgpdb_getkey(PK_ENCRYPT, PGP_E_ELG, NULL, NULL, NULL, NULL, NULL,
392                                      NULL, NULL, PGPKEY, NULL) > 0) && dsa_ok == 0)
393        dsa_ok = 1;
394    
395    if (force == 0 && (pgpdb_getkey(PK_ENCRYPT, PGP_E_ELG, NULL, NULL, NULL,    /* No need to rewrite the files - we didn't change a thing */
396                                    NULL, NULL, PGPKEY, NULL) > 0))    if (
397    #ifdef USE_RSA
398    #ifdef USE_IDEA
399          rsa_ok == 1 &&
400    #endif /* USE_IDEA */
401    #endif /* USE_RSA */
402          dsa_ok == 1)
403      goto end;      goto end;
404    
405    /* write RSA and DSA/ElGamal keys separately to make old PGP    /* write keys one key per armor to make hand editing easy and old PGP
406       versions happy */     * versions happy */
407    err = -1;    err = -1;
408    for (type = 0; type < 2; type++) {    keys = pgpdb_open(PGPKEY, NULL, 0, PGP_TYPE_PUBLIC);
409      keys = pgpdb_open(PGPREMSECRING, NULL, 0);    if (keys == NULL)
410      if (keys == NULL)      recreate_pubring = 1;
411        goto end;    else {
412      while (pgpdb_getnext(keys, key, NULL, userid) != -1) {      while (pgpdb_getnext(keys, key, NULL, userid) != -1) {
       buf_clear(outkey);  
413        buf_clear(outtxt);        buf_clear(outtxt);
414        if (pgp_makepubkey(key, outtxt, outkey, pass,        if (pgp_makekeyheader(PGP_PUBKEY, key, outtxt, NULL, PGP_ANY) == 0) {
415                           type == 0 ? PGP_ES_RSA : PGP_S_DSA) == 0) {          err = 0;
416          err = 0;          buf_appends(out, "Type Bits/KeyID     Date       User ID\n");
         buf_appends(out, "Type Bits/KeyID    Date       User ID\n");  
417          buf_cat(out, outtxt);          buf_cat(out, outtxt);
         pgp_armor(outkey, 2);  
418          buf_nl(out);          buf_nl(out);
419          buf_cat(out, outkey);          pgp_armor(key, PGP_ARMOR_KEY);
420            buf_cat(out, key);
421          buf_nl(out);          buf_nl(out);
422        }        }
423      }      }
424      pgpdb_close(keys);      pgpdb_close(keys);
425    }    }
426      if (err != 0)
427        recreate_pubring = 1;
428      err = -1;
429    
430      keys = pgpdb_open(PGPREMSECRING, NULL, 0, PGP_TYPE_PRIVATE);
431      if (keys == NULL)
432        goto end;
433      while (pgpdb_getnext(keys, key, NULL, userid) != -1) {
434        buf_clear(outtxt);
435        buf_clear(outkey);
436        buf_clear(keybak);
437        buf_cat(keybak, key);
438        if (pgp_makekeyheader(PGP_SECKEY, key, outtxt, pass, PGP_ANY) == 0) {
439          err = 0;
440          buf_appends(secout, "Type Bits/KeyID     Date       User ID\n");
441          buf_cat(secout, outtxt);
442          buf_nl(secout);
443          pgp_armor(key, PGP_ARMOR_SECKEY);
444          buf_cat(secout, key);
445          buf_nl(secout);
446        }
447        buf_clear(outtxt);
448        if (recreate_pubring &&
449            pgp_makepubkey(keybak, outtxt, outkey, pass, PGP_ANY) == 0) {
450          buf_appends(out, "Type Bits/KeyID     Date       User ID\n");
451          buf_cat(out, outtxt);
452          buf_nl(out);
453          pgp_armor(outkey, PGP_ARMOR_KEY);
454          buf_cat(out, outkey);
455          buf_nl(out);
456        }
457      }
458      pgpdb_close(keys);
459    
460      seclock = lockfile(PGPREMSECRING);
461      if (err == 0 && (f = mix_openfile(PGPREMSECRING, "w")) != NULL) {
462        buf_write(secout, f);
463        fclose(f);
464      } else
465        err = -1;
466      unlockfile(seclock);
467    if (err == 0 && (f = mix_openfile(PGPKEY, "w")) != NULL) {    if (err == 0 && (f = mix_openfile(PGPKEY, "w")) != NULL) {
468        buf_write(out, f);      buf_write(out, f);
469        fclose(f);      fclose(f);
470      } else    } else
471        err = -1;      err = -1;
472  end:  end:
473    buf_free(key);    buf_free(key);
474      buf_free(keybak);
475    buf_free(out);    buf_free(out);
476    buf_free(userid);    buf_free(userid);
477    buf_free(pass);    buf_free(pass);
478    buf_free(outtxt);    buf_free(outtxt);
479    buf_free(outkey);    buf_free(outkey);
480      buf_free(secout);
481      return (err);
482    }
483    
484    int pgp_latestkeys(BUFFER* outtxt, int algo)
485    /* returns our latest key from pgpkey.txt in the buffer outtxt
486     * with pgp key header, ascii armored
487     *
488     * Can probably be extended to do this for all keys if we pass
489     * the keyring file and the userid
490     *
491     * IN:  algo: PGP_ANY, PGP_ES_RSA, PGP_E_ELG, PGP_S_DSA
492     * OUT: outtxt
493     */
494    {
495      int err = -1;
496      long expires_found = 0, expires;
497      BUFFER *key, *userid, *tmptxt;
498      KEYRING *keys;
499    
500      key = buf_new();
501      userid = buf_new();
502      buf_sets(userid, REMAILERNAME);
503      tmptxt = buf_new();
504    
505      keys = pgpdb_open(PGPKEY, NULL, 0, PGP_TYPE_PUBLIC);
506      if (keys != NULL) {
507        while (pgpdb_getnext(keys, key, NULL, userid) != -1) {
508          buf_clear(tmptxt);
509          if (pgp_makekeyheader(PGP_PUBKEY, key, tmptxt, NULL, algo) == 0) {
510            buf_rewind(key);
511            pgp_getkey(PK_VERIFY, algo, NULL, NULL, &expires, key, NULL, NULL, NULL, NULL);
512            if (expires == 0 || (expires_found <= expires)) {
513              err = 0;
514              buf_clear(outtxt);
515              buf_appends(outtxt, "Type Bits/KeyID     Date       User ID\n");
516              buf_cat(outtxt, tmptxt);
517              buf_nl(outtxt);
518              pgp_armor(key, PGP_ARMOR_KEY);
519              buf_cat(outtxt, key);
520              buf_nl(outtxt);
521              expires_found = expires;
522            }
523          }
524        }
525        pgpdb_close(keys);
526      }
527    
528      buf_free(key);
529      buf_free(userid);
530      buf_free(tmptxt);
531    
532    return (err);    return (err);
533  }  }
534    
# Line 385  int pgp_rlist(REMAILER remailer[], int n Line 556  int pgp_rlist(REMAILER remailer[], int n
556    return (0);    return (0);
557  }  }
558    
559    int pgp_rkeylist(REMAILER remailer[], int keyid[], int n)
560         /* Step through all remailers and get keyid */
561    {
562      BUFFER *userid;
563      BUFFER *id;
564      int i, err;
565    
566      userid = buf_new();
567      id = buf_new();
568    
569      for (i = 1; i < n; i++) {
570        buf_clear(userid);
571        buf_setf(userid, "<%s>", remailer[i].addr);
572    
573        keyid[i]=0;
574        if (remailer[i].flags.pgp) {
575          buf_clear(id);
576          err = pgpdb_getkey(PK_VERIFY, PGP_ANY, NULL, NULL, NULL, NULL, userid, NULL, id, NULL, NULL);
577          if (id->length == 8) {
578            /* printf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %s\n",
579               id->data[0], id->data[1], id->data[2], id->data[3], id->data[4], id->data[5], id->data[6], id->data[7], id->data[8], remailer[i].addr); */
580            keyid[i] = (((((id->data[4] << 8) + id->data[5]) << 8) + id->data[6]) << 8) + id->data[7];
581          }
582        }
583      }
584    
585      buf_free(userid);
586      return (0);
587    }
588    
589  #endif /* USE_PGP */  #endif /* USE_PGP */

Legend:
Removed from v.91  
changed lines
  Added in v.332

  ViewVC Help
Powered by ViewVC 1.1.5