/[echolot]/trunk/Echolot/Storage/File.pm
ViewVC logotype

Contents of /trunk/Echolot/Storage/File.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 26 - (show annotations) (download)
Tue Jun 11 16:00:00 2002 UTC (10 years, 11 months ago) by weasel
File size: 19104 byte(s)
Also return last_update with get_key
1 package Echolot::Storage::File;
2
3 # (c) 2002 Peter Palfrader <peter@palfrader.org>
4 # $Id: File.pm,v 1.7 2002/06/11 16:00:00 weasel Exp $
5 #
6
7 =pod
8
9 =head1 Name
10
11 Echolot::Storage::File - Storage backend for echolot
12
13 =head1 DESCRIPTION
14
15 This package provides several functions for data storage for echolot.
16
17 =over
18
19 =cut
20
21 use strict;
22 use warnings;
23 use XML::Parser;
24 use XML::Dumper;
25 use IO::Handle;
26 use English;
27 use Carp qw{cluck confess};
28 use Fcntl ':flock'; # import LOCK_* constants
29 use Fcntl ':seek'; # import LOCK_* constants
30 use Echolot::Tools;
31
32 =item B<new> (I<%args>)
33
34 Creates a new storage backend object.
35 args keys:
36
37 =over
38
39 =item I<datadir>
40
41 The basedir where this module may store it's configuration and pinging
42 data.
43
44 =back
45
46 =cut
47
48 my $CONSTANTS = {
49 'metadatafile' => 'metadata'
50 };
51
52 $ENV{'PATH'} = '/bin:/usr/bin';
53 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
54
55 my $METADATA_VERSION = 1;
56
57 sub new {
58 my ($class, %params) = @_;
59 my $self = {};
60 bless $self, $class;
61
62 defined($params{'datadir'}) or
63 confess ('No datadir option passed to new');
64 $self->{'datadir'} = $params{'datadir'};
65 $self->{'DELAY_COMMIT'} = 0;
66
67 $self->delay_commit();
68 $self->metadata_open() or
69 cluck ('Opening Metadata failed. Exiting'),
70 exit 1;
71 $self->metadata_read() or
72 cluck ('Reading Metadata from Storage failed. Exiting'),
73 exit 1;
74 $self->pingdata_open() or
75 cluck ('Opening Ping files failed. Exiting'),
76 exit 1;
77 $self->enable_commit();
78
79 return $self;
80 };
81
82 sub commit($) {
83 my ($self) = @_;
84
85 if ($self->{'DELAY_COMMIT'}) {
86 $self->{'COMMIT_PENDING'} = 1;
87 return;
88 };
89 $self->metadata_write();
90 $self->{'COMMIT_PENDING'} = 0;
91 };
92
93 sub delay_commit($) {
94 my ($self) = @_;
95
96 $self->{'DELAY_COMMIT'}++;
97 };
98 sub enable_commit($) {
99 my ($self) = @_;
100
101 $self->{'DELAY_COMMIT'}--;
102 $self->commit() if ($self->{'COMMIT_PENDING'} && ! $self->{'DELAY_COMMIT'});
103 };
104
105 sub finish($) {
106 my ($self) = @_;
107
108 $self->pingdata_close();
109 $self->metadata_write();
110 $self->metadata_close();
111 };
112
113
114
115
116 sub metadata_open($) {
117 my ($self) = @_;
118
119 $self->{'METADATA_FH'} = new IO::Handle;
120 my $filename = $self->{'datadir'} .'/'. $CONSTANTS->{'metadatafile'};
121
122 if ( -e $filename ) {
123 open($self->{'METADATA_FH'}, '+<' . $filename) or
124 cluck("Cannot open $filename for reading: $!"),
125 return 0;
126 } else {
127 open($self->{'METADATA_FH'}, '+>' . $filename) or
128 cluck("Cannot open $filename for reading: $!"),
129 return 0;
130 };
131 flock($self->{'METADATA_FH'}, LOCK_SH) or
132 cluck("Cannot get shared lock on $filename: $!"),
133 return 0;
134 };
135
136 sub metadata_close($) {
137 my ($self) = @_;
138
139 flock($self->{'METADATA_FH'}, LOCK_UN) or
140 cluck("Error when releasing lock on metadata file: $!"),
141 return -1;
142 close($self->{'METADATA_FH'}) or
143 cluck("Error when closing metadata file: $!"),
144 return 0;
145 };
146
147
148 sub metadata_read($) {
149 my ($self) = @_;
150
151 $self->{'METADATA'} = ();
152 seek($self->{'METADATA_FH'}, 0, SEEK_SET) or
153 cluck("Cannot seek to start of metadata file: $!"),
154 return 0;
155 eval {
156 my $parser = new XML::Parser(Style => 'Tree');
157 my $tree = $parser->parse( $self->{'METADATA_FH'} );
158 my $dump = new XML::Dumper;
159 $self->{'METADATA'} = $dump->xml2pl($tree);
160 };
161 $EVAL_ERROR and
162 cluck("Error when reading from metadata file: $EVAL_ERROR"),
163 return 0;
164
165 defined($self->{'METADATA'}->{'version'}) or
166 cluck("Stored data lacks version header"),
167 return 0;
168 ($self->{'METADATA'}->{'version'} == ($METADATA_VERSION)) or
169 cluck("Metadata version mismatch ($self->{'METADATA'}->{'version'} vs. $METADATA_VERSION)"),
170 return 0;
171
172
173 defined($self->{'METADATA'}->{'secret'}) or
174 $self->{'METADATA'}->{'secret'} = Echolot::Tools::make_random ( 16, armor => 1 ),
175 $self->commit();
176
177 return 1;
178 };
179
180 sub metadata_write($) {
181 my ($self) = @_;
182
183 # FIXME XML::Dumper bug workaround
184 # There is a bug in pl2xml that changes data passed (cf. Debian Bug #148969 and #148970
185 # at http://bugs.debian.org/148969 and http://bugs.debian.org/148970
186 require Data::Dumper;
187 my $storedata;
188 eval ( Data::Dumper->Dump( [ $self->{'METADATA'} ], [ 'storedata' ] ));
189
190 my $dump = new XML::Dumper;
191 my $data = $dump->pl2xml($storedata);
192 my $fh = $self->{'METADATA_FH'};
193
194 seek($fh, 0, SEEK_SET) or
195 cluck("Cannot seek to start of metadata file: $!"),
196 return 0;
197 truncate($fh, 0) or
198 cluck("Cannot truncate metadata file to zero length: $!"),
199 return 0;
200 print($fh "<!-- vim:set syntax=xml: -->\n") or
201 cluck("Error when writing to metadata file: $!"),
202 return 0;
203 print($fh $data) or
204 cluck("Error when writing to metadata file: $!"),
205 return 0;
206 $fh->flush();
207
208 return 1;
209 };
210
211
212
213
214
215 sub pingdata_open_one($$$$) {
216 my ($self, $remailer_addr, $type, $key) = @_;
217
218 defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}) or
219 cluck ("$remailer_addr does not exist in Metadata"),
220 return 0;
221 defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}) or
222 cluck ("$remailer_addr has no keys in Metadata"),
223 return 0;
224 defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}) or
225 cluck ("$remailer_addr type $type does not exist in Metadata"),
226 return 0;
227 defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}->{$type}->{$key}) or
228 cluck ("$remailer_addr type $type key $key does not exist in Metadata"),
229 return 0;
230
231
232 my $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'stats'}->{$type}->{$key};
233 defined($basename) or
234 $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'stats'}->{$type}->{$key} = $remailer_addr.'.'.$key.'.'.time.'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internalcounter'}++,
235 $self->commit();
236
237 my $filename = $self->{'datadir'} .'/'. $basename;
238
239 for my $direction ('out', 'done') {
240 my $fh = new IO::Handle;
241 if ( -e $filename.'.'.$direction ) {
242 open($fh, '+<' . $filename.'.'.$direction) or
243 cluck("Cannot open $filename.$direction for reading: $!"),
244 return 0;
245 $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction} = $fh;
246 } else {
247 open($fh, '+>' . $filename.'.'.$direction) or
248 cluck("Cannot open $filename.$direction for reading: $!"),
249 return 0;
250 $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction} = $fh;
251 };
252 flock($fh, LOCK_EX) or
253 cluck("Cannot get exclusive lock on $remailer_addr $type $key $direction pings: $!"),
254 return 0;
255 };
256
257 return 1;
258 };
259
260 sub pingdata_open($) {
261 my ($self) = @_;
262
263 for my $remailer_addr ( keys %{$self->{'METADATA'}->{'remailers'}} ) {
264 for my $type ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{'keys'}} ) {
265 for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_addr}->{$type}->{'keys'}} ) {
266 $self->pingdata_open_one($remailer_addr, $type, $key);
267 };
268 };
269 };
270 return 1;
271 };
272
273 sub get_ping_fh($$$$$) {
274 my ($self, $remailer_addr, $type, $key, $direction) = @_;
275
276 defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}) or
277 cluck ("$remailer_addr does not exist in Metadata"),
278 return 0;
279
280 my @pings;
281 my $fh = $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction};
282
283 defined ($fh) or
284 $self->pingdata_open_one($remailer_addr, $type, $key),
285 $fh = $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction};
286 defined ($fh) or
287 cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for $direction pings"),
288 return 0;
289
290 return $fh;
291 };
292
293 sub pingdata_close() {
294 my ($self) = @_;
295
296 for my $remailer_addr ( keys %{$self->{'PING_FHS'}} ) {
297 for my $type ( keys %{$self->{'PING_FHS'}->{$remailer_addr}} ) {
298 for my $key ( keys %{$self->{'PING_FHS'}->{$remailer_addr}->{$type}} ) {
299 for my $direction ( keys %{$self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}} ) {
300
301 my $fh = $self->{'PING_FHS'}->{$remailer_addr}->{$type}->{$key}->{$direction};
302 flock($fh, LOCK_UN) or
303 cluck("Error when releasing lock on $remailer_addr type $type key $key direction $direction pings: $!"),
304 return 0;
305 close ($fh) or
306 cluck("Error when closing $remailer_addr type $type key $key direction $direction pings: $!"),
307 return 0;
308 };
309 };
310 };
311 };
312 return 1;
313 };
314
315 sub get_pings($$$$$) {
316 my ($self, $remailer_addr, $type, $key, $direction) = @_;
317
318 my @pings;
319
320 my $fh = $self->get_ping_fh($remailer_addr, $type, $key, $direction) or
321 cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for $direction pings"),
322 return 0;
323
324 seek($fh, 0, SEEK_SET) or
325 cluck("Cannot seek to start of $remailer_addr type $type key $key direction $direction pings: $!"),
326 return 0;
327
328 if ($direction eq 'out') {
329 @pings = map {chomp; $_; } <$fh>;
330 } elsif ($direction eq 'done') {
331 @pings = map {chomp; my @arr = split (/\s+/, $_, 2); \@arr; } <$fh>;
332 } else {
333 confess("What the hell am I doing here? $remailer_addr; $type; $key; $direction"),
334 return 0;
335 };
336 return @pings;
337 };
338
339
340
341
342
343
344 sub register_pingout($$$$) {
345 my ($self, $remailer_addr, $type, $key, $sent_time) = @_;
346
347 #require Data::Dumper;
348 #print Data::Dumper->Dump( [ $self->{'PING_FHS'} ] );
349
350 my $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'out') or
351 cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for out pings"),
352 return 0;
353
354 seek($fh, 0, SEEK_END) or
355 cluck("Cannot seek to end of $remailer_addr; type=$type; key=$key; out pings: $!"),
356 return 0;
357 print($fh $sent_time."\n") or
358 cluck("Error when writing to $remailer_addr; type=$type; key=$key; out pings: $!"),
359 return 0;
360 $fh->flush();
361 print "registering pingout at $sent_time for $remailer_addr\n";
362
363 return 1;
364 };
365
366 sub register_pingdone($$$$$) {
367 my ($self, $remailer_addr, $type, $key, $sent_time, $latency) = @_;
368
369 defined ($self->{'METADATA'}->{'remailers'}->{$remailer_addr}) or
370 cluck ("$remailer_addr does not exist in Metadata"),
371 return 0;
372
373 my @outpings = $self->get_pings($remailer_addr, $type, $key, 'out');
374 my $origlen = scalar (@outpings);
375 @outpings = grep { print "_: '$_'; st: '$sent_time'\n" ; $_ != $sent_time } @outpings;
376 ($origlen == scalar (@outpings)) and
377 warn("No ping outstanding for $remailer_addr, $key, $sent_time\n"),
378 return 1;
379
380 # write ping to done
381 my $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'done') or
382 cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for done pings"),
383 return 0;
384 seek($fh, 0, SEEK_END) or
385 cluck("Cannot seek to end of $remailer_addr out pings: $!"),
386 return 0;
387 print($fh $sent_time." ".$latency."\n") or
388 cluck("Error when writing to $remailer_addr out pings: $!"),
389 return 0;
390 $fh->flush();
391
392 # rewrite outstanding pings
393 $fh = $self->get_ping_fh($remailer_addr, $type, $key, 'out') or
394 cluck ("$remailer_addr; type=$type; key=$key has no assigned filehandle for out pings"),
395 return 0;
396 seek($fh, 0, SEEK_SET) or
397 cluck("Cannot seek to start of outgoing pings file for remailer $remailer_addr; key=$key: $!"),
398 return 0;
399 truncate($fh, 0) or
400 cluck("Cannot truncate outgoing pings file for remailer $remailer_addr; key=$key file to zero length: $!"),
401 return 0;
402 print($fh (join "\n", @outpings), (scalar @outpings ? "\n" : '') ) or
403 cluck("Error when writing to outgoing pings file for remailer $remailer_addr; key=$key file: $!"),
404 return 0;
405 $fh->flush();
406 print "registering pingdone from $sent_time with latency $latency for $remailer_addr\n";
407
408 return 1;
409 };
410
411
412
413
414
415 sub add_prospective_address($$$$) {
416 my ($self, $addr, $reason, $additional) = @_;
417
418 return 1 if defined $self->{'METADATA'}->{'addresses'}->{$addr};
419 push @{ $self->{'METADATA'}->{'prospective_addresses'}{$addr} }, time().'; '. $reason. '; '. $additional;
420 $self->commit();
421 };
422
423 sub get_addresses($) {
424 my ($self) = @_;
425
426 my @addresses = keys %{$self->{'METADATA'}->{'addresses'}};
427 my @return_data = map { my %tmp = %{$self->{'METADATA'}->{'addresses'}->{$_}}; $tmp{'address'} = $_; \%tmp; } @addresses;
428 return @return_data;
429 };
430
431 sub get_address_by_id($$) {
432 my ($self, $id) = @_;
433
434 my @addresses = grep {$self->{'METADATA'}->{'addresses'}->{$_}->{'id'} == $id}
435 keys %{$self->{'METADATA'}->{'addresses'}};
436 return undef unless (scalar @addresses);
437 if (scalar @addresses >= 2) {
438 cluck("Searching for address by id '$id' gives more than one result");
439 };
440 my %return_data = %{$self->{'METADATA'}->{'addresses'}->{$addresses[0]}};
441 $return_data{'address'} = $addresses[0];
442 return \%return_data;
443 };
444
445 sub decrease_ttl($$) {
446 my ($self, $address) = @_;
447
448 defined ($self->{'METADATA'}->{'addresses'}->{$address}) or
449 cluck ("$address does not exist in Metadata address list"),
450 return 0;
451 $self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} --;
452 $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} = 'disabled',
453 warn("Remailer $address disablesd: ttl expired\n")
454 if ($self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} <= 0);
455 # FIXME have proper logging
456 $self->commit();
457 return 1;
458 };
459
460 sub restore_ttl($$) {
461 my ($self, $address) = @_;
462
463 defined ($self->{'METADATA'}->{'addresses'}->{$address}) or
464 cluck ("$address does not exist in Metadata address list"),
465 return 0;
466 $self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} = Echolot::Config::get()->{'addresses_default_ttl'};
467 $self->commit();
468 return 1;
469 };
470
471 sub set_caps($$$$$$) {
472 my ($self, $type, $caps, $nick, $address, $timestamp) = @_;
473
474 if (! defined $self->{'METADATA'}->{'remailers'}->{$address}) {
475 $self->{'METADATA'}->{'remailers'}->{$address} =
476 {
477 status => 'active',
478 pingit => Echolot::Config::get()->{'ping_new'},
479 showit => Echolot::Config::get()->{'show_new'},
480 conf => {
481 nick => $nick,
482 type => $type,
483 capabilities => $caps,
484 last_update => $timestamp
485 }
486 };
487 } else {
488 my $conf = $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'};
489 if ($conf->{'last_update'} >= $timestamp) {
490 warn ("Stored data is already newer for remailer $nick\n");
491 return 1;
492 };
493 $conf->{'last_update'} = $timestamp;
494 if ($conf->{'nick'} ne $nick) {
495 warn ($conf->{'nick'}." was renamed to $nick\n");
496 $conf->{'nick'} = $nick;
497 };
498 if ($conf->{'capabilities'} ne $caps) {
499 warn ("$nick has a new caps string '$caps' old: '".$conf->{'capabilities'}."'\n");
500 $conf->{'capabilities'} = $caps;
501 };
502 if ($conf->{'type'} ne $type) {
503 warn ("$nick has a new type string '$type'\n");
504 $conf->{'type'} = $type;
505 };
506 };
507 $self->commit();
508
509 return 1;
510 };
511
512 sub set_key($$$$$$$$$) {
513 my ($self, $type, $nick, $address, $key, $keyid, $version, $caps, $summary, $timestamp) = @_;
514
515 if (! defined $self->{'METADATA'}->{'remailers'}->{$address}) {
516 $self->{'METADATA'}->{'remailers'}->{$address} =
517 {
518 status => 'active',
519 pingit => Echolot::Config::get()->{'ping_new'},
520 showit => Echolot::Config::get()->{'show_new'},
521 };
522 };
523
524 if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}) {
525 $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'} = {};
526 };
527 if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}) {
528 $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type} = {};
529 };
530
531 if (! defined $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}->{$keyid}) {
532 $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}->{$keyid} =
533 {
534 key => $key,
535 summary => $summary,
536 nick => $nick,
537 last_update => $timestamp
538 };
539 } else {
540 my $keyref = $self->{'METADATA'}->{'remailers'}->{$address}->{'keys'}->{$type}->{$keyid};
541 if ($keyref->{'last_update'} >= $timestamp) {
542 warn ("Stored data is already newer for remailer $nick\n");
543 return 1;
544 };
545 $keyref->{'last_update'} = $timestamp;
546 if ($keyref->{'nick'} ne $nick) {
547 warn ("$nick has a new key nick string '$nick' old: '".$keyref->{'nick'}."'\n");
548 $keyref->{'nick'} = $nick;
549 };
550 if ($keyref->{'summary'} ne $summary) {
551 warn ("$nick has a new key summary string '$summary' old: '".$keyref->{'summary'}."'\n");
552 $keyref->{'summary'} = $summary;
553 };
554 if ($keyref->{'key'} ne $key) {
555 warn ("$nick has a new key string '$key' old: '".$keyref->{'key'}."' - This probably should not happen\n");
556 $keyref->{'key'} = $key;
557 };
558 };
559 $self->commit();
560
561 return 1;
562 };
563
564 sub get_secret($) {
565 my ($self) = @_;
566
567 return $self->{'METADATA'}->{'secret'};
568 };
569
570 sub get_remailers($) {
571 my ($self) = @_;
572
573 my @remailers = keys %{$self->{'METADATA'}->{'remailers'}};
574 return @remailers;
575 };
576
577 sub get_types($$) {
578 my ($self, $remailer) = @_;
579
580 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}) or
581 cluck ("$remailer does not exist in Metadata remailer list"),
582 return 0;
583
584 return () unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'};
585 my @types = keys %{$self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}};
586 return @types;
587 };
588
589 sub has_type($$$) {
590 my ($self, $remailer, $type) = @_;
591
592 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}) or
593 cluck ("$remailer does not exist in Metadata remailer list"),
594 return 0;
595
596 return 0 unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'};
597 return 0 unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type};
598 return 0 unless scalar keys %{$self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}};
599 return 1;
600 };
601
602 sub get_keys($$) {
603 my ($self, $remailer, $type) = @_;
604
605 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}) or
606 cluck ("$remailer does not exist in Metadata remailer list"),
607 return 0;
608
609 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}) or
610 cluck ("$remailer does not have type '$type' in Metadata remailer list"),
611 return 0;
612
613 my @keys = keys %{$self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}};
614 return @keys;
615 };
616
617 sub get_key($$$$) {
618 my ($self, $remailer, $type, $key) = @_;
619
620 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}) or
621 cluck ("$remailer does not exist in Metadata remailer list"),
622 return 0;
623
624 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}) or
625 cluck ("$remailer does not have type '$type' in Metadata remailer list"),
626 return 0;
627
628 defined ($self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}) or
629 cluck ("$remailer does not have key '$key' in type '$type' in Metadata remailer list"),
630 return 0;
631
632 my %result = (
633 summary => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'summary'},
634 key => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'key'},
635 nick => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'nick'},
636 last_update => $self->{'METADATA'}->{'remailers'}->{$remailer}->{'keys'}->{$type}->{$key}->{'last_update'}
637 );
638
639 return %result;
640 };
641
642 sub get_capabilities($$) {
643 my ($self, $remailer) = @_;
644
645 return undef unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'};
646 return $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'}->{'capabilities'};
647 };
648
649 sub get_nick($$) {
650 my ($self, $remailer) = @_;
651
652 return undef unless defined $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'};
653 return $self->{'METADATA'}->{'remailers'}->{$remailer}->{'conf'}->{'nick'};
654 };
655
656 =back
657
658 =cut
659
660 # vim: set ts=4 shiftwidth=4:

  ViewVC Help
Powered by ViewVC 1.1.5