Develop and Download Open Source Software

Browse Subversion Repository

Contents of /branches/mty-makai/key.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 252 - (show annotations) (download) (as text)
Mon Dec 27 03:23:48 2010 UTC (13 years, 3 months ago) by notanpe
File MIME type: text/x-csrc
File size: 15586 byte(s)
本家 R245 をベースに全てやりなおし。
1 /***********************************************************************
2 *
3 * file: key.c
4 *
5 * $Id: key.c 241 2010-03-15 14:28:18Z chapuni $
6 *
7 */
8
9 #include <assert.h>
10 #include <stdlib.h>
11
12 #include "cp932.h"
13 #include "desconst.h"
14 #include "key.h"
15 #include "translate.h"
16
17 #if USE_DT
18 #include "dt4.h"
19 #endif
20
21 /* 拡張鍵クラス */
22 #define KCLS_DT0 64
23 #define KCLS_DT1 128
24 #define KCLS_K2 256
25
26 #if USE_DT
27 /* 鍵キメ用辞書インデクス */
28 struct DT *kd[8 + 8];
29
30 /* 辞書インデクス */
31 struct DT *dtidx[0x100 + 1];
32 #endif
33
34 /* 指定されたクラスと入っているキーから、classify を行う */
35 void
36 key_make_map(struct KS_KEY *key, int n)
37 {
38 int i, j;
39 unsigned c = key->map[n][key->key[n]];
40
41 #if USE_DT
42 if (3 <= n && n < 7 && kd[n - 3])
43 {
44 /* 辞書のケツの文字。後ろにナニヤラキャラクタが来る */
45 c = kd[n - 3]->c[0];
46 if ((0x81 <= c && c <= 0x9F)
47 || (0xE0 <= c && c <= 0xFC))
48 c = KCLS_K2;
49 else
50 c = (cp932[256 * key->key[n]]
51 | cp932[256 * (key->key[n] ^ 0x80)]);
52 #if DEBUG>=1
53 printf("*n=%d, key=%02X, cls=%04X\n",
54 n,
55 key[n],
56 c);
57 #endif
58 }
59 else if (2 <= n && n < 6 && kd[n - 2])
60 {
61 return;
62 }
63 else if (1 <= n && n < 5 && kd[n - 1])
64 {
65 return;
66 }
67 #if USE_DT
68 else if (1 <= n && n < 5 && !kd[n - 1]
69 //&& (c & KCLS_K2)
70 && (c & KCLS_DT1))
71 {
72 /* 漢字2文字を拾っていきまつ */
73 #if DEBUG>=1
74 printf("(%d)%02X %02X(%02X:%02X:%02X:%02X)\n",
75 n, key->key[n - 1], key->key[n],
76 cp932[(256 * key->key[n - 1] + key->key[n])],
77 cp932[(256 * key->key[n - 1] + key->key[n]) ^ 0x0080],
78 cp932[(256 * key->key[n - 1] + key->key[n]) ^ 0x8000],
79 cp932[(256 * key->key[n - 1] + key->key[n]) ^ 0x8080]);
80 #endif
81 if (n != 1 && n != 2
82 && (cp932[(256 * key->key[n - 1] + key->key[n]) ^ 0x0080] & KCLS_DT1))
83 key->key[n] ^= 0x80;
84 else if (n != 2 && n != 3
85 && (cp932[(256 * key->key[n - 1] + key->key[n]) ^ 0x8000] & KCLS_DT1))
86 key->key[n - 1] ^= 0x80;
87 else if (n > 3 && (cp932[(256 * key->key[n - 1] + key->key[n]) ^ 0x8080] & KCLS_DT1))
88 key->key[n - 1] ^= 0x80, key->key[n] ^= 0x80;
89 if (cp932[256 * key->key[n - 1] + key->key[n]] & KCLS_DT1)
90 {
91 for (kd[n - 1] = dtidx[key->key[n - 1]];
92 kd[n - 1]->c[1] != key->key[n];
93 kd[n - 1]++)
94 assert(kd[n - 1]->c[0] == key->key[n - 1]);
95 #if DEBUG>=1
96 printf("(%02X%02X:%02X%02X)%c%c%c%c\n",
97 kd[n - 1]->c[0],
98 kd[n - 1]->c[1],
99 kd[n - 1]->c[2],
100 kd[n - 1]->c[3],
101 kd[n - 1]->c[0],
102 kd[n - 1]->c[1],
103 kd[n - 1]->c[2],
104 kd[n - 1]->c[3]);
105 #endif
106 return;
107 }
108 }
109 else if (n < 4 && (c & KCLS_DT0) && kd[n] == NULL)
110 {
111 /* カタカナ埋め込みいきます */
112 assert(kd[n] == NULL);
113 #if DEBUG>=1
114 printf("n=%d, key=%02X\n", n, key->key[n]);
115 #endif
116 kd[n] = dtidx[key->key[n]];
117 if (!kd[n]
118 && !(n == 1 || n == 2)
119 && dtidx[key->key[n] ^ 0x80])
120 {
121 key->key[n] ^= 0x80;
122 kd[n] = dtidx[key->key[n]];
123 }
124 if (kd[n])
125 return;
126 }
127 #endif
128 else
129 {
130 kd[n] = NULL;
131 }
132 #endif
133
134 /* 最後の部分は class map を生成する必要ナシ */
135 if (n >= 6)
136 return;
137
138 for (i = 0; i < 256; i++)
139 {
140 unsigned bm = 0;
141 #if 1
142 if (c & KCLS_K1)
143 {
144 if (cp932[256 * key->key[n] + i] & KCLS_K1)
145 bm |= KCLS_K2 | (cp932[256 * key->key[n] + i] & KCLS_DT1);
146 if (cp932[256 * (key->key[n] ^ 0x80) + i] & KCLS_K1)
147 bm |= KCLS_K2 | (cp932[256 * (key->key[n] ^ 0x80) + i] & KCLS_DT1);
148 #if 0
149 bm |= ((cp932[256 * key->key[n] + i] & KCLS_K1)
150 || (cp932[256 * (key->key[n] ^ 0x80) + i] & KCLS_K1)
151 ? KCLS_K2 : 0);
152 #endif
153 }
154 if (c & (KCLS_AN | KCLS_KA | KCLS_K2))
155 for (j = 0; j < 256; j++)
156 {
157 bm |= cp932[256 * i + j] & (KCLS_AN | KCLS_KA | KCLS_K1
158 | KCLS_DT0);
159 #if 0
160 if (j >= 127 && !(n == 0 || n == 1))
161 break;
162 #endif
163 }
164 key->map[n + 1][i] = bm;
165 #endif
166 if (i >= 128 && !(n == 0 || n == 1))
167 key->map[n + 1][i - 128] |= key->map[n + 1][i];
168 }
169
170 if (n < 6)
171 key->map[n + 1][0x00] = key->map[n + 1][0x80] = 0;
172 if (n == 6)
173 key->map[7][0x00] |= KCLS_AN;
174 }
175
176 #if USE_DT
177 unsigned
178 dt_get(int kdn,
179 int xn,
180 int n,
181 int ch)
182 {
183 int i;
184 #if DEBUG>=1
185 printf("*dt_get(%d)%c%c%c%c(%02X%02X:%02X%02X)->ch=%d",
186 n,
187 kd[kdn]->c[0], kd[kdn]->c[1], kd[kdn]->c[2], kd[kdn]->c[3],
188 kd[kdn]->c[0], kd[kdn]->c[1], kd[kdn]->c[2], kd[kdn]->c[3],
189 ch);
190 #endif
191 /* まずは数える */
192 for (i = 0;
193 kd[kdn][i].c[xn] == kd[kdn]->c[xn];
194 i++)
195 ;
196 assert(i > 0);
197 kd[kdn] += ch % i;
198 #if DEBUG>=1
199 printf("/%d\n dt_get: %c%c%c%c(%02X%02X:%02X%02X)->ch=%d\n",
200 i,
201 kd[kdn]->c[0], kd[kdn]->c[1], kd[kdn]->c[2], kd[kdn]->c[3],
202 kd[kdn]->c[0], kd[kdn]->c[1], kd[kdn]->c[2], kd[kdn]->c[3],
203 ch);
204 #endif
205 return kd[kdn]->c[n];
206 }
207 #endif
208
209 /* マップから文字を拾ってセット */
210 unsigned
211 key_set(struct KS_KEY *key, int n, unsigned ch)
212 {
213 int cnt = 0, i;
214
215 #if USE_DT
216 if (3 <= n && n < 7 && kd[n - 3])
217 {
218 return dt_get(n - 3, 2, 3, ch);
219 return kd[n - 3]->c[3];
220 }
221 else if (2 <= n && n < 6 && kd[n - 2])
222 {
223 return dt_get(n - 2, 1, 2, ch);
224 return kd[n - 2]->c[2];
225 }
226 else if (1 <= n && n < 5 && kd[n - 1])
227 {
228 return dt_get(n - 1, 0, 1, ch);
229 return kd[n - 1]->c[1];
230 }
231 #endif
232
233 #if DEBUG>=3
234 if (cnt == 0)
235 {
236 printf("n=%d, ch=%d, (n-1)=%02X\n", n, ch, key[n - 1]);
237 int j;
238 for (i = 0; i < 16; i++)
239 {
240 printf("map[0x%02X] =", 16 * i);
241 for (j = 0; j < 16; j++)
242 printf(" %03X", kcls[n].map[16 * i + j]);
243 printf("\n");
244 }
245 }
246 #endif
247 for (i = 0; i < 256; i++)
248 {
249 #if 1
250 /* 鳥屋っぽーい */
251 if (1 <= n && n <= 2 && '.' < i && i <= 'z')
252 continue;
253 #endif
254 if (key->map[n][i])
255 {
256 if (ch-- == 0)
257 return i;
258 cnt++;
259 }
260 if (n != 1 && n != 2 && i >= 127)
261 break;
262 }
263 /* 見つからなかったのでもいっぺん */
264 assert(cnt > 0);
265 ch %= cnt;
266 for (i = 0; i < 256; i++)
267 if (key->map[n][i])
268 {
269 #if 1
270 /* 鳥屋っぽーい */
271 if (1 <= n && n <= 2 && '.' < i && i <= 'z')
272 continue;
273 #endif
274 if (ch-- == 0)
275 return i;
276 }
277 assert(!"not matched");
278 return 0;
279 }
280
281 /* bitwise key をセット */
282 void
283 key_set64(struct KEY *key64,
284 int n,
285 unsigned k,
286 unsigned vk,
287 unsigned sk)
288 {
289 int i, j;
290 if (!((vk | sk) & 0x7F))
291 return;
292
293 for (i = 0; i < 7; i++)
294 {
295 if (n == 7 && i < N_STRIDE) continue;
296 if (sk & (1 << i))
297 {
298 /* セット */
299 int o = tr_pc1[n][6 - i] - 1;
300 if (o < 28)
301 {
302 assert(o >= 0);
303 for (j = 0; j < N_ALU; j++)
304 key64->k[0][0][o].a[j]
305 = key64->k[0][1][o].a[j]
306 = -!!(k & (1 << i));
307 }
308 else
309 {
310 assert(o >= 28);
311 assert(o < 56);
312 for (j = 0; j < N_ALU; j++)
313 key64->k[1][0][o - 28].a[j]
314 = key64->k[1][1][o - 28].a[j]
315 = -!!(k & (1 << i));
316 }
317 }
318 else if (vk & (1 << i))
319 {
320 /* 反転 */
321 int o = tr_pc1[n][6 - i] - 1;
322 if (o < 28)
323 {
324 assert(o >= 0);
325 for (j = 0; j < N_ALU; j++)
326 key64->k[0][0][o].a[j]
327 = key64->k[0][1][o].a[j]
328 = ~key64->k[0][0][o].a[j];
329 }
330 else
331 {
332 assert(o >= 28);
333 assert(o < 56);
334 for (j = 0; j < N_ALU; j++)
335 key64->k[1][0][o - 28].a[j]
336 = key64->k[1][1][o - 28].a[j]
337 = ~key64->k[1][0][o - 28].a[j];
338 }
339 }
340 }
341 }
342
343 /* 指定されたクラスの開始値にリセット
344 直前の文字のクラスに縛られる */
345 int
346 key_reset(struct KS_KEY *key, int n)
347 {
348 if (n >= 8)
349 return 1;
350 if (n == 7)
351 {
352 key->key[7] = 0;
353 return 1;
354 }
355
356 /* 0-2 文字目はランダムに決める
357 3 文字目以降は初期値に */
358 if (n >= KEY_SHUFFLE_POS)
359 key->key[n] = key_set(key, n, 0);
360 else
361 key->key[n] = key_set(key, n, rand());
362
363 #if DEBUG>=3
364 printf("key[%d]=%02X ncls=%04X\n", n, key[n], kcls[n].map[key[n]]);
365 #endif
366
367 /* セットされた文字を元に、次キャラの文字クラスを決める */
368 key_make_map(key, n);
369
370 return key_reset(key, n + 1);
371 }
372
373 /* 指定された鍵空間の中で、キーをひとつ進める
374 安全にインクリメントできた場合 true を返す */
375 int
376 key_inc(struct KS_KEY *key, int n, int e)
377 {
378 if (n >= 8)
379 return 0;
380 else if (n == 7)
381 {
382 /* 最後のバイト */
383 uint8_t o_k = (key->key[7] + (1 << N_STRIDE)) & 0x7F;
384 if (!o_k)
385 return 0; /* インクリメントできなかったときは次へ進めず待つ */
386
387 /* 進める */
388 key->key[7] = o_k;
389 return 1;
390 }
391 else if (n >= e - 1)
392 {
393 /* do nothing */
394 }
395 else if (key_inc(key, n + 1, e)
396 /*
397 && key_inc(n + 1)
398 && key_inc(n + 1)
399 && key_inc(n + 1)*/
400 )
401 return 1;
402
403 /* Salt はインクリメントしない約束にする */
404 if (n == 1 || n == 2)
405 return 1;
406
407 #if DEBUG>=3
408 printf("key_inc(n=%d,ck=%02X)\n", n, key->key[n]);
409 #endif
410
411 #if USE_DT
412 /* 辞書語はインクリメントしていい約束にする */
413 if (3 <= n && n < 7 && kd[n - 3])
414 {
415 if ((key->key[n - 3] & 0x7F) == ((kd[n - 3] + 1)->c[0] & 0x7F)
416 && (key->key[n - 2] & 0x7F) == ((kd[n - 3] + 1)->c[1] & 0x7F)
417 && (key->key[n - 1] & 0x7F) == ((kd[n - 3] + 1)->c[2] & 0x7F))
418 {
419 memcpy(&key->key[n - 3], &(++kd[n - 3])->c[0], 4);
420 #if DEBUG>=2
421 printf(">dt_get:%c%c%c%c(%02X%02X:%02X%02X)\n",
422 kd[n - 3]->c[0], kd[n - 3]->c[1], kd[n - 3]->c[2], kd[n - 3]->c[3],
423 kd[n - 3]->c[0], kd[n - 3]->c[1], kd[n - 3]->c[2], kd[n - 3]->c[3]);
424 #endif
425 return 1;
426 }
427 else
428 {
429 return 0;
430 }
431 }
432 else if (2 <= n && n < 6 && kd[n - 2])
433 {
434 if ((key->key[n - 2] & 0x7F) == ((kd[n - 2] + 1)->c[0] & 0x7F)
435 && (key->key[n - 1] & 0x7F) == ((kd[n - 2] + 1)->c[1] & 0x7F))
436 {
437 memcpy(&key->key[n - 2], &(++kd[n - 2])->c[0], 4);
438 #if DEBUG>=2
439 printf(">dt_get:%c%c%c%c(%02X%02X:%02X%02X)\n",
440 kd[n - 2]->c[0], kd[n - 2]->c[1], kd[n - 2]->c[2], kd[n - 2]->c[3],
441 kd[n - 2]->c[0], kd[n - 2]->c[1], kd[n - 2]->c[2], kd[n - 2]->c[3]);
442 #endif
443 return 1;
444 }
445 else
446 {
447 return 0;
448 }
449 if (kd[n - 2]->c[0] == key->key[n - 2])
450 return 1;
451 else
452 return 0;
453 }
454 else if (1 <= n && n < 5 && kd[n - 1])
455 {
456 unsigned c2 = kd[n - 1]->c[0];
457 if ((0x81 <= c2 && c2 <= 0x9F)
458 || (0xE0 <= c2 && c2 <= 0xFC))
459 {
460 kd[n - 1] = NULL;
461 #if 0
462 if (!(n == 1 && n == 2))
463 key->key[n] &= 0x7F;
464 if (!(n == 2 && n == 3))
465 key->key[n - 1] &= 0x7F;
466 #endif
467 key_make_map(n - 1);
468 }
469 else if ((key->key[n - 1] & 0x7F) == ((kd[n - 1] + 1)->c[0] & 0x7F))
470 {
471 memcpy(&key->key[n - 1], &(++kd[n - 1])->c[0], 4);
472 #if DEBUG>=2
473 printf(">dt_get:%c%c%c%c(%02X%02X:%02X%02X)\n",
474 kd[n - 1]->c[0], kd[n - 1]->c[1], kd[n - 1]->c[2], kd[n - 1]->c[3],
475 kd[n - 1]->c[0], kd[n - 1]->c[1], kd[n - 1]->c[2], kd[n - 1]->c[3]);
476 #endif
477 return 1;
478 }
479 else
480 {
481 return 0;
482 }
483 #if 0
484 if (kd[n - 1]->c[0] == key->key[n - 1])
485 return 1;
486 else
487 return 0;
488 #endif
489 }
490 else if (n < 4 && kd[n])
491 {
492 if (0 && kd[n]->c[0] == key->key[n])
493 return 1;
494 kd[n] = NULL;
495 #if 0
496 if (!(n == 1 || n == 2))
497 key->key[n] &= 0x7F;
498 #endif
499 }
500 #endif
501
502 /* 実際に増やしてみる */
503 assert(n >= 3);
504 for (;;)
505 {
506 #if USE_DT
507 if (n <= 3
508 && !(key->key[n] & 0x80)
509 && kcls[n].map[key->key[n] ^ 0x80] & (KCLS_DT0))
510 {
511 /* 半角カタカナの1バイト目 */
512 key->key[n] ^= 0x80;
513 }
514 else
515 #endif
516 {
517 key->key[n] = (key->key[n] & 0x7F) + 1;
518 if (key->key[n] >= 0x80)
519 {
520 key->key[n] = 0xFF; /* 次に突入させないため */
521 return 0;
522 }
523 }
524
525 if (key->map[n][key->key[n]])
526 {
527 key_make_map(key, n);
528 key_reset(key, n + 1);
529 return 1;
530 }
531 }
532 }
533
534 /* 鍵を完全にリセットする
535 Saltもセットし直す */
536 void
537 key_init(struct KS_KEY *key)
538 {
539 int i, j;
540
541 #if USE_DT
542 /* 辞書を、インデクスを作りながらマップにはめこんで逝く
543 辞書はコード順昇順に並んでるものとする */
544 for (i = 0; i < dtcnt; i++)
545 {
546 unsigned c = dt[i].c[0];
547
548 assert(dt[i].c[0]
549 && dt[i].c[1]
550 && dt[i].c[2]
551 && dt[i].c[3]);
552
553 /* BSD 鯖でしにそうな文字は残念ながら除外 */
554 assert((dt[i].c[0] & 0x7F)
555 && (dt[i].c[1] & 0x7F)
556 && (dt[i].c[2] & 0x7F)
557 && (dt[i].c[3] & 0x7F));
558
559 /* インデクス */
560 if (!dtidx[c])
561 dtidx[c] = &dt[i];
562
563 if ((0x81 <= c && c <= 0x9F)
564 || (0xE0 <= c && c <= 0xFC))
565 {
566 /* 全角なので、2バイトきまった時点で立てる */
567 cp932[256 * c + dt[i].c[1]] |= KCLS_DT1;
568 }
569 else if (0xA1 <= c && c <= 0xDF)
570 {
571 /* 半角カナ */
572 for (j = 0; j < 256; j++)
573 cp932[256 * c + j] |= KCLS_DT0;
574 }
575 }
576 /* ケツ、ちうか番人 */
577 dtidx[0x100] = &dt[i];
578 #endif
579
580 key->key[8] = 0;
581
582 /* 初期マップを組む */
583 for (i = 0; i < 256; i++)
584 {
585 unsigned bm = 0;
586 key->map[0][i] = 0;
587 for (j = 0; j < 256; j++)
588 bm |= cp932[256 * i + j];
589 key->map[0][i] = bm & (KCLS_AN | KCLS_KA | KCLS_K1
590 | KCLS_DT0
591 );
592 if (i >= 128)
593 key->map[0][i - 128] |= key->map[0][i];
594 }
595
596 key_reset(key, 0);
597 }
598
599 /***************************************************************
600 *
601 * 固定キーの生成
602 *
603 * 一見 Big Endian に非対応のように見えるだろうが
604 * 随所でに散らばっている kludge により
605 * ALU_T が 64 ビットである限り、これで問題なく動く。
606 *
607 */
608
609 void
610 key_init_sk(struct KEY *key)
611 {
612 int i, j;
613 int o;
614 uint64_t m;
615
616 for (i = 5, m = 0xFFFFFFFF00000000ULL;
617 i >= 0;
618 m ^= (m >> (1 << --i)))
619 {
620 o = tr_pc1[7][6 - i] - 1;
621 #if DEBUG>=2
622 printf("%d:%d->%2d: %08X%08X\n",
623 N_Q, i, o,
624 (unsigned)(m >> 32),
625 (unsigned)m);
626 #endif
627 for (j = 0; j < N_Q; j++)
628 if (o < 28)
629 key->k[0][0][o ].q[j] = key->k[0][1][o ].q[j] = m;
630 else
631 key->k[1][0][o - 28].q[j] = key->k[1][1][o - 28].q[j] = m;
632 }
633 #if N_STRIDE==7
634 /* bit 6 は Little Endian として扱う */
635 o = 0;
636 assert(tr_pc1[7][0] - 1 == o);
637 assert(N_Q == 2);
638 key->k[0][0][o].q[0] = key->k[0][1][o].q[0] = 0x0000000000000000ULL;
639 key->k[0][0][o].q[1] = key->k[0][1][o].q[1] = 0xFFFFFFFFFFFFFFFFULL;
640 #endif
641 }
642
643 /***************************************************************
644 *
645 * Salt のセット
646 * オペランドのオフセットを書き換えて回ってるので注意
647 *
648 */
649
650 void
651 set_salt(CODE_T *code,
652 struct CRYPT64_DESC const *desc,
653 uint8_t const *k)
654 {
655 int i, j;
656
657 for (i = 0; i < 2; i++)
658 {
659 unsigned s = k[1 + i] & 255;
660 if (s > 'z')
661 s = 0;
662 else if (s >= 'a')
663 s = s - 'a' + 2 + 10 + 26;
664 else if (s >= 'A')
665 s = s - 'A' + 2 + 10;
666 else if (s >= '.')
667 s = s - '.';
668 else
669 s = 0;
670
671 #if DEBUG>=1
672 printf("Salt %d:%3o\n", i, s & 63);
673 #endif
674 for (j = 0; j < 6; j++)
675 {
676 #if DEBUG>=2
677 //printf("Salt %d:%d %+3d:%+3d",
678 printf("Salt %d:%d %08lX:%08lX",
679 i, j,
680 LSALT(desc, code, 0, i, j, 0),
681 LSALT(desc, code, 0, i, j, 24));
682 #endif
683 if (s & (1 << j))
684 {
685 LSALT(desc, code, 0, i, j, 0) = sizeof(WS_T) * (((4 * i + j + 15) & 31) - 16);
686 LSALT(desc, code, 0, i, j, 24) = sizeof(WS_T) * (((4 * i + j - 1) & 31) - 16);
687 }
688 else
689 {
690 LSALT(desc, code, 0, i, j, 0) = sizeof(WS_T) * (((4 * i + j - 1) & 31) - 16);
691 LSALT(desc, code, 0, i, j, 24) = sizeof(WS_T) * (((4 * i + j + 15) & 31) - 16);
692 }
693 LSALT(desc, code, 0, i, j, 12) = sizeof(WS_T) * (((4 * i + j + 7) & 31) - 16);
694 LSALT(desc, code, 0, i, j, 36) = sizeof(WS_T) * (((4 * i + j + 23) & 31) - 16);
695 #if DEBUG>=2
696 //printf(" => %+3d:%+3d\n",
697 printf(" => %08lX:%08lX\n",
698 LSALT(desc, code, 0, i, j, 0),
699 LSALT(desc, code, 0, i, j, 24));
700 #endif
701 }
702 }
703 }
704
705 /*
706 * Local Variables:
707 * tab-width: 4
708 * End:
709 *
710 * EOF */

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26