summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Palfrader <peter@palfrader.org>2002-06-05 04:05:40 +0000
committerPeter Palfrader <peter@palfrader.org>2002-06-05 04:05:40 +0000
commitecd052098413f87701ba00e28f88563248a177f6 (patch)
tree0b31ba6942617df44e2745014b291df97625021c
Initial Import
-rw-r--r--.cvsignore2
-rw-r--r--COPYING340
-rw-r--r--Echolot/Conf.pm84
-rw-r--r--Echolot/Config.pm48
-rw-r--r--Echolot/Globals.pm37
-rw-r--r--Echolot/Mailin.pm120
-rw-r--r--Echolot/Scheduler.pm150
-rw-r--r--Echolot/Storage/File.pm458
-rw-r--r--Echolot/Tools.pm93
-rw-r--r--LICENSE15
-rw-r--r--README28
-rwxr-xr-xpingd40
-rw-r--r--pingd.conf24
13 files changed, 1439 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..4bda8ff
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,2 @@
+data
+mail
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Echolot/Conf.pm b/Echolot/Conf.pm
new file mode 100644
index 0000000..b19b25c
--- /dev/null
+++ b/Echolot/Conf.pm
@@ -0,0 +1,84 @@
+package Echolot::Conf;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: Conf.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Conf - remailer Configuration/Capabilities
+
+=head1 DESCRIPTION
+
+This package provides functions for requesting, parsing, and analyzing
+remailer-conf and remailer-key replies.
+
+=cut
+
+use strict;
+use warnings;
+use Carp qw{cluck};
+
+
+sub send_requests() {
+ Echolot::Globals::get()->{'storage'}->delay_commit();
+ for my $remailer (Echolot::Globals::get()->{'storage'}->get_addresses()) {
+ next unless ($remailer->{'status'} eq 'active');
+ for my $type (qw{conf key help stats}) {
+ Echolot::Tools::send_message(
+ 'To' => $remailer->{'address'},
+ 'Subject' => 'remailer-'.$type,
+ 'Token' => $type.'.'.$remailer->{'id'})
+ };
+ Echolot::Globals::get()->{'storage'}->decrease_ttl($remailer->{'address'});
+ };
+ Echolot::Globals::get()->{'storage'}->enable_commit();
+};
+
+sub remailer_conf($$$) {
+ my ($conf, $token, $time) = @_;
+
+ my ($id) = $token =~ /^conf\.(\d+)$/;
+ cluck("Could not find id in token '$token'"), return 0 unless defined $id;
+ my ($remailer_type) = ($conf =~ /^\s*Remailer-Type:\s* (.*?) \s*$/imx);
+ cluck("No remailer type found in remailer_conf from '$token'"), return 0 unless defined $remailer_type;
+ my ($remailer_caps) = ($conf =~ /^\s*( \$remailer{".*"} \s*=\s* "<.*@.*>.*"; )\s*$/imx);
+ cluck("No remailer caps found in remailer_conf from '$token'"), return 0 unless defined $remailer_caps;
+ my ($remailer_nick, $remailer_address) = ($remailer_caps =~ /^\s* \$remailer{"(.*)"} \s*=\s* "<(.*@.*)>.*"; \s*$/ix);
+ cluck("No remailer nick found in remailer_caps from '$token': '$remailer_caps'"), return 0 unless defined $remailer_nick;
+ cluck("No remailer address found in remailer_caps from '$token': '$remailer_caps'"), return 0 unless defined $remailer_address;
+
+
+ my $remailer = Echolot::Globals::get()->{'storage'}->get_address_by_id($id);
+ if ($remailer->{'address'} ne $remailer_address) {
+ # Address mismatch -> Ignore reply and add $remailer_address to prospective addresses
+ cluck("Remailer address mismatch $remailer->{'address'} vs $remailer_address. Adding latter to prospective remailers.");
+ Echolot::Globals::get()->{'storage'}->add_prospective_address($remailer_address, 'conf-reply');
+ } else {
+ Echolot::Globals::get()->{'storage'}->restore_ttl( $remailer->{'address'} );
+ Echolot::Globals::get()->{'storage'}->set_caps($remailer_type, $remailer_caps, $remailer_nick, $remailer_address, $time);
+ }
+};
+
+sub remailer_key($$$) {
+ my ($conf, $token, $time) = @_;
+
+ print "Remailer key\n";
+};
+
+sub remailer_stats($$$) {
+ my ($conf, $token, $time) = @_;
+
+ #print "Remailer stats\n";
+};
+
+sub remailer_help($$$) {
+ my ($conf, $token, $time) = @_;
+
+ #print "Remailer help\n";
+};
+
+1;
+# vim: set ts=4 shiftwidth=4:
diff --git a/Echolot/Config.pm b/Echolot/Config.pm
new file mode 100644
index 0000000..f679af8
--- /dev/null
+++ b/Echolot/Config.pm
@@ -0,0 +1,48 @@
+package Echolot::Config;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: Config.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Config - echolot configuration
+
+=head1 DESCRIPTION
+
+=cut
+
+use strict;
+use warnings;
+use XML::Parser;
+use XML::Dumper;
+use Carp;
+
+my $CONFIG;
+
+sub init() {
+ my $DEFAULT;
+ $DEFAULT->{'recipient_delimiter'} = '+';
+ $DEFAULT->{'dev_random'} = '/dev/random';
+ $DEFAULT->{'hash_len'} = 8;
+
+ {
+ my $parser = new XML::Parser(Style => 'Tree');
+ my $tree = $parser->parsefile('pingd.conf');
+ my $dump = new XML::Dumper;
+ $CONFIG = $dump->xml2pl($tree);
+ }
+
+ for my $key (keys %$DEFAULT) {
+ $CONFIG->{$key} = $DEFAULT->{$key} unless defined $CONFIG->{$key};
+ };
+};
+
+sub get() {
+ return $CONFIG;
+};
+
+1;
+# vim: set ts=4 shiftwidth=4:
diff --git a/Echolot/Globals.pm b/Echolot/Globals.pm
new file mode 100644
index 0000000..4b5eb13
--- /dev/null
+++ b/Echolot/Globals.pm
@@ -0,0 +1,37 @@
+package Echolot::Globals;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: Globals.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Globals - echolot global variables
+
+=head1 DESCRIPTION
+
+=cut
+
+use strict;
+use warnings;
+use Carp;
+
+my $GLOBALS;
+
+sub init {
+ my $hostname = `hostname`;
+ $hostname =~ /^([a-zA-Z0-9_-]*)$/;
+ $hostname = $1 || 'unknown';
+ $GLOBALS->{'hostname'} = $hostname;
+ $GLOBALS->{'storage'} = new Echolot::Storage::File ( datadir => Echolot::Config::get()->{'storage'}->{'File'}->{'basedir'} );
+ $GLOBALS->{'internalcounter'} = 1;
+};
+
+sub get() {
+ return $GLOBALS;
+};
+
+1;
+# vim: set ts=4 shiftwidth=4:
diff --git a/Echolot/Mailin.pm b/Echolot/Mailin.pm
new file mode 100644
index 0000000..411433a
--- /dev/null
+++ b/Echolot/Mailin.pm
@@ -0,0 +1,120 @@
+package Echolot::Mailin;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: Mailin.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Mailin - Incoming Mail Dispatcher for Echolot
+
+=head1 DESCRIPTION
+
+
+=cut
+
+use strict;
+use warnings;
+use Carp qw{cluck};
+use English;
+use Echolot::Globals;
+
+sub make_sane_name() {
+ my $result = time().'.'.$PROCESS_ID.'_'.Echolot::Globals::get()->{'internal_counter'}++.'.'.Echolot::Globals::get()->{'hostname'};
+ return $result;
+};
+
+sub sane_move($$) {
+ my ($from, $to) = @_;
+
+ my $link_success = link($from, $to);
+ $link_success or
+ cluck("Cannot link $from to $to: $! - Trying move"),
+ rename($from, $to) or
+ cluck("Renaming $from to $to didn't work either: $!"),
+ return 0;
+
+ $link_success && (unlink($from) or
+ cluck("Cannot unlink $from: $!") );
+ return 1;
+};
+
+sub handle($) {
+ my ($file) = @_;
+
+ open (FH, $file) or
+ cluck("Cannot open file $file: $!"),
+ return 0;
+
+ my $to;
+ while (<FH>) {
+ chomp;
+ last if $_ eq '';
+
+ if (m/^To:\s*(.*?)\s*$/) {
+ $to = $1;
+ };
+ };
+ my $body = join('', <FH>);
+ close (FH) or
+ cluck("Cannot close file $file: $!");
+
+ (defined $to) or
+ cluck("No To header found in $file"),
+ return 0;
+
+ my $delimiter = quotemeta( Echolot::Config::get()->{'recipient_delimiter'});
+ my ($type, $timestamp, $received_hash) = $to =~ /$delimiter (.*) = (\d+) = ([0-9a-f]+) @/x or
+ cluck("Could not parse to header '$to'"),
+ return 0;
+
+ my $token = $type.'='.$timestamp;
+ my $hash = Echolot::Tools::hash($token . Echolot::Globals::get()->{'storage'}->get_secret() );
+ my $cut_hash = substr($hash, 0, Echolot::Config::get()->{'hash_len'});
+
+ ($cut_hash eq $received_hash) or
+ cluck("Hash mismatch in '$to'"),
+ return 0;
+
+ Echolot::Conf::remailer_conf($body, $type, $timestamp), return 1 if ($type =~ /^conf\./);
+ Echolot::Conf::remailer_key($body, $type, $timestamp), return 1 if ($type =~ /^key\./);
+ Echolot::Conf::remailer_help($body, $type, $timestamp), return 1 if ($type =~ /^help\./);
+ Echolot::Conf::remailer_stats($body, $type, $timestamp), return 1 if ($type =~ /^stats\./);
+
+ Echolot::Ping::receive($body, $type, $timestamp), return 1 if ($type =~ /^ping\./);
+
+ cluck("Didn't know what to do with '$to'"),
+ return 0;
+};
+
+sub process() {
+ my $mailindir = Echolot::Config::get()->{'mailindir'};
+ my $targetdir = Echolot::Config::get()->{'mailerrordir'};
+ my @files = ();
+ for my $sub (qw{new cur}) {
+ opendir(DIR, $mailindir.'/'.$sub) or
+ cluck("Cannot open direcotry '$mailindir/$sub': $!"),
+ return 0;
+ push @files, map { $sub.'/'.$_ } grep { ! /^\./ } readdir(DIR);
+ closedir(DIR) or
+ cluck("Cannot close direcotry '$mailindir/$sub': $!");
+ };
+ for my $file (@files) {
+ $file =~ /^(.*)$/s or
+ croak("I really should match here. ('$file').");
+ $file = $1;
+ if (handle($mailindir.'/'.$file)) {
+ unlink($mailindir.'/'.$file);
+ } else {
+ my $name = make_sane_name();
+ sane_move($mailindir.'/'.$file, $targetdir.'/new/'.$name) or
+ cluck("Sane moving of $mailindir/$file to $targetdir/new/$name failed");
+ };
+ };
+};
+
+1;
+
+# vim: set ts=4 shiftwidth=4:
diff --git a/Echolot/Scheduler.pm b/Echolot/Scheduler.pm
new file mode 100644
index 0000000..24ca6e3
--- /dev/null
+++ b/Echolot/Scheduler.pm
@@ -0,0 +1,150 @@
+package Echolot::Scheduler;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: Scheduler.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Scheduler - Task selector/scheduler for echolot
+
+=head1 DESCRIPTION
+
+This package provides several functions for scheduling tasks within
+the ping daemon.
+
+=over
+
+=cut
+
+use strict;
+use warnings;
+use Carp gw{cluck};
+
+my $ORDER = 1;
+
+=item B<new> ()
+
+Creates a new scheduler object.
+
+=cut
+sub new {
+ my ($class, %params) = @_;
+ my $self = {};
+ bless $self, $class;
+ return $self;
+};
+
+=item B<add> (I<name>, I<interval>, I<offset>, I<what>)
+
+Adds a task with I<name> to the list of tasks. Every I<interval> seconds
+I<what> is called. If for example I<interval> is 3600 - meaning I<what>
+should be executed hourly - setting I<offset> to 600 would mean that
+it get's called 10 minutes after the hour.
+
+=cut
+sub add($$$$$) {
+ my ($self, $name, $interval, $offset, $what) = @_;
+
+ if (defined $self->{'tasks'}->{$name}) {
+ @{ $self->{'schedule'} } = grep { $_->{'name'} ne $name } @{ $self->{'schedule'} };
+ };
+
+ $self->{'tasks'}->{$name} =
+ {
+ interval => $interval,
+ offset => $offset,
+ what => $what,
+ order => $ORDER++
+ };
+
+ $self->schedule($name);
+
+ return 1;
+};
+
+=item B<schedule> (I<name>, I<for>)
+
+Internal function.
+
+Schedule execution of I<name> for I<for>. If I<for> is not given it is calculated
+from I<interval> and I<offset> passed to B<new>.
+
+=cut
+sub schedule($$;$) {
+ my ($self, $name, $for) = @_;
+
+ (defined $self->{'tasks'}->{$name}) or
+ cluck("Task $name is not defined"),
+ return 0;
+
+ my $interval = $self->{'tasks'}->{$name}->{'interval'};
+ my $offset = $self->{'tasks'}->{$name}->{'offset'};
+
+
+ unless (defined $for) {
+ my $now = time();
+ $for = $now - $now % $interval + $offset;
+ ($for <= $now) and $for += $interval;
+ };
+
+ push @{ $self->{'schedule'} },
+ {
+ start => $for,
+ order => $self->{'tasks'}->{$name}->{'order'},
+ name => $name
+ };
+
+ @{ $self->{'schedule'} } = sort { $a->{'start'} <=> $b->{'start'} or $a->{'order'} <=> $b->{'order'} }
+ @{ $self->{'schedule'} };
+
+ return 1;
+};
+
+=item B<run> ()
+
+Start the scheduling run.
+
+It will run forever or until a task with I<what> == 'exit' is executed.
+
+=cut
+sub run($) {
+ my ($self) = @_;
+
+ my $task = shift @{ $self->{'schedule'} };
+ (defined $task) or
+ croak("Scheduler is empty"),
+ return 0;
+
+ while(1) {
+ my $now = time();
+ if ($task->{'start'} < $now) {
+ warn("Task $task->{'name'} could not be started on time\n");
+ } else {
+ sleep ($task->{'start'} - $now);
+ };
+
+ $now = $task->{'start'};
+ do {
+ my $name = $task->{'name'};
+ (defined $self->{'tasks'}->{$name}) or
+ warn("Task $task->{'name'} is not defined\n");
+
+ my $what = $self->{'tasks'}->{$name}->{'what'};
+ last if ($what eq 'exit');
+ &$what();
+ $self->schedule($name, $now + $self->{'tasks'}->{$name}->{'interval'});
+
+ $task = shift @{ $self->{'schedule'} };
+ (defined $task) or
+ croak("Scheduler is empty"),
+ return 0;
+ } while ($now == $task->{'start'});
+ };
+
+ return 1;
+};
+
+# vim: set ts=4 shiftwidth=4:
diff --git a/Echolot/Storage/File.pm b/Echolot/Storage/File.pm
new file mode 100644
index 0000000..6e66ec8
--- /dev/null
+++ b/Echolot/Storage/File.pm
@@ -0,0 +1,458 @@
+package Echolot::Storage::File;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: File.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Storage::File - Storage backend for echolot
+
+=head1 DESCRIPTION
+
+This package provides several functions for data storage for echolot.
+
+=over
+
+=cut
+
+use strict;
+use warnings;
+use XML::Parser;
+use XML::Dumper;
+use IO::Handle;
+use English;
+use Carp qw{cluck confess};
+use Fcntl ':flock'; # import LOCK_* constants
+use Fcntl ':seek'; # import LOCK_* constants
+use Echolot::Tools;
+
+=item B<new> (I<%args>)
+
+Creates a new storage backend object.
+args keys:
+
+=over
+
+=item I<datadir>
+
+The basedir where this module may store it's configuration and pinging
+data.
+
+=back
+
+=cut
+
+my $CONSTANTS = {
+ 'metadatafile' => 'metadata'
+};
+
+$ENV{'PATH'} = '/bin:/usr/bin';
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+my $METADATA_VERSION = 1;
+
+my $INTERNAL_COUNT = 1;
+
+sub new {
+ my ($class, %params) = @_;
+ my $self = {};
+ bless $self, $class;
+
+ defined($params{'datadir'}) or
+ confess ('No datadir option passed to new');
+ $self->{'datadir'} = $params{'datadir'};
+ $self->{'DELAY_COMMIT'} = 0;
+
+ $self->delay_commit();
+ $self->metadata_open() or
+ cluck ('Opening Metadata failed. Exiting'),
+ exit 1;
+ $self->metadata_read() or
+ cluck ('Reading Metadata from Storage failed. Exiting'),
+ exit 1;
+ $self->pingdata_open() or
+ cluck ('Opening Ping files failed. Exiting'),
+ exit 1;
+ $self->enable_commit();
+
+ return $self;
+};
+
+sub commit($) {
+ my ($self) = @_;
+
+ return if $self->{'DELAY_COMMIT'};
+ $self->metadata_write();
+};
+
+sub delay_commit($) {
+ my ($self) = @_;
+
+ $self->{'DELAY_COMMIT'}++;
+};
+sub enable_commit($) {
+ my ($self) = @_;
+
+ $self->{'DELAY_COMMIT'}--;
+ $self->commit();
+};
+
+sub finish($) {
+ my ($self) = @_;
+
+ $self->pingdata_close();
+ $self->metadata_write();
+ $self->metadata_close();
+};
+
+
+sub metadata_open($) {
+ my ($self) = @_;
+
+ $self->{'METADATA_FH'} = new IO::Handle;
+ my $filename = $self->{'datadir'} .'/'. $CONSTANTS->{'metadatafile'};
+
+ if ( -e $filename ) {
+ open($self->{'METADATA_FH'}, '+<' . $filename) or
+ cluck("Cannot open $filename for reading: $!"),
+ return 0;
+ } else {
+ open($self->{'METADATA_FH'}, '+>' . $filename) or
+ cluck("Cannot open $filename for reading: $!"),
+ return 0;
+ };
+ flock($self->{'METADATA_FH'}, LOCK_SH) or
+ cluck("Cannot get shared lock on $filename: $!"),
+ return 0;
+};
+
+sub metadata_close($) {
+ my ($self) = @_;
+
+ flock($self->{'METADATA_FH'}, LOCK_UN) or
+ cluck("Error when releasing lock on metadata file: $!"),
+ return -1;
+ close($self->{'METADATA_FH'}) or
+ cluck("Error when closing metadata file: $!"),
+ return 0;
+};
+
+
+sub metadata_read($) {
+ my ($self) = @_;
+
+ $self->{'METADATA'} = ();
+ seek($self->{'METADATA_FH'}, 0, SEEK_SET) or
+ cluck("Cannot seek to start of metadata file: $!"),
+ return 0;
+ eval {
+ my $parser = new XML::Parser(Style => 'Tree');
+ my $tree = $parser->parse( $self->{'METADATA_FH'} );
+ my $dump = new XML::Dumper;
+ $self->{'METADATA'} = $dump->xml2pl($tree);
+ };
+ $EVAL_ERROR and
+ cluck("Error when reading from metadata file: $EVAL_ERROR"),
+ return 0;
+
+ defined($self->{'METADATA'}->{'version'}) or
+ cluck("Stored data lacks version header"),
+ return 0;
+ ($self->{'METADATA'}->{'version'} == ($METADATA_VERSION)) or
+ cluck("Metadata version mismatch ($self->{'METADATA'}->{'version'} vs. $METADATA_VERSION)"),
+ return 0;
+
+
+ defined($self->{'METADATA'}->{'secret'}) or
+ $self->{'METADATA'}->{'secret'} = Echolot::Tools::make_random ( 16, armor => 1 ),
+ $self->commit();
+
+ return 1;
+};
+
+sub metadata_write($) {
+ my ($self) = @_;
+
+ my $dump = new XML::Dumper;
+ my $data = $dump->pl2xml($self->{'METADATA'});
+ my $fh = $self->{'METADATA_FH'};
+
+ seek($fh, 0, SEEK_SET) or
+ cluck("Cannot seek to start of metadata file: $!"),
+ return 0;
+ truncate($fh, 0) or
+ cluck("Cannot truncate metadata file to zero length: $!"),
+ return 0;
+ print($fh "<!-- vim:set syntax=xml: -->\n") or
+ cluck("Error when writing to metadata file: $!"),
+ return 0;
+ print($fh $data) or
+ cluck("Error when writing to metadata file: $!"),
+ return 0;
+
+ return 1;
+};
+
+
+sub pingdata_open($) {
+ my ($self) = @_;
+
+ for my $remailer_name ( keys %{$self->{'METADATA'}->{'remailers'}} ) {
+ for my $key ( keys %{$self->{'METADATA'}->{'remailers'}->{$remailer_name}->{'keys'}} ) {
+ my $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_name}->{'stats'}->{$key};
+ defined($basename) or
+ $basename = $self->{'METADATA'}->{'remailers'}->{$remailer_name}->{'stats'}->{$key} = $remailer_name.'.'.$key.'.'.time.'.'.$PROCESS_ID.'_'.$INTERNAL_COUNT++,
+ $self->commit();
+
+ my $filename = $self->{'datadir'} .'/'. $basename;
+
+ for my $type ('out', 'done') {
+ my $fh = new IO::Handle;
+ if ( -e $filename.'.'.$type ) {
+ open($fh, '+<' . $filename.'.'.$type) or
+ cluck("Cannot open $filename.$type for reading: $!"),
+ return 0;
+ $self->{'PING_FHS'}->{$remailer_name}->{$key}->{$type} = $fh;
+ } else {
+ open($fh, '+>' . $filename.'.'.$type) or
+ cluck("Cannot open $filename.$type for reading: $!"),
+ return 0;
+ $self->{'PING_FHS'}->{$remailer_name}->{$key}->{$type} = $fh;
+ };
+ flock($fh, LOCK_EX) or
+ cluck("Cannot get exclusive lock on $remailer_name $type pings: $!"),
+ return 0;
+ };
+ };
+ };
+ return 1;
+};
+
+sub get_pings($$$$) {
+ my ($self, $remailer_name, $key, $type) = @_;
+
+ defined ($self->{'METADATA'}->{'remailers'}->{$remailer_name}) or
+ cluck ("$remailer_name does not exist in Metadata"),
+ return 0;
+
+ my @pings;
+ my $fh = $self->{'PING_FHS'}->{$remailer_name}->{$key}->{$type};
+
+ defined ($fh) or
+ cluck ("$remailer_name; key=$key has no assigned filehandle for $type pings"),
+ return 0;
+
+ seek($fh, 0, SEEK_SET) or
+ cluck("Cannot seek to start of $remailer_name $type pings: $!"),
+ return 0;
+
+ if ($type eq 'out') {
+ @pings = map {chomp; $_; } <$fh>;
+ } elsif ($type eq 'done') {
+ @pings = map {chomp; my @arr = split (/\s+/, $_, 2); \@arr; } <$fh>;
+ } else {
+ confess("What the hell am I doing here? $remailer_name; $key; $type"),
+ return 0;
+ };
+ return \@pings;
+};
+
+sub pingdata_close() {
+ my ($self) = @_;
+
+ for my $remailer_name ( keys %{$self->{'PING_FHS'}} ) {
+ for my $key ( keys %{$self->{'PING_FHS'}->{$remailer_name}} ) {
+ for my $type ('out', 'done') {
+
+ my $fh = $self->{'PING_FHS'}->{$remailer_name}->{$key}->{$type};
+ flock($fh, LOCK_UN) or
+ cluck("Error when releasing lock on $remailer_name $type pings: $!"),
+ return 0;
+ close ($self->{'PING_FHS'}->{$remailer_name}->{$key}->{$type}) or
+ cluck("Error when closing $remailer_name $type pings: $!"),
+ return 0;
+ };
+ };
+ };
+ return 1;
+};
+
+
+
+
+
+sub register_pingout($$$$) {
+ my ($self, $remailer_name, $key, $sent_time) = @_;
+
+ defined ($self->{'METADATA'}->{'remailers'}->{$remailer_name}) or
+ cluck ("$remailer_name does not exist in Metadata"),
+ return 0;
+
+ my $fh = $self->{'PING_FHS'}->{$remailer_name}->{$key}->{'out'};
+ defined ($fh) or
+ cluck ("$remailer_name; key=$key has no assigned filehandle for outgoing pings"),
+ return 0;
+ seek($fh, 0, SEEK_END) or
+ cluck("Cannot seek to end of $remailer_name out pings: $!"),
+ return 0;
+ print($fh $sent_time."\n") or
+ cluck("Error when writing to $remailer_name out pings: $!"),
+ return 0;
+
+ return 1;
+};
+
+sub register_pingdone($$$$$) {
+ my ($self, $remailer_name, $key, $sent_time, $latency) = @_;
+
+ defined ($self->{'METADATA'}->{'remailers'}->{$remailer_name}) or
+ cluck ("$remailer_name does not exist in Metadata"),
+ return 0;
+
+ my $outpings = $self->get_pings($remailer_name, $key, 'out');
+ my $origlen = scalar (@$outpings);
+ @$outpings = grep { $_ != $sent_time } @$outpings;
+ ($origlen == scalar (@$outpings)) and
+ warn("No ping outstanding for $remailer_name, $key, $sent_time\n"),
+ return 1;
+
+ # write ping to done
+ my $fh = $self->{'PING_FHS'}->{$remailer_name}->{$key}->{'done'};
+ defined ($fh) or
+ cluck ("$remailer_name; key=$key has no assigned filehandle for done pings"),
+ return 0;
+ seek($fh, 0, SEEK_END) or
+ cluck("Cannot seek to end of $remailer_name out pings: $!"),
+ return 0;
+ print($fh $sent_time." ".$latency."\n") or
+ cluck("Error when writing to $remailer_name out pings: $!"),
+ return 0;
+
+ # rewrite outstanding pings
+ $fh = $self->{'PING_FHS'}->{$remailer_name}->{$key}->{'out'};
+ defined ($fh) or
+ cluck ("$remailer_name; key=$key has no assigned filehandle for out pings"),
+ return 0;
+ seek($fh, 0, SEEK_SET) or
+ cluck("Cannot seek to start of outgoing pings file for remailer $remailer_name; key=$key: $!"),
+ return 0;
+ truncate($fh, 0) or
+ cluck("Cannot truncate outgoing pings file for remailer $remailer_name; key=$key file to zero length: $!"),
+ return 0;
+ print($fh (join "\n", @$outpings),"\n") or
+ cluck("Error when writing to outgoing pings file for remailer $remailer_name; key=$key file: $!"),
+ return 0;
+
+ return 1;
+};
+
+sub add_prospective_address($$$) {
+ my ($self, $addr, $where) = @_;
+
+ push @{ $self->{'METADATA'}->{'prostective_addresses'} },
+ { 'address' => $addr,
+ 'where' => $where };
+ $self->commit();
+};
+
+sub get_addresses($) {
+ my ($self) = @_;
+
+ my @addresses = keys %{$self->{'METADATA'}->{'addresses'}};
+ my @return_data = map { my %tmp = %{$self->{'METADATA'}->{'addresses'}->{$_}}; $tmp{'address'} = $_; \%tmp; } @addresses;
+ return @return_data;
+};
+
+sub get_address_by_id($$) {
+ my ($self, $id) = @_;
+
+ my @addresses = grep {$self->{'METADATA'}->{'addresses'}->{$_}->{'id'} = $id}
+ keys %{$self->{'METADATA'}->{'addresses'}};
+ return undef unless (scalar @addresses);
+ if (scalar @addresses >= 2) {
+ cluck("Searching for address by id '$id' gives more than one result");
+ };
+ my %return_data = %{$self->{'METADATA'}->{'addresses'}->{$addresses[0]}};
+ $return_data{'address'} = $addresses[0];
+ return \%return_data;
+};
+
+sub decrease_ttl($$) {
+ my ($self, $address) = @_;
+
+ defined ($self->{'METADATA'}->{'addresses'}->{$address}) or
+ cluck ("$address does not exist in Metadata address list"),
+ return 0;
+ $self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} --;
+ $self->{'METADATA'}->{'addresses'}->{$address}->{'status'} = 'disabled'
+ if ($self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} <= 0);
+ $self->commit();
+ return 1;
+};
+
+sub restore_ttl($$) {
+ my ($self, $address) = @_;
+
+ defined ($self->{'METADATA'}->{'addresses'}->{$address}) or
+ cluck ("$address does not exist in Metadata address list"),
+ return 0;
+ $self->{'METADATA'}->{'addresses'}->{$address}->{'ttl'} = Echolot::Config::get()->{'addresses_default_ttl'};
+ $self->commit();
+ return 1;
+};
+
+sub set_caps($$$$$$) {
+ my ($self, $type, $caps, $nick, $address, $timestamp) = @_;
+
+ if (! defined $self->{'METADATA'}->{'remailers'}->{$address}) {
+ $self->{'METADATA'}->{'remailers'}->{$address} =
+ {
+ status => 'active',
+ pingit => Echolot::Config::get()->{'ping_new'},
+ showit => Echolot::Config::get()->{'show_new'},
+ conf => {
+ nick => $nick,
+ type => $type,
+ capabilities => $caps,
+ last_update => $timestamp
+ }
+ };
+ } else {
+ my $conf = $self->{'METADATA'}->{'remailers'}->{$address}->{'conf'};
+ if ($conf->{'last_update'} >= $timestamp) {
+ warn ("Stored data is already newer for remailer $nick\n");
+ return 1;
+ };
+ $conf->{'last_update'} = $timestamp;
+ if ($conf->{'nick'} ne $nick) {
+ warn ($conf->{'nick'}." was renamed to $nick\n");
+ $conf->{'nick'} = $nick;
+ };
+ if ($conf->{'capabilities'} ne $caps) {
+ warn ("$nick has a new caps string '$caps' old: '".$conf->{'capabilities'}."'\n");
+ $conf->{'capabilities'} = $caps;
+ };
+ if ($conf->{'type'} ne $type) {
+ warn ("$nick has a new type string '$type'\n");
+ $conf->{'type'} = $type;
+ };
+ };
+ $self->commit();
+
+ return 1;
+};
+
+sub get_secret($) {
+ my ($self) = @_;
+
+ return $self->{'METADATA'}->{'secret'};
+};
+
+=back
+
+=cut
+
+# vim: set ts=4 shiftwidth=4:
diff --git a/Echolot/Tools.pm b/Echolot/Tools.pm
new file mode 100644
index 0000000..06c6638
--- /dev/null
+++ b/Echolot/Tools.pm
@@ -0,0 +1,93 @@
+package Echolot::Tools;
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: Tools.pm,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+=pod
+
+=head1 Name
+
+Echolot::Tools - Tools for echolot
+
+=head1 DESCRIPTION
+
+
+=cut
+
+use strict;
+use warnings;
+use Carp qw{cluck};
+use Digest::MD5 qw{};
+use Mail::Internet;
+
+sub hash($) {
+ my ($data) = @_;
+ ($data) = $data =~ m/(.*)/s; # untaint
+ my $hash = Digest::MD5::md5_hex($data);
+ return $hash;
+};
+
+sub make_random($;%) {
+ my ($length, %args) = @_;
+
+ my $random;
+
+ open (FH, Echolot::Config::get()->{'dev_random'}) or
+ cluck("Cannot open ".Echolot::Config::get()->{'dev_random'}." for reading: $!"),
+ return 0;
+ read(FH, $random, $length) or
+ cluck("Cannot read from ".Echolot::Config::get()->{'dev_random'}.": $!"),
+ return 0;
+ close (FH) or
+ cluck("Cannot close ".Echolot::Config::get()->{'dev_random'}.": $!"),
+ return 0;
+
+ $random = unpack('H*', $random)
+ if ($args{'armor'} == 1);
+
+ return $random;
+};
+
+
+sub send_message(%) {
+ my (%args) = @_;
+
+ defined($args{'To'}) or
+ cluck ('No recipient address given'),
+ return 0;
+ $args{'Subject'} = '' unless (defined $args{'Subject'});
+ $args{'Body'} = '' unless (defined $args{'Body'});
+ if (defined $args{'Token'}) {
+ my $token = $args{'Token'}.'='.time();
+ my $hash = hash($token . Echolot::Globals::get()->{'storage'}->get_secret() );
+ my $cut_hash = substr($hash, 0, Echolot::Config::get()->{'hash_len'});
+ my $complete_token = $token.'='.$cut_hash;
+ $args{'From'} =
+ Echolot::Config::get()->{'my_localpart'}.
+ Echolot::Config::get()->{'recipient_delimiter'}.
+ $complete_token.
+ '@'.
+ Echolot::Config::get()->{'my_domain'};
+ } else {
+ $args{'From'} =
+ Echolot::Config::get()->{'my_localpart'}.
+ '@'.
+ Echolot::Config::get()->{'my_domain'};
+ };
+ $args{'Subject'} = 'none' unless (defined $args{'Subject'});
+
+ my $message = "To: $args{'To'}\n";
+ $message .= "From: $args{'From'}\n";
+ $message .= "Subject: $args{'Subject'}\n";
+ $message .= "\n".$args{'Body'};
+
+ my @lines = split (/\n/, $message);
+ my $mail = new Mail::Internet ( \@lines );
+
+ $mail->smtpsend( Host => Echolot::Config::get()->{'smarthost'} );
+};
+
+1;
+
+# vim: set ts=4 shiftwidth=4:
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a45864d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ Echolot is (c) 2002 Peter Palfrader <peter@palfrader.org>
+
+ Echolot is free software. you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
diff --git a/README b/README
new file mode 100644
index 0000000..5f9809f
--- /dev/null
+++ b/README
@@ -0,0 +1,28 @@
+Echolot is a Pinger for anonymous remailers.
+
+A Pinger in the context of anonymous remailers is a program that regularily
+sends messages through remailers to check their reliability. It then
+calculates reliability statistics which are used by remailer clients to
+choose the chain of remailers to use.
+
+Additionally it collects configuration parameters and keys of all remailers
+and offers them in a format readable by remailer clients.
+
+
+This is echolot2. Besides the name, author and purpose this has nothing to do
+with echolot1. It's written from scratch.
+
+LICENSE
+-------
+Please see the file named "LICENSE".
+
+
+REQUIREMENTS
+------------
+FIXME
+
+
+SETUP
+-----
+FIXME
+
diff --git a/pingd b/pingd
new file mode 100755
index 0000000..21ce893
--- /dev/null
+++ b/pingd
@@ -0,0 +1,40 @@
+#!/usr/bin/perl -wT
+
+# (c) 2002 Peter Palfrader <peter@palfrader.org>
+# $Id: pingd,v 1.1 2002/06/05 04:05:40 weasel Exp $
+#
+
+use strict;
+use XML::Parser;
+use XML::Dumper;
+use Getopt::Long;
+use lib '.';
+use Echolot::Config;
+use Echolot::Globals;
+use Echolot::Storage::File;
+#use Echolot::Scheduler;
+use Echolot::Conf;
+use Echolot::Mailin;
+
+$ENV{'PATH'} = '/bin:/usr/bin';
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+Echolot::Config::init();
+Echolot::Globals::init();
+
+#my $storage = new Echolot::Storage::File ( datadir => 'data' );
+#my $conf = new Echolot::Conf( storage => $storage );
+
+#$conf->send_requests();
+
+#my @addr = $storage->get_query_addresses();
+
+require Data::Dumper;
+print Data::Dumper->Dump([Echolot::Globals::get()]);
+#Echolot::Conf::send_requests();
+Echolot::Mailin::process();
+
+#$storage->commit();
+
+
+# vim: set ts=4 shiftwidth=4:
diff --git a/pingd.conf b/pingd.conf
new file mode 100644
index 0000000..5760c57
--- /dev/null
+++ b/pingd.conf
@@ -0,0 +1,24 @@
+<!-- vim:set syn=xml: -->
+
+<perldata>
+ <hash>
+ <item key="storage">
+ <hash>
+ <item key="backend">File</item>
+ <item key="File">
+ <hash>
+ <item key="basedir">data</item>
+ </hash>
+ </item>
+ </hash>
+ </item>
+ <item key="addresses_default_ttl">5</item>
+ <item key="smarthost">localhost</item>
+ <item key="my_localpart">pinger</item>
+ <item key="my_domain">marvin.palfrader.org</item>
+ <item key="mailindir">mail/IN</item>
+ <item key="mailerrordir">mail/ERROR</item>
+ <item key="ping_new">1</item>
+ <item key="show_new">0</item>
+ </hash>
+</perldata>