#!/usr/bin/perl -w # ================================================================== # Gossamer Mail - enhanced email management system # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # Revision : $Id: outgoing.pl,v 1.27 2001/11/01 20:23:08 alex Exp $ # # Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved. # Redistribution in part or in whole strictly prohibited. Please # see LICENSE file for full details. # ================================================================== # Pragmas use strict; use lib '../admin'; use vars qw/$CLEANUP $VALID_CHARACTERS/; # Internal Modules use GMail qw/:objects :folders ADMIN/; use GMail::Admin; use GMail::Messages; use GMail::Options::Filters; use GMail::Options::Spam; use GMail::Folders; use GMail::Auth; use GMail::User; # GT modules use GT::TempFile; use GT::Mail; use GT::Mail::Parse; # System modules use Fcntl qw/:DEFAULT :flock/; # Error handling local $SIG{__DIE__} = \&fatal; # Begin $PLG->dispatch('outgoing::main', \&main); sub main { # ------------------------------------------------------------------------ # Clear ENV for -T checks. # foreach my $key (keys %ENV) { next if ($key eq 'REQUEST_METHOD'); $ENV{$key} = ''; } # Load config file $PLG->dispatch('GMail::Config::load_config', sub { $CFG->load_config }); # -v argument for verbose debugging, or if called via web. if (($ARGV[0] and $ARGV[0] eq '-v') or $ENV{REQUEST_METHOD}) { $CFG->{debug} = 1; shift @ARGV; } # Called from the web if ($ENV{REQUEST_METHOD}) { $| = 1; print $IN->header, "
"; $CFG->{debug} = 1; open STDERR, ">&STDOUT" or die "Couldn't redirect err to out!"; select((select(STDERR), $|=1)[0]); } # Give our temp module a place to put tmp files $ENV{GT_TMPDIR} = "$CFG->{location}->{path}->{data}/tmp"; # For class debugging $GMail::DEBUG = $CFG->{debug} if $CFG->{debug}; # Load all template specific files. my $t = GMail->template_set || 'default'; $CFG->load_template_set($t); # Load admin user $PLG->dispatch('GMail::Config::load_user', sub { $USER->load_user(@_) }, ADMIN); $PLG->dispatch('GMail::Config::check_create', sub { $USER->check_create }); my $dir = "$CFG->{location}->{path}->{data}/users/ADMIN"; # Do any bounces that need to be done. my $bounce_dir = "$dir/bounce"; -d $bounce_dir or GMail->mkdir($bounce_dir); opendir DIR, $bounce_dir or die "Could not open $bounce_dir; Reason ($!)"; my @bounce_files = map "$bounce_dir/$_", grep { /\d+/ and -f "$bounce_dir/$_" } readdir DIR; my $bounced = @bounce_files; $PLG->dispatch('outgoing::send_bounces', \&send_bounces, @bounce_files) if @bounce_files; # Do any replies that need to be done. my $reply_dir = "$dir/reply"; -d $reply_dir or GMail->mkdir($reply_dir); opendir DIR, $reply_dir or die "Could not open $reply_dir; Reason ($!)"; my @reply_files = map "$reply_dir/$_", grep { /^\d+$/ and -f "$reply_dir/$_" } readdir DIR; my $replies = @reply_files; $PLG->dispatch('outgoing::send_replies', \&send_replies, @reply_files) if @reply_files; # Do any forwards that need to be done. my $forward_dir = "$dir/forward"; -d $forward_dir or GMail->mkdir($forward_dir); opendir DIR, $forward_dir or die "Could not open $forward_dir; Reason ($!)"; my @forward_files = map "$forward_dir/$_", grep { /\d+/ and -f "$forward_dir/$_" } readdir DIR; my $forwards = @forward_files; $PLG->dispatch('outgoing::send_forwards', \&send_forwards, @forward_files) if @forward_files; if ($CFG->{debug}) { GMail->debug("$bounced message(s) bounced."); GMail->debug("$replies message(s) replied."); GMail->debug("$forwards message(s) forwarded."); } } sub send_bounces { # ------------------------------------------------------------------------ # Takes a list of files and bounces them. # my @files = @_; FILE: for my $file (@files) { GMail->debug("Parsing bounce email ($file)") if $CFG->{debug}; my $m = new GT::Mail(debug => $CFG->{debug}); $file =~ /^(.+)$/ and $file = $1; # Taint checks my $head; if (!$CFG->{email}->{bounce}->{no_attach}) { $head = $m->parse($file); } else { $head = $m->parse_head($file); } unlink($file) or die "Could not unlink ($file); Reason: ($!)\n"; my %opts = (); my $reason = $head->delete('X-Reason'); for ($head->get) { my $val = $head->get($_); $val =~ s/\r?\n//g; $opts{lc($_)} = $val } $opts{header} = $head->header_as_string; $opts{reason} = $reason; my $vars = GMail->get_tags(\%opts); my $t = GMail->template_set || 'default'; my $mail = GMail->parse_message("$CFG->{location}->{path}->{data}/templates/$t/bounce.eml", @{$vars}); my $top = $mail->top_part; # Bounces should go to return path first. my $to = $head->get('Return-Path') || $head->get('from') || $head->get('reply-to') || next; if ($to =~ /<\s*>/) { GMail->debug ("No return path, not bouncing.") if ($CFG->{debug}); next FILE; } GMail->debug("Setting 'To' line to ($to)") if $CFG->{debug}; $mail->top_part->set( 'Return-Path' => '<>' ); $mail->top_part->set( to => $to ); unless ($CFG->{email}->{bounce}->{no_attach}) { $mail->top_part->set('Content-Type' => 'multipart/mixed'); $mail->attach({ 'Content-Type' => 'message/rfc822', msg => $m->as_string, encoding => '-guess', }); } # Detection of loops my $loop = $head->get('X-GLoop') || $head->get('X-Loop'); if ($loop) { GMail->debug('Loop detected. Not sending'); next FILE; } if (bad_to($head)) { GMail->debug("Bad address, possible mail loop detected. Skipping") if $CFG->{debug}; next FILE; } $top->set('X-GLoop' => 'Gossamer Mail'); $top->set('X-Loop' => 'Gossamer Mail'); if ($CFG->{email}->{out} eq 'smtp') { $top->set('Message-Id' => '{email}->{smtp} . '>'); } $mail->send(($CFG->{email}->{out} eq 'smtp' ? (smtp => $CFG->{email}->{smtp}) : (sendmail => $CFG->{email}->{sendmail}))); } } sub send_replies { # ------------------------------------------------------------------------ # Takes a list of files to reply and send them. # my @files = @_; FILE: for my $file (@files) { GMail->debug("Parsing reply email ($file)") if $CFG->{debug}; # Maybe add user defined tags here for forwards later my $mail = new GT::Mail(debug => $CFG->{debug}); $file =~ /^(.+)$/ and $file = $1; # Taint checks my $head = $mail->parse($file); unlink($file) or die "Could not unlink ($file); Reason: ($!)\n"; # Detection of loops my $loop = $head->get('X-GLoop') || $head->get('X-Loop'); if ($loop) { GMail->debug('Loop detected. Not sending'); next FILE; } if (bad_to($head)) { GMail->debug("Bad address, possible mail loop detected. Skipping") if $CFG->{debug}; next FILE; } # Add any admin header fields if (-e "$CFG->{location}->{path}->{data}/admin/defaults/header.txt" and -s _) { local $/; my $head = \do { local *FH; *FH }; open $head, "$CFG->{location}->{path}->{data}/admin/defaults/header.txt" or die "Could not open $CFG->{location}->{path}->{data}/admin/defaults/header.txt for reading; Reason: $!"; for (split /\r?\n/, <$head>) { my ($k, $v) = split /\s*:\s*/; next unless (defined $k and length $k); next unless (defined $v and length $v); $head->set($k => $v); } close $head; } # Add any messsage the admin want at the end $file = ""; my $part; my @parts = $head->parts; @parts = ($head) unless @parts; for (@parts) { my $t = $_->get('content-type') || $_->mime_type; next unless lc($t) =~ /((?:html)|(?:text))/; my $type = $1; $file = ($type eq 'text') ? 'footer.txt' : 'footer.htm'; $part = $_; last; } if ($part and -e "$CFG->{location}->{path}->{data}/admin/defaults/$file") { local $/; my $add = \do { local *FH; *FH }; open $add, "$CFG->{location}->{path}->{data}/admin/defaults/$file" or die "Could not open $CFG->{location}->{path}->{data}/admin/defaults/$file for reading; Reason: $!"; my $body = $part->body_as_string; if ($file eq 'footer.htm') { $body =~ s,