The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

#!/usr/bin/perl -w
#
# Copyright (C) 2002-2020 National Marrow Donor Program. All rights reserved.
#
# For a description of this program, please refer to the POD documentation
# embedded at the bottom of the file (e.g. perldoc ecs_proc_meta).
use EMDIS::ECS qw($ECS_CFG $ECS_NODE_TBL load_ecs_config log_info
move_to_dir format_doc_filename format_msg_filename send_admin_email);
use strict;
use vars qw($err $filename $message_content $msg $msg_type $node $sender);
# redirect STDERR to STDOUT
open STDERR, ">&STDOUT" or die "Unable to dup STDOUT: $!\n";
select STDERR; $| = 1; # make unbuffered
select STDOUT; $| = 1; # make unbuffered
# process command line arguments
my $prg = basename $0;
my $usage = "Usage: $prg [--config ecs_cfg] <filename> <sender>\n" .
"For help, start $prg with the option -help\n";
my $opt_config = 'ecs.cfg';
my $opt_help = 0;
GetOptions('config=s' => \$opt_config, 'help' => \$opt_help)
or die "Error: unrecognized command line option.\n$usage";
pod2usage(-verbose=>2) if $opt_help;
die "Invalid number of command line arguments.\n$usage"
if $#ARGV != 1;
($filename,$sender) = @ARGV;
print "filename: $filename\nsender: $sender\n";
# initialize
$err = load_ecs_config($opt_config);
die "ecs_proc_meta: $err\nUnable to initialize ECS.\n" if $err;
# read node info from node_tbl
$ECS_NODE_TBL->lock() # lock node_tbl
or die "ecs_proc_meta: unable to lock node_tbl: " .
$ECS_NODE_TBL->ERROR . "\n";
$node = $ECS_NODE_TBL->read($sender);
$err = $ECS_NODE_TBL->ERROR;
$ECS_NODE_TBL->unlock(); # unlock node_tbl
if($err or not $node) {
my $err2 = move_to_dir($filename, $ECS_CFG->ECS_MBX_TRASH_DIR);
warn "ecs_proc_meta: $err\n" if $err2;
die "ecs_proc_meta: unable to read node $sender from node_tbl: $err\n"
if $err;
die "ecs_proc_meta: node not found: $sender\n";
}
# read message from file
if($node->{encr_meta} !~ /true/i) {
# non-encrypted message expected
$msg = EMDIS::ECS::Message::read_from_file($filename);
$message_content = $msg->body if ref $msg;
}
else {
# encrypted message expected
$msg = EMDIS::ECS::Message::read_from_encrypted_file($filename);
$message_content = $msg->cleartext if ref $msg;
}
die "ecs_proc_meta: $msg\n"
unless ref $msg;
die "ecs_proc_meta: file does not contain a meta-message: $filename\n"
unless $msg->is_meta_message;
# parse msg_type from message body
$msg_type = '';
if($message_content =~ /msg_type\s*=\s*(\S+)/i) {
$msg_type = $1;
}
# READY
if($msg_type =~ /^READY/i)
{
$ECS_NODE_TBL->lock() # lock node_tbl
or die "ecs_proc_meta: unable to lock node_tbl: " .
$ECS_NODE_TBL->ERROR . "\n";
$node = $ECS_NODE_TBL->read($sender);
if($node)
{
$node->{last_in} = time;
$ECS_NODE_TBL->write($sender, $node);
}
$ECS_NODE_TBL->unlock(); # unlock node_tbl
if(not $node)
{
$err = move_to_dir($filename, $ECS_CFG->ECS_MBX_TRASH_DIR);
warn "ecs_proc_meta: $err\n" if $err;
die "ecs_proc_meta: node not found: $sender\n";
}
unlink $filename; # remove input file after successful processing
exit 0; # successful
}
# RE_SEND
if($msg_type =~ /^RE_SEND/i)
{
# get seq_num (and part_num, if specified)
my $seq_num = '';
my $part_num = '';
if($message_content =~ /seq_num\s*=\s*(\d+):(\d+)/i)
{
$seq_num = $1;
$part_num = $2;
}
if($message_content =~ /seq_num\s*=\s*(\d+)/i)
{
$seq_num = $1;
}
else
{
die "ecs_proc_meta: seq_num not specified in RE_SEND message: " .
"$filename\n";
}
# look up email address of sender
$ECS_NODE_TBL->lock() # lock node_tbl
or die "ecs_proc_meta: unable to lock node_tbl: " .
$ECS_NODE_TBL->ERROR . "\n";
$node = $ECS_NODE_TBL->read($sender);
if($node)
{
$node->{last_in} = time;
$ECS_NODE_TBL->write($sender, $node);
}
$ECS_NODE_TBL->unlock(); # unlock node_tbl
if(not $node)
{
$err = move_to_dir($filename, $ECS_CFG->ECS_MBX_TRASH_DIR);
warn "ecs_proc_meta: $err\n" if $err;
die "ecs_proc_meta: node not found: $sender\n";
}
# check whether $seq_num > $node->{out_seq}
if($seq_num > $node->{out_seq})
{
die "ecs_proc_meta: node requested RE_SEND of unsent message " .
"($sender,$seq_num)\n";
}
# look in out_box for requested message
my $msg_filename = format_msg_filename($sender,$seq_num);
die "ecs_proc_meta: requested FML message file not found: $msg_filename\n"
unless -e $msg_filename;
# resend the message
my $msg = new EMDIS::ECS::FileBackedMessage($msg_filename);
die "ecs_proc_meta: unable to load FML message from file $msg_filename: " .
"$msg\n"
unless ref $msg;
die "ecs_proc_meta: found unexpected seq_num " . $msg->seq_num .
", expected $seq_num\n"
unless $msg->seq_num == $seq_num;
$err = $msg->send_this_message($sender, 1, $part_num);
if ($err){
send_admin_email("An error occurred while trying to resend ecs msg!" ,
" Email from $sender with sequence number $seq_num " ,
($part_num ? "(part_num $part_num) " : '') .
"could not be resent." );
die "ecs_proc_meta: error sending email: $err\n"
}
unlink $filename; # remove input file after successful processing
log_info("\nFinished processing $sender RE_SEND request $seq_num" .
($part_num ? ":$part_num" : '') . "\n");
exit 0; # successful
}
# DOC_RE_SEND
if($msg_type =~ /^DOC_RE_SEND/i)
{
# get seq_num
my $seq_num = '';
if($message_content =~ /seq_num\s*=\s*(\d+)/i)
{
$seq_num = $1;
}
else
{
die "ecs_proc_meta: seq_num not specified in DOC_RE_SEND message: " .
"$filename\n";
}
# look up email address of sender
$ECS_NODE_TBL->lock() # lock node_tbl
or die "ecs_proc_meta: unable to lock node_tbl: " .
$ECS_NODE_TBL->ERROR . "\n";
$node = $ECS_NODE_TBL->read($sender);
if($node)
{
$node->{last_in} = time;
$ECS_NODE_TBL->write($sender, $node);
}
$ECS_NODE_TBL->unlock(); # unlock node_tbl
if(not $node)
{
$err = move_to_dir($filename, $ECS_CFG->ECS_MBX_TRASH_DIR);
warn "ecs_proc_meta: $err\n" if $err;
die "ecs_proc_meta: node not found: $sender\n";
}
# check whether $seq_num > $node->{doc_out_seq}
if($seq_num > $node->{doc_out_seq})
{
die "ecs_proc_meta: node requested DOC_RE_SEND of unsent document " .
"($sender,$seq_num)\n";
}
# look in out_box for requested message
my $doc_filename = format_doc_filename($sender,$seq_num);
die "ecs_proc_meta: requested document file not found: $doc_filename\n"
unless -e $doc_filename;
# resend the message
my $msg = new EMDIS::ECS::FileBackedMessage($doc_filename);
die "ecs_proc_meta: unable to load document from file $doc_filename: " .
"$msg\n"
unless ref $msg;
die "ecs_proc_meta: found unexpected seq_num " . $msg->seq_num .
", expected $seq_num\n"
unless $msg->seq_num == $seq_num;
$err = $msg->send_this_message($sender, 1, '');
if ($err){
send_admin_email("An error occurred while trying to resend ecs document!" ,
" Email from $sender with sequence number $seq_num " ,
"could not be resent." );
die "ecs_proc_meta: error sending email: $err\n"
}
unlink $filename; # remove input file after successful processing
log_info("\nFinished processing $sender DOC_RE_SEND request $seq_num\n");
exit 0; # successful
}
# MSG_ACK
if($msg_type =~ /^MSG_ACK/i)
{
my $ack_seq = '';
if($message_content =~ /seq_num\s*=\s*(\d+)/i)
{
$ack_seq = $1;
}
else
{
die "ecs_proc_meta: seq_num not specified in MSG_ACK message: " .
"$filename\n";
}
$ECS_NODE_TBL->lock() # lock node_tbl
or die "ecs_proc_meta: unable to lock node_tbl: " .
$ECS_NODE_TBL->ERROR . "\n";
$node = $ECS_NODE_TBL->read($sender);
if($node)
{
if((not $node->{ack_seq}) or $node->{ack_seq} < $ack_seq and $ack_seq <= $node->{out_seq})
{
$node->{ack_seq} = $ack_seq;
}
$node->{last_in} = time;
$ECS_NODE_TBL->write($sender, $node);
}
$ECS_NODE_TBL->unlock(); # unlock node_tbl
if(not $node)
{
$err = move_to_dir($filename, $ECS_CFG->ECS_MBX_TRASH_DIR);
warn "ecs_proc_meta: $err\n" if $err;
die "ecs_proc_meta: node not found: $sender\n";
}
if($ack_seq > $node->{out_seq})
{
die "ecs_proc_meta: node acknowledged unsent message " .
"($sender,$ack_seq)\n";
}
unlink $filename; # remove input file after successful processing
exit 0; # successful
}
# DOC_MSG_ACK
if($msg_type =~ /^DOC_MSG_ACK/i)
{
my $ack_seq = '';
if($message_content =~ /seq_num\s*=\s*(\d+)/i)
{
$ack_seq = $1;
}
else
{
die "ecs_proc_meta: seq_num not specified in DOC_MSG_ACK message: " .
"$filename\n";
}
$ECS_NODE_TBL->lock() # lock node_tbl
or die "ecs_proc_meta: unable to lock node_tbl: " .
$ECS_NODE_TBL->ERROR . "\n";
$node = $ECS_NODE_TBL->read($sender);
if($node)
{
if((not $node->{doc_ack_seq}) or $node->{doc_ack_seq} < $ack_seq and $ack_seq <= $node->{doc_out_seq})
{
$node->{doc_ack_seq} = $ack_seq;
}
$node->{last_in} = time;
$ECS_NODE_TBL->write($sender, $node);
}
$ECS_NODE_TBL->unlock(); # unlock node_tbl
if(not $node)
{
$err = move_to_dir($filename, $ECS_CFG->ECS_MBX_TRASH_DIR);
warn "ecs_proc_meta: $err\n" if $err;
die "ecs_proc_meta: node not found: $sender\n";
}
if($ack_seq > $node->{doc_out_seq})
{
die "ecs_proc_meta: node acknowledged unsent document " .
"($sender,$ack_seq)\n";
}
unlink $filename; # remove input file after successful processing
exit 0; # successful
}
die "ecs_proc_meta: unrecognized msg_type: '$msg_type'\n";
__END__
# embedded POD documentation
# for more info: man perlpod
=head1 NAME
ecs_proc_meta - process ECS meta-message
=head1 SYNOPSIS
ecs_proc_meta tmp/20030320_183247_1_0005_FYUh.msg FR
=head1 DESCRIPTION
Process file containing ECS meta-message; delete input file after
successful processing.
=head1 RETURN VALUE
Returns a non-zero exit code if an error is encountered.
=head1 BUGS
Possibly.
=head1 SEE ALSO
EMDIS::ECS, ecs_scan_mail, ecs_proc_msg
=head1 AUTHOR
Joel Schneider <jschneid@nmdp.org>
=head1 COPYRIGHT AND LICENSE
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Copyright (C) 2002-2020 National Marrow Donor Program. All rights reserved.
See LICENSE file for license details.
=head1 HISTORY
ECS, the EMDIS Communication System, was originally designed and
implemented by the ZKRD (http://www.zkrd.de/). This Perl implementation
of ECS was developed by the National Marrow Donor Program
Added MS Windows support for Windows 2000 and Windows XP
Added "DIRECTORY" inBox Protocol. This can interface with any mail
system that can output the new messages to text files.
2007-08-01
ZKRD - emdisadm@zkrd.de
Added new error report management. All 'email to admin' statements are removed.
In relation to the error code ECS.pm will send an email to admin or not.